tinycloud.dev/v1alpha1. Tinycloud parses a strict
YAML subset, validates it against
tiny.v1alpha1.json,
normalizes defaults, and then applies semantic and organization-policy rules.
pnpm tiny init [name] to write this shape and pnpm tiny validate to
validate it locally.
Supported YAML
The parser accepts block and flow mappings/sequences, block strings, quoted and plain scalars, booleans, null, and numbers. It rejects anchors, aliases, custom tags, tabs used as indentation, and duplicate keys. Use one ordinary manifest document; YAML composition features are outside the supported subset.These restrictions keep a manifest deterministic and safe to hash — which is
what lets the normalized form identify a revision.
Top-level fields
metadata
string
required
A lowercase DNS-style slug, 3–63 characters. This is the app’s stable
identity.
email
required
Normalization lowercases it and always adds the owner as an app admin.
string
Defaults to the slug.
string
map
Arbitrary key/value labels, e.g.
department: platform.runtime
Entrypoints must be relative, may not contain
.., and must be included in the
uploaded artifact.
build
build.command runs inside a throwaway Firecracker microVM whose only network
is the package registry. The control plane, not the manifest, chooses the
install command, and it is always the package manager’s reproducible mode
(npm ci --ignore-scripts, pnpm install --frozen-lockfile, and so on) — a
manifest cannot ask for a looser dependency resolution than the lockfile
describes.
build.output selects the directory that becomes the deployed artifact, and
runtime.entrypoint is resolved inside it: with output: dist, an
entrypoint of index.js means dist/index.js. Omitting output deploys the
whole tree, including node_modules.
lockfileRequired: true refuses the build when the matching lockfile is absent.
Setting it to false is allowed, and tiny plan will warn that two deploys of
the same source may install different code.
Build output is re-packed through the same pipeline as an upload, so the
per-file and total size limits apply, and credential-shaped files such as a
stray .env are stripped rather than deployed. The result is content-addressed:
an unchanged source, command, and toolchain reuses the previous build instead of
running it again, and a rollback returns to identical bytes.
@tinycloud/runtime-sdk is supplied by the build image, so an application does
not declare it as a dependency.executesBuildCommands: false and tiny plan refuses a manifest with
build.command. It never falls back to building on the control-plane host.
Full guide: Builds.
access
access.visibility is private, organization, or public. requireLogin
defaults to true; non-public apps may not disable it. Subjects have exactly
one of user, group, or serviceAccount, plus an optional role (admin,
editor, user, or viewer; default user).
Only the last row serves anonymous requests, and only a public app may set
requireLogin: false. An anonymous request carries no identity token, so
context.user is null — the handler is never given a synthetic user it might
mistake for a real one. A visitor who does hold a session keeps their own
identity, role, and bindings on a public app; anonymity is the fallback, not a
downgrade applied to everyone.
Only the production environment opens. A preview keeps the login redirect even
when production serves the world, because visibility is read from the production
revision and a preview is unreviewed work at a guessable hostname.
Anonymous traffic is metered like any other request but is not written to the
audit log one row per request: there is no identity to record, and a public
app’s volume is not bounded by headcount. Denials, deploys, and every
control-plane action are audited as before.
Because that volume is unbounded, two organization-policy limits apply:
Full guide: Access and sharing.
resources
database
Migrations are ordered.sql files. Once a migration has been applied, changing
its contents is a deployment error; add a new migration instead. Migrations are
read from the uploaded source, not from the build output, so a build.output
that excludes them is fine.
engine: sqlite is always available: one file per environment, held by the
control plane and reachable only through the platform binding. It allows one
open write transaction per environment at a time, which is what SQLite is.
engine: postgres requires the operator to have configured a cluster. Each
environment gets its own role and database, and the app’s SQL is the dialect
Postgres speaks — use $1 placeholders rather than ?. Asking for an engine
the control plane does not have is a plan-time refusal, never a silent
substitution.
Neither engine gives the application a connection string. The database lives
outside the replaceable microVM, and the guest reaches it by sending SQL to the
control plane under its own workload token.
storage
resources.storage declares up to five object namespaces. Each has a name
the app passes to context.storage(name) — the name may be omitted when only one
is declared — and a maxSize quota.
Objects are not mounted into the microVM: the guest sends each operation to the
control plane over its private link under its workload token, exactly as it does
for SQL, and the token decides which namespaces it may address.
maxSize is measured against the namespace as it will be after the write, so
replacing an object does not charge for the copy it replaces. Filling it is a
RESOURCE_LIMIT_EXCEEDED refusal rather than a truncated write. Alongside it the
platform bounds an object at 32 MiB, a key at 1024 bytes, and a listing at 1000
keys per call. Keys must be relative and may not contain ...
retention describes how long deleted objects are kept for recovery. It is
recorded on the claim and is not yet enforced by a reaper.
Full guides: Databases ·
Object storage.
capabilities
payments.read becomes stripe.payments.read. A production capability
request requires approval under the default policy. Every operation id has
exactly three parts, because that is what the broker route addresses.
See the capability catalog for every provider,
operation, and enforced constraint, and Capabilities for
the guide.
network
allowlist rules take a host and ports (ports default to [443]):
secrets
TINY_* names. The default policy allows raw secrets but makes them an
approval signal. Prefer capabilities when an operation exists.
Full guide: Secrets.
lifecycle
Durations use integer units such as15s, 60m, 72h, or 30d, with tighter
units enforced for individual fields.
previewTtldefaults to72hand may not exceed the default organization maximum of 168 hours.- If both are set,
archiveAfterUnusedmust be longer thansleepAfter. deleteAfterArchivedrequiresarchiveAfterUnused.
observability
Defaults are log levelinfo, retention 7d, and trace sample rate 0.05.
The manifest can also carry alert intent for error rate, latency, or request
thresholds.
The current implementation persists structured worker logs and usage counters.
It does not run a separate alert delivery service, so alert intent is
recorded rather than acted on.
Target compatibility
Schema validity does not guarantee a target can deploy the manifest.plan
compares the normalized manifest with the provider’s live capabilities and
reports incompatibilities. The default organization policy caps memory at 2 GiB,
which is also the Firecracker provider limit.
Check compatibility with either: