The Node API (REST + MCP)
The node is the single binary that turns your private mesh into a public surface. It joins the mesh, discovers the apps your supervisors are hosting, and proxies traffic straight to each app's address — so external clients and AI agents never touch mesh setup. It speaks two protocols at once: a REST API (OpenAPI 3 + Swagger UI) and a full MCP server over Streamable-HTTP.
The public edge lives at https://api.tabbify.io — a CloudFront HTTPS
endpoint that fronts the node. That's the only address you ever use; the raw
host and port are an implementation detail behind the edge.
REST surface
Every protected endpoint expects Authorization: Bearer <TABBIFY_TOKEN> — your
Tabbify API token — checked in constant time. GET /health, GET /openapi.json,
and GET /swagger-ui are open.
# Discover what's running across all supervisors
curl -H 'Authorization: Bearer <TABBIFY_TOKEN>' \
https://api.tabbify.io/v1/apps
# Start, stop, and reset app instances
curl -X POST -H 'Authorization: Bearer <TABBIFY_TOKEN>' \
https://api.tabbify.io/v1/apps/<uuid>/start
Core endpoints: /v1/supervisors (list), /v1/apps (list), /v1/topology
(discovery), /v1/apps/{uuid}/start|stop|reset, and /v1/apps/{uuid}/deploy
(roll an app to a new image). The async deploy pipeline is POST /v1/deploy
(returns 202 {deploy_id}) with GET /v1/deploy/{id}/status to poll, plus the
per-supervisor GET/PUT /v1/supervisors/{name}/version. The full schema lives
at /openapi.json and is browsable at /swagger-ui.
/app/{uuid} routing
This is the data path. The node derives an app's mesh address directly from its
UUID — blake3(uuid)[0:6] mapped into fd5a:1f02:...::1 — and dials it on port
8730. No supervisor query, no cache lookup; the node and the supervisor compute
the same address independently. See Routing for the address
scheme.
curl https://api.tabbify.io/app/0191e7c2-1111-7222-8333-444455556666/ \
-H 'Authorization: Bearer <TABBIFY_TOKEN>'
GET /app/{uuid} proxies /; ANY /app/{uuid}/{*rest} forwards method, headers,
query, and body to the subpath. Bodies stream both directions with no
buffering — large uploads and downloads pass straight through, but a slow upstream
holds the connection (no per-hop timeout). An unreachable app returns 502; a
malformed UUID returns 400.
MCP tools
The same business logic backs an MCP endpoint at POST/GET /mcp (official rmcp
SDK, Streamable-HTTP transport, Mcp-Session-Id header for sessions). Tools mirror
the REST verbs — list_supervisors, list_apps, get_app, start_app,
stop_app, reset_app, and deploy_pipeline — and carry the same Bearer auth.
So an agent discovers, deploys, starts, and routes to apps through one session.
How apps get there
You don't talk to the node to deploy — you git push. GitHub Actions runs the
tabbify-io/deploy action, which calls POST /v1/deploy; the node clones your
repo, builds it on a builder supervisor (a Firecracker build sandbox → OCI
image), pushes the image to the mesh registry, and runs it as a Firecracker
microVM runner that joins the mesh as its own peer. You can drive the same
pipeline straight from a terminal with tcli deploy --remote. See the
deploy pipeline and the CLI reference.
Config and caveats
The node joins the mesh automatically — the coordinator address is baked into the joiner, so there is nothing to point it at. Requests authenticate with a Bearer token (your network token), or — for MCP clients — via the node's OAuth flow.
Connect an MCP client
The same node serves the Model Context Protocol alongside the REST API, at
https://api.tabbify.io/mcp. Interactive clients authorize via the node's full
OAuth 2.1 surface (/.well-known/oauth-protected-resource,
/.well-known/oauth-authorization-server, dynamic client /register,
/authorize, /token) — nothing to paste; a static Bearer token works for
headless/CI use. For step-by-step setup of Claude, OpenAI, and other clients,
see Connect an MCP client.
Honest about the current state: activation-policy and desired-version stores are
in-memory only, lost on restart unless seeded via env. The mesh join needs
NET_ADMIN + /dev/net/tun at runtime, so a self-hosted node needs those
capabilities (Docker: cap_add: [NET_ADMIN] with the TUN device). For standing
up your own edge, see Self-hosting a node.