Skip to content

Concepts

Record Owns
App Stable global identity registered in the catalog.
Release Immutable App artifact reference, digest and source/build provenance.
Public Surface An App-owned stable route with an explicit selected Release.
Transition receipt The recorded result of an operation-scoped selection or revocation.

IDs describe identity. They do not grant permission to change that identity. Each ID has a fixed grammar, such as app_<a-z0-9>, rel_<a-z0-9>, ps_<a-z0-9>, artifact:<name>, idem_<key>, git:<40 hex>, sha256:<64 hex> and a single-segment route like /hello. Parse untrusted strings with the root parse* functions. A rejected value throws AppReleaseInputError naming the parser.

The root entrypoint exports createAppReleaseCatalog, the parse* boundary parsers, the record types, the error classes and the App declaration vocabulary. The catalog is a Promise facade over an Effect core. The ./effect entrypoint exposes that core (createAppReleaseCore), the decode* functions and the in-memory store. Both use the same grammar and decisions.

A Release artifact is an App root with media type application/vnd.fungi.app+json and format: "fungi-app-v1". It binds a visual part (an HTML page or an asset manifest), an optional durable backend artifact, or both, under one digest-covered description. An App can be visual, headless or both, but never empty. ./app builds and parses App roots, ./backend parses backend descriptors, and ./assets parses asset manifests.

Declared capabilities, handlers, roles and commands describe what an App may ask its host to expose. They do not approve those operations.

The artifact reader resolves a stored opaque reference and verifies it. Verification checks the expected digest, content type, configured size bounds and an owned copy of the bytes. A reader never uses a caller-supplied digest to find different bytes. Asset manifests also bind object versions, digests, lengths and a mount base. Your host keeps admission and document isolation.

Create the App before its Releases and Surfaces. A Release or Surface for a missing App throws AppReleaseOwnerAppAbsentError or AppReleaseSurfaceOwnerAppAbsentError. Replaying the same immutable record returns it. Reusing an identity with changed material fails, and for a Release that is AppReleaseImmutableConflictError.

Creating a Release does not promote it. Verify its artifact, then request an explicit promotion or rollback to an eligible Release that belongs to the App. Unverified, revoked and cross-App selections fail. Retrying a transition with the same idempotency key and input returns its receipt. Changed input under that key throws AppReleaseTransitionIdempotencyConflictError and leaves the selection alone.

A revocation stops eligible delivery and selection. It does not edit immutable artifact bytes or decide how a Team restores an installation. Your host’s backend state lifecycle and schema migrations are separate from Release facts.

resolvePublicWebsite(route) takes a canonical stable route, reads the current selection, verifies the artifact and rechecks authority before returning bytes. A private cache never skips that recheck. Request bodies, cookies and caller-supplied Release IDs cannot select unpublished content.

./cloudflare provides createD1AppReleaseStore for catalog state, createR2ArtifactReader for artifact reads, and APP_RELEASE_D1_FRESH_SCHEMA for an empty database. ./cloudflare-assets reads and verifies asset builds from R2. ./publisher holds the schemas for registering an App’s source repository with a publishing Team. The adapters do not provision resources or supply Team authorization.