The object model
tenant boundary
Owns apps, members, connections, secrets, policy, and audit history. Every
organization-scoped read takes an
organizationId and filters in the query,
not afterwards. In API paths the literal self refers to the token’s own
organization; an explicit ID from another organization returns 404.unit of intent
Named by
metadata.name, a lowercase DNS-style slug of 3–63 characters. An
app has a lifecycle state — active, archived, or scheduled for deletion — that
is a separate state machine from any individual deployment.where an app runs
Every app has
production. It may also have named environments and expiring
preview environments. Durable resources bind per
environment, which is why a rollout, a rollback, and a sleep/wake cycle all
see the same data.normalized manifest
Parsing resolves every default exactly once. The normalized form is what gets
hashed, stored on the revision, and read by every downstream subsystem — so
two manifests that mean the same thing hash the same.
content-addressed source
Remote clients upload bounded bytes before planning or deploying. The server
never accepts a caller-selected filesystem path. Artifacts omit
.git,
node_modules, .tiny, common caches, and credential-looking files.one immutable rollout
A deployment walks an explicit state machine: provision resources, apply
migrations, boot a candidate microVM, health-check it, and only then switch
the route. Retryable failures retain their current state for idempotent
reconciliation.
Deploy is asynchronous
POST /v1/apps:deploy persists desired state plus a durable, leased job and
returns 202. A background reconciler does the work and resumes those jobs
after a crash.
Four identity layers
Each layer is a separate audience, and every token carries one. A token minted for one boundary is rejected at another.
The workload token is the load-bearing one. It is what lets a replaceable
microVM have durable state without holding a credential: the control plane
resolves the token to exactly one environment’s binding, and a superseded
deployment’s token stops working the moment the route moves.
Client-supplied
Tiny-* headers are stripped at the gateway. Application code
can never see a forged platform header.Two ways an app reaches the outside world
Capabilities (preferred)
The app calls a named operation. The broker validates the grant,
constraints, rate limit, and response filter, injects the credential into a
header the app cannot set or read, and returns only status and payload. The
app holds nothing.
Direct egress (opt-in)
context.fetch against a manifest allowlist. Off unless an operator sets
TINY_FIRECRACKER_ALLOW_EGRESS=true. An app with direct egress holds
whatever credential that endpoint requires.Approval gates on blast radius, not on deploys
Redeploying an app whose permissions did not change is fully automatic — that is what makes the platform usable by an agent. Approval is required only when a change widens blast radius in production: a new capability, a new raw secret, a new outbound host, widened visibility, or a destructive migration. A gated deployment sits inawaiting_approval — a pause, not a failure.
Approving resumes the same deployment. See Approvals and
ADR 0004.
Where things run
- Control-plane host
- Guest microVM
- Your machine
Linux with
/dev/kvm. Runs the API, the embedded gateway, the reconciler,
the capability broker, the data service, and the object store. Holds the
SQLite control-plane database, artifacts, app databases, and storage
namespaces under $TINY_DATA_DIR.