Documentation
ReferenceArchitecture

Core concepts

Tabbify is an office for your AI: you describe an outcome in chat, the AI writes the code, and the platform runs it as a real service. This page is the mental model that ties the pieces together — what an office holds, how an app becomes a running process, how those processes find each other, and where the human stays in the loop.

Offices and apps

An office is the bounded namespace you own: its apps, services, secrets, and approvals. The chat is your control plane; everything below is runtime.

An app is a workload identified by a UUID v7 — its permanent address, stable across every deploy. You don't publish artifacts by hand. The deploy flow is a git push: GitHub Actions runs the tabbify-io/deploy action, which calls tcli deploy --remote. Tabbify clones your repo, builds it on its own machines, and runs the result — no Docker on your laptop, no object-storage upload, no registry login. (You can also run tcli deploy --remote straight from a terminal if you prefer; see the CLI reference and Quickstart.)

A single tabbify.toml is the source of truth — [app] metadata, [build], [runtime] resource limits and lifecycle, and any [[deploy]] placement targets.

The runtime

There is exactly one runtime: a Firecracker microVM built from an OCI image. The build always produces an image (your Dockerfile → OCI image → ext4 rootfs), and that image boots in its own tiny VM. There is no runtime to pick — [build] is always kind = "docker" and the app always runs as Firecracker:

[build]
kind = "docker"          # build the Dockerfile into an OCI image

[runtime]
lifecycle        = "on_request"   # start on first visit, sleep when idle
memory_mb        = 256
vcpus            = 1

Older manifests sometimes carry a [runtime].type (such as wasm-http or docker) or a [[deploy]].runtime field. Those are tolerated for backward compatibility but ignored — they do not select an alternate runtime. Supervisors carry mesh capability tags (for example firecracker, which requires /dev/kvm, and builder), and the node validates that a target can run an app before placing it there. See The Runtime and Runtimes.

The private mesh

Apps do not listen on the open internet. They join a WireGuard overlay where each app gets a deterministic IPv6 address derived from its UUID:

fd5a:1f02:<blake3(uuid)>::1

The address is stable across restarts and host migrations, so one service always finds another at the same place — no discovery dance. A supervisor spawns a detached runner process per app, and each runner is itself a first-class mesh peer on that UUID-derived address — not just a socket the supervisor opened. The node proxies your requests to it across the mesh. Full detail in Private Mesh and Routing.

Events and approvals

State changes are recorded as an append-only event logapp_registered, app_started, secret_registered. The log is the source of truth: recovery is replay, and every API mutation flows through it for audit.

Two honest caveats today: the event stream is fire-and-forget (a subscriber that connects late misses an event; recent ones sit in a ring buffer), and the approval card — the chat widget that lists requested capabilities like telegram:send before anything runs — is designed but not yet wired in the frontend. Secrets never reach the prompt: they live in an encrypted store and are injected as environment, never written to the log. See Services & capabilities.