This repository has no description
flarebot docs installation-ownership.md
10 kB

Installation ownership metadata #

The Flarebot control plane stores only installation management metadata in its native SQLite INSTALLATIONS Durable Object binding. InstallationRegistry uses one object per verified Cloudflare UserInfo subject. That subject is the stable customer identity; an owner may have multiple installations across multiple Cloudflare accounts. No customer Agent state or conversation transport is connected to this registry.

The publisher Worker exports this DO separately from AuthVault. Provision it with wrangler.control-plane.jsonc and its native SQLite declarative export. Neither the registry nor its binding belongs in the customer release or browser bundle. OAuth credentials remain in the separate encrypted, expiring vault; installation metadata contains no grant reference or token. Runtime session secrets and model keys remain entirely outside this metadata boundary.

Browser API #

All responses are private no-store JSON with no-referrer. Mutations require the exact configured URL origin and Origin header. Authentication always resolves the real opaque identity session; submitted subjects or account IDs cannot authorize a record.

Method Path Input and result
POST /api/installations Form body containing only requestId, a fresh 32-character lowercase hexadecimal random ID. Revalidate the session's selected account against its current grant, then return { installation }.
GET /api/installations Optional exclusive cursor installation ID; return at most 50 owned records and nextCursor (null at the end).
GET /api/installations/:id Return the owned { installation }; another owner's ID and an unknown valid ID produce the same 404.

Reservation creates metadata only. The record has status: reserved, desiredRelease: null, installedRelease: null and no runtime origin/resource IDs. It does not upload code, create Containers, run health checks or provide an installation-ready link. There is no browser updater or caller-selected identity endpoint. The current public UI does not yet invoke reservation.

Creation requires current deployment authorization and an account selection. Reading existing ownership requires the eight-hour identity session only: expired one-hour deployment grants, reconnect requirements or unavailable OAuth capability setup do not erase records or prevent authenticated reads. When the identity session expires, reconnect to read them again. A fresh verified session for the same subject resolves the same registry.

The client retains its request ID until reservation has a definite result. A native storage transaction commits the new record and request-to-installation mapping together. Duplicate concurrent requests and retries after a lost reply return the same installation, including its current metadata. Reusing that request ID after selecting another account returns 409; it never retargets the record. A new request ID intentionally reserves another installation. Replay mappings live for the lifetime of their installations, with no expiry that could turn a delayed retry into a second resource. Listing is ordered by installation ID, not creation time; the cursor is an exclusive position, not a snapshot of concurrent future reservations.

Stored contract and server integration #

control-plane/installation-metadata.ts defines the strict recursive allowlist. Every RPC input, persisted record, record read and HTTP export is validated even when the value already has a TypeScript type. Unknown keys fail, including keys nested in release/resource objects. Errors expose declared categories only; never pass raw provider errors, credentials or customer content as strings.

The record contains immutable schema/installation/owner/account identity; server-assigned creation/update timestamps and revision; resource identifiers; desired and last verified installed release; operation ID, status and a declared error code. Release identity is { version, sourceRevision, artifactDigest }, where the digest is the SHA-256 of the actual immutable release manifest. Installed release additionally records a server-assigned installedAt.

Resource names are fixed from the server-generated installation ID: flarebot-<id> for the Worker and flarebot-shell-<id> for the Containers application. PersonalAgent namespace, Sandbox namespace, Containers application ID and runtime origin are initially null. A trusted provisioner may assign them once; subsequent updates cannot change or clear them. The runtime origin must exactly match https://flarebot-<id>.<account-subdomain>.workers.dev. The provisioner must obtain that subdomain and the resource IDs from verified Cloudflare responses and check the application's Sandbox namespace association. The schema validates identifiers and relationships it can know locally; it does not claim to prove Cloudflare ownership or health by parsing strings.

An optional custom domain is managed in a separate revisioned record. It never replaces this immutable management/recovery origin. Only active verified domain records may supply an additional browser login audience; arbitrary caller-supplied origins remain forbidden.

The private binding exposes four methods with a sanitized result envelope:

reserve(ownerSubject, accountId, requestId);
get(ownerSubject, installationId);
list(ownerSubject, (cursor = null));
update(ownerSubject, installationId, expectedRevision, changes);
// => { ok: true, value } | { ok: false, error }

The DO verifies its own native ID matches the requested owner, and checks each record's immutable owner/installation ID. Its helper methods are ECMAScript private methods, not additional callable RPCs. There is no DO fetch router, public Agent RPC or browser binding. Only trusted control-plane server code has the binding; that code must derive subjects from real authentication.

ownedInstallation(request, env, id) in control-plane/installations.ts is the ownership lookup for the future customer login bridge. It authenticates, selects the verified owner's registry, requires ownership and returns the strict record. The bridge can use its stored owner/origin for assertion claims; it needs no public reverse owner lookup. FLA-9 must still implement the challenge-bound, pinned-key, short-lived, one-time assertion and actual customer session issuance described in OAuth onboarding.

The internal update method accepts only explicit changes to desired release, operation ID, status, error code and nullable resource assignments. It cannot assign owner/account/names, timestamps, revision or installed release. The native transaction checks the expected revision, prevents active operation/release replacement, preserves assigned resource identities and increments revision. Concurrent writers cannot overwrite each other. A failed CAS returns installation_conflict; read and reconcile the original operation rather than retrying stale work with an arbitrary newer revision.

FLA-9 must validate the actual immutable artifact and fresh grant/account before CAS-starting an operation with a non-null desired release and operation ID. Changing the selected account cannot retarget an existing installation. All external effects follow this commit. Durable operation-start replay and actual resource reconciliation belong to the provisioner; FLA-11 adds no workflow, queue, external deployment, artifact publishing pipeline or operation history.

Only active installing/updating operations can finish ready or failed. Ready requires all resource identifiers and origin, and atomically copies the pinned desired release into installed release with the current server timestamp. The trusted caller must first prove Worker/assets/Containers boot and customer auth health. Merely satisfying the metadata schema is not a health check. A later update keeps the last verified installed release until the replacement passes health; failed updates retain that previous version and the attempted desired release with a sanitized recovery category. FLA-12 must preserve customer resources, variables and secrets when executing that update.

Validation and limits #

pnpm test:ownership exercises the actual publisher routing and native SQLite DOs with the existing fixed OAuth network fixture. It covers verified two-owner isolation, rejected forged fields/Origins, account changes, concurrent duplicate reservation, lost reply and persistent restart replay, plural cursor listing, private RPC helpers, strict nested storage/export validation, revision races, incomplete readiness, immutable resources, failed update/version preservation, and identity reads after grant expiry or publisher capability changes. Fixture inspection and mutation routes never enter the production artifact. The test also checks that metadata calls contact only the Accounts endpoint and that known credential/content/error sentinels do not appear in storage, responses or logs.

Run builds and packaged tests sequentially: pnpm build:release, then pnpm build:control-plane, then pnpm test:oauth and pnpm test:ownership. pnpm typecheck does not require generated release metadata. These checks prove local authorization, persistence and the storage boundary. They do not certify a live OAuth grant, resource provisioning, Containers boot, deployed ownership or installation/update health.