Documentation
ReferenceArchitecture

Routing & public access

Every Tabbify app gets its own address on the private mesh, derived from its UUID. The public node is the one component with a public listener — it joins the mesh and proxies external requests to those addresses. This page covers both halves: the per-app address and the public edge in front of it.

Per-app addresses

An app's mesh address is a deterministic IPv6 ULA derived from its UUID via BLAKE3, in the fd5a:1f02::/32 block:

fn derive_app_ula(app_uuid: Uuid) -> Ipv6Addr {
  let b = blake3::hash(app_uuid.as_bytes());
  let b = b.as_bytes();
  Ipv6Addr::new(
    0xfd5a, 0x1f02,
    u16::from_be_bytes([b[0], b[1]]),
    u16::from_be_bytes([b[2], b[3]]),
    u16::from_be_bytes([b[4], b[5]]),
    0, 0, 1,
  ) // fd5a:1f02:<48-bit hash>::1
}

Because the hash depends only on the UUID, the supervisor and the node compute the same address independently — no lookup table. The address is host-independent and migration-safe: move an app to another supervisor and it keeps fd5a:1f02:…::1. Each app's runner binds [app_ula]:8730.

The public edge

tabbify-node listens on 0.0.0.0:8090 (not :8080auth owns that port on the same host). It joins the mesh tagged node, which installs /128 routes to supervisor-advertised app ULAs so it can dial apps directly.

# Proxy any method/path straight to the app's runner over the mesh
curl http://3.124.69.92:8090/app/<uuid>/some/path \
  -H 'Authorization: Bearer tabbify-dev-node-key'

/app/{uuid} and /app/{uuid}/{*rest} derive the ULA and stream the request to [app_ula]:8730 — bodies are not buffered, hop-by-hop headers are stripped both ways. An unreachable app returns 502; a malformed UUID returns 400. All /v1/* and /app/* routes require the bearer key (TABBIFY_NODE_KEY); /health, /openapi.json, and /swagger-ui do not.

Getting a public URL

In front of the node sits CloudFront, giving you HTTPS at api.tabbify.io. The flow:

  1. git push triggers the deploy pipeline; the supervisor spawns a runner and returns its app_ula.
  2. Request https://api.tabbify.io/app/<uuid>/ → node → mesh → runner.
browser → CloudFront (HTTPS) → node :8090 → WireGuard tunnel → [app_ula]:8730

Honest caveats for the current phase: the bearer key is a hardcoded RnD default; the node still queries supervisors per request to find the host (no UUID→supervisor cache yet); and proper per-app direct binding is staged — today the node dials via the supervisor's advertised routes. See routing internals in the node and self-hosting for the supervisor side.