Installation manifests
Source preview: The manifest engine and clean-account installation path are part of the private OSS release candidate. The referenced package and example files are not publicly downloadable yet, and this page is not a supported self-hosted release. See Release status.
@tedix/installation-manifest defines the versioned handoff from installation
planning to provisioning. It describes organization profile, Cloudflare account
and domain topology, Workers, routes, bindings, resources, provider
prerequisites, capabilities, lifecycle requirements, and sanitized bootstrap
inputs.
The manifest contains secret names only. Credentials remain in the operator’s secret provider and are injected after preflight. Runtime entitlements are independent from optional billing settlement metadata.
billingSettlement is explicit deployment policy. An omitted block generates
TEDIX_BILLING_SETTLEMENT_MODE=disabled; configured external or managed
mode is emitted unchanged. Disabled and external overlays do not require Stripe
secrets. Managed Stripe settlement fails overlay generation unless the live and
test API-key and webhook-secret names are declared. The overlay also projects
the manifest’s entitlement grants through the non-secret
TEDIX_RUNTIME_ENTITLEMENT_GRANTS JSON variable.
fleetAuthority.mode is also required and is emitted as
TEDIX_FLEET_AUTHORITY_MODE. The sanitized developer/OSS manifest uses
disabled, emits no FLEET_DB, and requires no Stripe or fleet secret names.
co-located requires the worker’s DB binding. Reserved service mode requires
an explicit D1 binding named FLEET_DB; API runtime calls still fail closed
until all classified fleet owners have moved, so declaring the binding is not a
claim that the separate service is operational.
Installation authority is also distinct from tenant and fleet authority. The
machine-checked map in scripts/oss/authority-classification.json keeps runtime
entitlements in tenant-product, puts bootstrap, upgrade, backup, restore, and
installation-owner health in installation-operations, and reserves Stripe,
provider settlement, reconciliation, global catalog promotion, and
cross-installation administration for fleet-commercial. An installation
manifest may supply coordinates for enabled planes; it does not merge their
authority or make commercial settlement a prerequisite for tenant entitlement
enforcement.
Certification
Import parseInstallationManifest or certifyInstallationManifest from
@tedix/installation-manifest/schema. A document marked certified is rejected
when it has unresolved coordinates, unavailable required capabilities, a
profile/access-plan mismatch, missing required entitlements, or an unready
required provider. Issues are sorted deterministically for CI and automation.
The execution policy is fixed to fail-before-mutation: provisioning tools must
validate and certify the complete manifest before creating or changing any
resource. The package also exposes deterministic JSON Schema generation for
systems that do not execute TypeScript.
Use @tedix/installation-manifest/developer-example as a sanitized reference.
It uses example-only coordinates and contains no production installation IDs or
secret values. It is intentionally uncertified at the schema-valid level;
schema evidence is not clean-account or installation-ready certification.
Account capability preflight
Import preflightCloudflareAccount from
@tedix/installation-manifest/preflight to verify a live Cloudflare account
before provisioning. The preflight is strictly read-only: it issues only GET
requests, and callers must refuse to provision when the returned
mutationAllowed is false. It checks that every declared account is reachable
with the supplied token, that each account exposes every product capability the
declared resources need (D1, R2, KV, Durable Objects, Workflows, Queues,
Workers, Browser Rendering, Workers AI, Vectorize, Hyperdrive, Containers,
assets), and that every declared zone exists, contains its declared hostname,
and belongs to its declared account.
Every manifest resource kind maps to exactly one probe; adding a resource kind to the schema without deciding its probe fails compilation, so a required dependency can never be silently omitted. Unresolved account or zone coordinates fail the preflight without any network call. Optional-resource capabilities that are missing are recorded but do not block.
For a disposable-account certification, set freshAccount: true on the target
account. The same read-only preflight then verifies that Worker scripts and
every declared resource inventory with a Cloudflare list endpoint are empty. A
non-empty inventory fails before provisioning, so an old alpha environment
cannot be misrepresented as a fresh-account result.
Run bunx wrangler login once using your own Cloudflare account. Then
bun run preflight --manifest <path> (in packages/installation-manifest,
--json for machine output) runs the same read-only check and exits non-zero
when a required capability is missing. The CLI reuses Wrangler authentication
through its supported auth token --json command; Tedix does not read
Wrangler’s private credential files or store a second copy. An explicit
CLOUDFLARE_API_TOKEN takes precedence for automation. Global API key/email
authentication is not supported by these bearer-token installation tools.
The manifest, not the login, selects the target account. --help needs no
credentials and does not make network calls.
Identity-provider readiness
An interactive Tedix OS also needs an operator-owned Descope project. For alpha and beta rehearsal, use a dedicated non-production project instead of reusing a shared production identity project. This keeps test users, approved origins, flows, keys, and later cleanup inside the installation being tested.
For provisioning, declare OS_URL and DESCOPE_PROJECT_ID in the manifest’s
OS worker vars. Optional DESCOPE_BASE_URL defaults to
https://api.descope.com. Both URL values must be canonical HTTPS origins:
no ports, credentials, paths, queries, fragments, or trailing slash. The
provisioning path supports one OS worker (multiple surfaces may reference it)
and refuses missing, malformed, or ambiguous targets before provider or
Cloudflare requests.
Create a temporary, project-scoped management key with read access to project
configuration. provision --apply needs only DESCOPE_MANAGEMENT_KEY from the
environment for identity checks; the manifest supplies the target. For a
standalone diagnostic independent of provisioning, run:
DESCOPE_PROJECT_ID=your-project-id \
DESCOPE_MANAGEMENT_KEY=temporary-project-key \
TEDIX_OS_URL=https://os.example.workers.dev \
bun run --cwd packages/installation-manifest identity:preflightThe report lists the supplied OS hostname and its expected login URL:
| Surface | Approved Web Domain | Redirect URL |
|---|---|---|
| Tedix OS | os.example.workers.dev |
https://os.example.workers.dev/login |
Approved Web Domains contain hostnames only — no scheme, path, or trailing
slash. The preflight checks the project’s OIDC issuer, verifies the management
key by exporting project configuration, confirms every exact hostname, and
checks that the sign-up-or-in flow exists and is enabled. It also verifies
that the project-level owner, admin, member, and viewer roles grant
tedis:read, which an authenticated Tedix OS needs to load its workspace. Descope
exposes project export as an HTTP POST, but it is a read operation: Tedix does
not call project import, update, or key-management endpoints. Revoke the
temporary key after validation; treat diagnostic output as potentially
sensitive.
For a manifest that requires Descope plus Tedix OS, provision --apply
requires a passing identity report before the Cloudflare account preflight and
before any resource mutation. Plan mode remains read-only and does not require
identity credentials.
This is a configuration check, not deployed authentication certification. It does not verify redirect registration, execute the login flow, or exercise broker callbacks, cookies, tenant selection, session renewal, or logout. Provisioning checks the manifest OS worker’s declared configuration, not deployed routes or browser build settings. Those must still agree with it.
The current OS hostname resolver and session-broker origin allowlist remain
Tedix-host-specific; the broker’s Descope refresh-cookie twin also names
auth.tedix.dev. Therefore an approved workers.dev hostname and a passing
identity report do not make independent OS login work. Installation-owned
routing, login/broker origins, and the matching authentication cookie topology
must be implemented together, then verified in a live browser. workersDev
controls Worker exposure only; it is not an authentication setup switch.
Deployment profiles
@tedix/installation-manifest/profiles defines what each supported profile —
developer, smb, enterprise — must declare before it can be certified
profile-ready. Requirements are additive: SMB includes developer plus an
operator UI surface (Tedix OS), a required queue, and lifecycle
backup; enterprise includes SMB plus required browser capability and governed
workstation containers, and a mandatory restore test. Every profile requires
the identity provider prerequisite and lifecycle export — customer-owned data
contracts are part of the product promise, not an upsell.
certifyProfileReadiness(manifest) checks a manifest against its declared
profile and returns deterministic, sorted issues; this is what the
profile-ready certification level means before any live-account proof. The
sanitized developer example passes for developer and reports exactly the
missing surfaces and requirement upgrades when re-declared as a higher
profile.
Resource provisioning
Import provisionCloudflareResources from
@tedix/installation-manifest/provision to materialize the manifest’s
account-level resources. It runs the capability preflight first and refuses to
act when that fails, honoring fail-before-mutation. The default plan mode
stays strictly read-only and reports the action an apply run would take per
resource; apply creates missing resources idempotently and adopts existing
ones instead of recreating them, resolving unresolved coordinates from the
created ids.
Every resource kind has exactly one compile-time-checked provisioning path: D1, R2, KV, Queues, and Vectorize are API-creatable; Durable Objects, Workflows, services, assets, and containers are deploy-owned (materialized by deploying the declaring Workers, reported but untouched); Browser Rendering and Workers AI are account capabilities proven by the preflight; Hyperdrive requires operator-held origin credentials and is only verified.
executeD1Statements runs ordered SQL against a provisioned D1 database over
the REST API, stopping at the first failure — the sanctioned path for
migrations and sanitized seed data, whose SQL always comes from the caller’s
tracked files, never from this package. bootstrapPlan derives the sanitized
seed profile, secret-free bootstrap inputs, and target D1 resources from the
manifest.
bun run provision --manifest <path> (in packages/installation-manifest)
uses the same Wrangler login or explicit API token and prints the plan;
mutation requires the explicit --apply flag, and --json emits the
machine-readable report. Interactive apply reads only DESCOPE_MANAGEMENT_KEY
for the identity check above. The former --os-url, --descope-base-url, and
--descope-management-api-base provisioning flags are removed; target settings
belong in the manifest, not parallel command-line or environment overrides.
Provisioning creates resources, not a complete running installation; it does
not deploy Workers or replace the remaining bootstrap and identity setup.
Backup, restore, and upgrade
@tedix/installation-manifest/lifecycle implements the manifest’s lifecycle
promises against a live account: exportD1Database (the polling export
protocol, returning the SQL dump), restoreD1Database (statement-split
restore through the same caller-supplied-SQL path the bootstrap uses),
verifyD1Restore (per-table row-count diff enumerated from the source
schema, so a missing table fails instead of narrowing the check),
r2BackupRoundtrip (write → copy under a backup prefix → byte-compare), and
applyD1Upgrade (migration statements plus a schema verification against a
restored database — recovery must accept the next upgrade, not just serve
reads). All network access is injectable; tests never leave the process.
Wrangler overlays
Each worker declares a workspace-relative authored sourceConfig and its
installation-specific non-secret vars. Import createWranglerOverlay from
@tedix/installation-manifest/wrangler-overlay to combine that manifest with
the authored Wrangler JSONC for one worker and installation environment.
workersDev is an explicit per-worker installation decision. The overlay
discards an authored workers_dev setting and uses the manifest value, so a
fresh-account rehearsal can intentionally expose a worker at its account’s
<subdomain>.workers.dev hostname without requiring a DNS zone. Set it to
false for a custom-domain-only worker.
The generator strips authored env branches and all source account, worker,
route, var, secret, binding, and resource coordinates before applying the
manifest. It preserves non-coordinate runtime structure, including main,
compatibility flags, observability, placement, triggers, and Durable Object
migrations. Output is deterministic Wrangler-compatible JSON; required secret
names are returned separately and secret values are never emitted.
Preview mode permits an uncertified schema-valid manifest but rejects unresolved
coordinates. Deploy mode requires a successfully certified manifest at
installation-ready or operational. The tracked
apps/api/wrangler.oss-example.json and matching .secrets.json name artifact
are regenerated with bun run installation-manifest:overlay; CI enforces drift
with bun run installation-manifest:overlay:check.
These generated files are sanitized public preview artifacts, not deployment or
clean-account certification. Managed production coordinates are never published:
the export rewrites every published wrangler.jsonc to a sanitized public base
with no account, zone, or resource identifiers, and the development repository
that holds the authored configuration stays private. See
Release status.