docs(tutorials): add Register a Remote Agent on Molecule AI tutorial
This commit is contained in:
parent
3c87417478
commit
98401b7eda
269
docs/tutorials/register-remote-agent.md
Normal file
269
docs/tutorials/register-remote-agent.md
Normal file
@ -0,0 +1,269 @@
|
||||
# Register a Remote Agent on Molecule AI
|
||||
|
||||
Remote agents let you connect AI agents running on *any* infrastructure — your laptop, a cloud VM, a CI/CD pipeline, or an on-premise server — to a single Molecule AI canvas. Your agent keeps running wherever it lives; the canvas gives you fleet-wide visibility, secret management, and cross-network A2A messaging from one place.
|
||||
|
||||
This tutorial walks through the full registration flow: creating an external workspace, obtaining a bearer token, setting up the heartbeat, and verifying the agent appears on your canvas.
|
||||
|
||||
> **Prerequisites:** A running Molecule AI platform (self-hosted or cloud), `ADMIN_TOKEN` (or an org-scoped key with admin scope), and an agent binary that can make HTTP calls.
|
||||
|
||||
## How remote agents work
|
||||
|
||||
Molecule AI's remote agent system has three parts:
|
||||
|
||||
1. **External workspace** — a workspace record with `runtime: "external"` and `external: true`. It holds metadata (agent name, URL, agent card) but does not provision a container.
|
||||
2. **Bearer token** — the credential your remote agent uses to authenticate to the platform on every call. Issued once at registration; stored by the agent.
|
||||
3. **Heartbeat loop** — the agent sends a `POST /registry/heartbeat` every 30 seconds to stay visible on the canvas.
|
||||
|
||||
```
|
||||
Your infra (laptop / VM / CI) Molecule AI Platform
|
||||
│ │
|
||||
│ POST /workspaces (create external workspace)
|
||||
│────────────────────────────────────►│
|
||||
│ │
|
||||
│ POST /registry/register (get bearer token)
|
||||
│────────────────────────────────────►│
|
||||
│ ← auth_token
|
||||
│ │
|
||||
│ POST /registry/heartbeat (every 30s)
|
||||
│────────────────────────────────────►│ Canvas shows purple REMOTE badge
|
||||
│ │
|
||||
│ GET /secrets (fetch workspace secrets)
|
||||
│ POST /a2a (A2A messaging)
|
||||
│────────────────────────────────────►│
|
||||
```
|
||||
|
||||
## Step-by-step registration
|
||||
|
||||
### Step 1: Create an external workspace
|
||||
|
||||
```bash
|
||||
ADMIN_TOKEN="your-admin-token-or-org-key"
|
||||
PLATFORM_URL="https://platform.moleculesai.app"
|
||||
AGENT_URL="https://your-agent.example.com" # must be reachable from the platform
|
||||
|
||||
WORKSPACE=$(curl -s -X POST "${PLATFORM_URL}/workspaces" \
|
||||
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "CI Agent",
|
||||
"runtime": "external",
|
||||
"external": true,
|
||||
"url": "https://your-agent.example.com"
|
||||
}')
|
||||
|
||||
WORKSPACE_ID=$(echo $WORKSPACE | jq -r '.id')
|
||||
echo "Workspace ID: ${WORKSPACE_ID}"
|
||||
```
|
||||
|
||||
The `runtime: "external"` flag tells the platform this workspace is agent-managed, not container-provisioned. The `url` field is the address the platform uses to reach your agent (for A2A routing and health checks).
|
||||
|
||||
Save the workspace ID — you'll use it in the next step.
|
||||
|
||||
### Step 2: Register the agent and receive a bearer token
|
||||
|
||||
```bash
|
||||
REG=$(curl -s -X POST "${PLATFORM_URL}/registry/register" \
|
||||
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"id\": \"${WORKSPACE_ID}\",
|
||||
\"url\": \"https://your-agent.example.com\",
|
||||
\"agent_card\": {
|
||||
\"name\": \"CI Agent\",
|
||||
\"runtime\": \"external\",
|
||||
\"version\": \"1.0\"
|
||||
}
|
||||
}")
|
||||
|
||||
AUTH_TOKEN=$(echo $REG | jq -r '.auth_token')
|
||||
echo "Auth token: ${AUTH_TOKEN}"
|
||||
|
||||
# IMPORTANT: the auth_token is shown once. Store it securely.
|
||||
# If lost, revoke and re-register.
|
||||
```
|
||||
|
||||
The response looks like:
|
||||
|
||||
```json
|
||||
{
|
||||
"auth_token": "rtok_01HZX... truncated ...",
|
||||
"workspace_id": "ws_01HZX...",
|
||||
"org_id": "org_01HZX...",
|
||||
"expires_at": null
|
||||
}
|
||||
```
|
||||
|
||||
Store `auth_token` in your agent's environment — **it's shown only once**. If you lose it, create a new external workspace and re-register.
|
||||
|
||||
### Step 3: Pull secrets on demand
|
||||
|
||||
Your agent fetches workspace secrets via the platform API using its bearer token. Secrets are never injected as environment variables for remote agents — the agent pulls them explicitly:
|
||||
|
||||
```bash
|
||||
curl -s "${PLATFORM_URL}/workspaces/${WORKSPACE_ID}/secrets" \
|
||||
-H "Authorization: Bearer ${AUTH_TOKEN}"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"secrets": {
|
||||
"OPENAI_API_KEY": "sk-...",
|
||||
"GITHUB_TOKEN": "ghs_..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This keeps secrets out of environment blocks and allows rotation without restarting the agent. Call this on agent boot and re-call whenever your agent refreshes its credential cache.
|
||||
|
||||
### Step 4: Start the heartbeat loop
|
||||
|
||||
The heartbeat keeps your agent visible on the canvas. Send it every **30 seconds**:
|
||||
|
||||
```python
|
||||
import requests, time
|
||||
|
||||
AUTH_TOKEN = "rtok_01HZX..."
|
||||
WORKSPACE_ID = "ws_01HZX..."
|
||||
PLATFORM_URL = "https://platform.moleculesai.app"
|
||||
|
||||
while True:
|
||||
resp = requests.post(
|
||||
f"{PLATFORM_URL}/registry/heartbeat",
|
||||
headers={"Authorization": f"Bearer {AUTH_TOKEN}"},
|
||||
json={"workspace_id": WORKSPACE_ID},
|
||||
)
|
||||
if resp.status_code != 200:
|
||||
print(f"Heartbeat failed: {resp.status_code} {resp.text}")
|
||||
time.sleep(30)
|
||||
```
|
||||
|
||||
If the platform misses three consecutive heartbeats (90 seconds), it marks the agent as `offline` on the canvas. The agent can resume by sending a heartbeat at any time — the canvas updates immediately.
|
||||
|
||||
### Step 5: Send and receive A2A messages
|
||||
|
||||
Remote agents use the standard A2A protocol. Your agent polls for inbound tasks:
|
||||
|
||||
```bash
|
||||
curl -s -X POST "${PLATFORM_URL}/a2a" \
|
||||
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Workspace-ID: ${WORKSPACE_ID}" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "message/send",
|
||||
"params": {
|
||||
"message": {
|
||||
"role": "user",
|
||||
"parts": [{"kind": "text", "text": "Hello from a remote agent"}]
|
||||
}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
The `X-Workspace-ID` header identifies which workspace the message originates from. Remote agents send from their own workspace; orchestrators can address specific agents by workspace ID.
|
||||
|
||||
### Step 6: Verify the agent appears on the canvas
|
||||
|
||||
Open your Molecule AI canvas, navigate to **Workspaces**, and look for your agent. Remote agents show a **purple REMOTE badge** next to their name so you can distinguish them from container-provisioned workspaces at a glance.
|
||||
|
||||
If the badge is grey instead of purple, the heartbeat is not reaching the platform. Check:
|
||||
- The agent's outbound HTTPS can reach `platform.moleculesai.app`
|
||||
- The heartbeat loop is running and not crashing silently
|
||||
- The `auth_token` matches the workspace ID
|
||||
|
||||
## Agent code: minimal Python example
|
||||
|
||||
Here's a minimal agent that registers, starts the heartbeat, and can receive A2A tasks:
|
||||
|
||||
```python
|
||||
import requests, time, threading, json
|
||||
|
||||
PLATFORM_URL = "https://platform.moleculesai.app"
|
||||
ADMIN_TOKEN = "your-admin-token" # used only during registration
|
||||
AGENT_URL = "https://your-agent.example.com" # must be HTTPS and reachable
|
||||
|
||||
# Step 1: Create external workspace
|
||||
workspace = requests.post(
|
||||
f"{PLATFORM_URL}/workspaces",
|
||||
headers={"Authorization": f"Bearer {ADMIN_TOKEN}"},
|
||||
json={"name": "CI Agent", "runtime": "external", "external": True, "url": AGENT_URL},
|
||||
).json()
|
||||
WORKSPACE_ID = workspace["id"]
|
||||
|
||||
# Step 2: Register and get bearer token
|
||||
reg = requests.post(
|
||||
f"{PLATFORM_URL}/registry/register",
|
||||
headers={"Authorization": f"Bearer {ADMIN_TOKEN}"},
|
||||
json={
|
||||
"id": WORKSPACE_ID,
|
||||
"url": AGENT_URL,
|
||||
"agent_card": {"name": "CI Agent", "runtime": "external"},
|
||||
},
|
||||
).json()
|
||||
AUTH_TOKEN = reg["auth_token"]
|
||||
|
||||
# Step 3: Fetch secrets on boot
|
||||
secrets = requests.get(
|
||||
f"{PLATFORM_URL}/workspaces/{WORKSPACE_ID}/secrets",
|
||||
headers={"Authorization": f"Bearer {AUTH_TOKEN}"},
|
||||
).json()
|
||||
# Store secrets in your agent's credential store
|
||||
|
||||
# Step 4: Heartbeat loop (runs in background)
|
||||
def heartbeat_loop():
|
||||
while True:
|
||||
requests.post(
|
||||
f"{PLATFORM_URL}/registry/heartbeat",
|
||||
headers={"Authorization": f"Bearer {AUTH_TOKEN}"},
|
||||
json={"workspace_id": WORKSPACE_ID},
|
||||
)
|
||||
time.sleep(30)
|
||||
|
||||
threading.Thread(target=heartbeat_loop, daemon=True).start()
|
||||
|
||||
# Step 5: Poll for A2A tasks
|
||||
print(f"Registered. Workspace ID: {WORKSPACE_ID}")
|
||||
print("Heartbeat running in background.")
|
||||
```
|
||||
|
||||
## Self-hosted agents
|
||||
|
||||
For agents on private networks or air-gapped infrastructure, the platform must be able to reach `AGENT_URL` for A2A delivery. If your agent is behind a NAT or firewall:
|
||||
|
||||
- Use a tunnel (Cloudflare Tunnel, ngrok, frp) to expose the agent on a public HTTPS URL
|
||||
- Ensure the URL resolves and the agent's HTTP server handles `POST /a2a` requests
|
||||
- Check that your firewall allows outbound HTTPS to `PLATFORM_URL`
|
||||
|
||||
For air-gapped deployments without internet access, contact your Molecule AI sales team for on-premise deployment options.
|
||||
|
||||
## Revoking and re-registering
|
||||
|
||||
To rotate the agent's bearer token:
|
||||
|
||||
1. **Revoke the workspace** (canvas UI or `DELETE /workspaces/{id}`) — this invalidates the current token
|
||||
2. Re-run Step 1 and Step 2 above with a new workspace name
|
||||
3. Update your agent's `AUTH_TOKEN` with the new value
|
||||
|
||||
To revoke without deleting the workspace record, use `DELETE /workspaces/{id}/tokens` if your platform version supports it.
|
||||
|
||||
## Remote agents vs. Docker workspaces
|
||||
|
||||
| | Remote Agent | Docker Workspace |
|
||||
|---|---|---|
|
||||
| Infrastructure | Your own (laptop, VM, bare metal) | Platform-provisioned containers |
|
||||
| Token issuance | Manual via `/registry/register` | Automatic on container boot |
|
||||
| Secrets | Pulled on demand via API | Injected as env vars at startup |
|
||||
| Heartbeat | Your code sends it every 30s | Platform sends it from the container |
|
||||
| Canvas badge | Purple REMOTE | Standard (no badge) |
|
||||
| Tear-down | Revoke token + stop agent | `DELETE /workspaces/{id}` |
|
||||
| Best for | CI/CD agents, laptops, on-prem | Cloud VMs managed by the platform |
|
||||
|
||||
## What's next
|
||||
|
||||
- [Agent Card reference](../agent-runtime/agent-card.md) — publish your agent's capabilities so orchestrators can discover and route tasks
|
||||
- [A2A protocol reference](../api-protocol/a2a-protocol.md) — full message format, error codes, and streaming
|
||||
- [Registry and heartbeat reference](../api-protocol/registry-and-heartbeat.md) — heartbeat interval, offline detection, and error handling
|
||||
- [Remote workspaces blog post](../blog/2026-04-20-remote-workspaces/index.md) — the product announcement with fleet visibility context
|
||||
|
||||
> **Molecule AI is open source.** Remote agent support is in `molecule-core/registry/` on `main`.
|
||||
Loading…
Reference in New Issue
Block a user