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 edge compute the
same address independently, with no per-request supervisor query. The address
stays associated with the application across deployments. Each app's
runner binds [app_ula]:8730, and production traffic reaches
that address through the managed mesh relay.
Per-instance addresses for managed data
Managed PostgreSQL, KV and object-storage instances are addressed the same way,
in their own fd5a:1f03::/32 block: the address is BLAKE3 over the instance
id instead of an app UUID, so it too is recomputed rather than stored, and it
is the address your connection string names:
postgresql://app_rw@[fd5a:1f03:a1b2:c3d4:e5f6::1]/mydb
No port is printed — every managed protocol is reached on its own default port
(PostgreSQL 5432, Redis 6379, S3 443) — and no DNS name is created for an
instance: the address is derived, and libpq and every mainstream client library
accept a bracketed IPv6 literal as-is.
An instance is not a mesh peer itself; its door (the managed proxy for that protocol) holds the address and answers on it, and only peers holding a grant on that instance are told the address exists. That is why a leaked connection string is not access on its own: reaching the address is a question of the grant, and the credentials in the string are the second factor.
The public edge
tabbify-node serves the standard public application routes. It joins the mesh
and installs routes to advertised app ULAs; the managed relay carries the
production hop to the selected workload host. The public edge terminates HTTPS,
so applications do not expose their guest ports to the internet.
The node exposes two kinds of routes:
- App-serving —
/app/{uuid},/app/{uuid}/{*rest}, and the bare/{uuid}and/{uuid}/{*rest}aliases. These are public: they carry no bearer gate, because this is the edge that fronts your deployed app to the open internet. Bothhttps://api.tabbify.io/app/<uuid>/pathand the shorterhttps://api.tabbify.io/<uuid>/pathreach the same app. - Control —
/v1/*. These requireAuthorization: Bearer <TABBIFY_TOKEN>, the API token issued to your Tabbify account./healthand the docs (/openapi.json,/swagger-ui) are public too.
# Reach a deployed app — public, no auth needed
curl https://app.tabbify.io/app/<uuid>/some/path
# Same app via the node's API host (the bare /<uuid> alias also works)
curl https://api.tabbify.io/<uuid>/some/path
# A control call — requires your Tabbify API token
curl https://api.tabbify.io/v1/apps \
-H "Authorization: Bearer $TABBIFY_TOKEN"
App-serving routes derive the ULA and stream the request to [app_ula]:8730
through the relay. Bodies are not buffered, and hop-by-hop headers are stripped
both ways. An unreachable app returns 502; a malformed UUID returns 400.
Getting a public URL
You don't wire any of this up by hand. When you deploy from a git push, the pipeline clones and builds your repo, runs it as a Firecracker microVM, and the runner binds its derived app_ula. The node already knows how to reach that address, so the app is live the moment the runner is healthy:
git pushtriggers the deploy pipeline; the supervisor spawns a runner that binds[app_ula]:8730.- A request to
https://app.tabbify.io/app/<uuid>/is routed from the TLS edge through the mesh relay to the runner.
browser -> TLS edge -> node -> WireGuard relay -> [app_ula]:8730
Custom domains and TLS
A tenant can attach a custom hostname to an application through the current domain workflow. Follow the DNS target and verification instructions shown by the console or API. After validation, the public edge serves the hostname with TLS and forwards it to the same application route.
Custom ingress does not add application authentication. If a route contains private data or mutations, enforce authentication and authorization in the application.
The deterministic address solves application routing; it does not imply a peer-to-peer production transport. Production currently remains relay-only. See Private Mesh and Self-hosting.