tiny.app(handler) from
@tinycloud/runtime-sdk. The handler receives standard Web Request and
Response objects plus a verified Tinycloud context.
500 APP_ERROR response and
written to the deployment log.
The SDK is supplied by the runtime and the build image. An application does not
declare it as a dependency.
Context
Identity is derived from a short-lived platform token verified by the runtime
host.
Database
context.db is not a connection. A microVM is replaceable, so the database
lives outside it, and the app is given no host, path, or credential to leak.
Statements travel over the VM’s private link to the control plane, carrying the
deployment’s own workload token; the control plane resolves that token to
exactly one environment’s database.
query, get, execute, and transaction. A failed
transaction rolls back automatically.
Parameters are always bound, never interpolated, whichever engine is behind the
binding. Write the placeholder syntax your engine uses: ? for SQLite, $1 for
Postgres.
Value types over the wire
Rows reach your app as JSON over the private link, so values arrive as the JSON type that can hold them without losing anything. That sometimes means a string where you might expect something richer:
Binary goes the other way as itself: pass a
Buffer or Uint8Array as a
parameter and it is bound as bytes, not as text.
Transactions are leased sessions
transaction opens a server-side session with a short lease rather than a lock
the app holds. Two consequences are worth knowing:
- Nesting is refused, not flattened. A flattened rollback would quietly commit work the caller believed was undone.
- The lease expires. A transaction left open past the timeout is rolled back and its session reclaimed, so a crashed guest cannot hold a write lock or a pooled connection indefinitely. Keep transactions short, and never wait on a slow remote call inside one you need to commit.
Bounds
The platform bounds what one request may ask for: statement size, parameter count, result rows, and result bytes. A query that would return more than the row limit is refused rather than streamed — add aLIMIT and paginate.
On SQLite, one write transaction is open per environment at a time; a second
begin is refused with that explanation rather than blocking. Postgres serves
several concurrently from its pool.
Object storage
Objects work the way SQL does: the guest holds no bucket, path, or credential, and every operation crosses to the control plane’s storage service over the private link under the deployment’s workload token. The token decides which namespaces the app may address, so a namespace another app declared is unreachable even by name.resources.storage[].name, and may be omitted when the
manifest declares exactly one namespace. Asking for a namespace the manifest
never declared throws before any request is made.
putaccepts a string orUint8Arrayand returns the stored object’s metadata.getreturnsUint8Array | null—nullmeans that key is absent, while a missing namespace or a superseded deployment raises, so an unreachable namespace never reads as an empty one.deletereturns whether the key existed.listreturns objects sorted by key withkey,size,lastModified, andetag, filtered by an optional prefix.
..; anything that would escape the
namespace is refused.
Bytes cross the link raw rather than base64-encoded in JSON, so binary is stored
exactly as given — the round trip is byte-for-byte, not UTF-8-normalized.
Bounds: an object is at most 32 MiB, a key at most 1024 bytes, and a listing
returns at most 1000 keys per call. A namespace also has the maxSize its
manifest declared, measured after the write, so replacing an object does not
charge for the copy it replaces. Bytes moved are metered as storage_bytes.
Capabilities
Vendor credentials never enter the worker.
Outbound fetch
context.fetch is for declared direct egress. It checks host and effective port
against network.egress.
On Firecracker, allowlist mode is deployable only when the operator has set
TINY_FIRECRACKER_ALLOW_EGRESS=true; the host then pins each declared
hostname’s resolved addresses into that VM’s firewall. Otherwise planning
rejects the manifest.
Brokered capabilities remain the preferred path because application code never
receives the vendor credential.
Logging and audit
log writes a structured deployment log event at the manifest’s level.
audit writes a structured app.audit event into the same log stream — use it
for actions a person may later need to account for.
Secrets
Runtime limits
The runtime host enforces the manifest request timeout and concurrency.A timed-out process is retired after its response, because JavaScript
promises cannot be forcibly cancelled. Do not rely on a timeout to stop work
already in flight.
The gateway applies the same default size limits.