Conormis status

The manual

The control plane (provisioning)

The control plane is the SaaS registry ABOVE the engine: a separate Worker (ribose-status-control, wrangler.control.toml) that owns the org + page + binding rows in its own KV namespace and turns a registration into a reviewable instance YAML through the same emission engine the admin's provisioning form uses. It never deploys anything — deploys stay deliberate (a tag or a manual dispatch), and the binding row only records where an emission went.

The live plane

Up since 2026-09-10: https://ribose-status-control.crimson-ribose.workers.dev (namespace CONTROL_KV id 81da03cb-..., the Bearer secret CONTROL_TOKEN). Without the token the whole surface answers 404 — the fail-closed doctrine; the local token copy lives at ~/.ribose-secrets/status-control.token (rotate with wrangler secret put CONTROL_TOKEN --config wrangler.control.toml).

The org lifecycle

An org retires only when no active pages remain (POST /v1/orgs/:id with {"retire": true}; the refusal names the live pages). A retired org refuses new pages; its roster rows stay as bookkeeping.

The registry's backup + restore

The weekly workflow exports the two listings (the plane's ENTIRE state) as a 90-day artifact beside the page-store backups. The restore is re-registration through the API — the emitted instance YAML rides in the pages document — and the registry's restore note in manual 07's worked example is the runbook. The one-liner:

npx wrangler kv key list --namespace-id 81da03cb7a9143bf844f84a504bbabc1   | jq -r '.[] | .name' > control-keys.txt   # the roster of rows; the
                                             # values follow with key get

The lifecycle

  1. Register the org — POST /v1/orgs with the kebab-case id, the name, and at least one bootstrap owner email (the member rows use the same grammar the engine's org surface uses).
  2. Register the page — POST /v1/pages with the org id, the slug, the title, the base URL, a description, and the services as id|name|url|needle lines. The plane emits the instance YAML (GET /v1/pages/<slug>/instance.yaml) and the manifest naming the explicit secret/KV steps. The YAML must parse through the same strict loader the build gate uses — the API returns 400 otherwise, and the leg proves the round trip.
  3. Deploy the emission — the deliberate act (review the YAML, then deploy it as its own worker or register it into a shared deployment, TODO 41).
  4. Record the binding — POST /v1/pages/<slug> with the host the page answers on (and the deployment URL/worker name): the state moves emitted → deployed. {"retire": true} retires it.
  5. Register the page's OP client — POST /v1/pages/<slug>/op-client: the plane performs RFC 7591 dynamic client registration at the plane's identity issuer and re-emits the YAML with admin.oidc real. See the next section.

The page's OP client (TODO 71)

Until this act runs, the emitted YAML carries admin.oidc commented (the operator would register the client at the OP by hand and uncomment). The plane can do that registration itself:

The plane and the live console (the two-way street)

The PLANE is the source of truth for NEW pages: registration emits the YAML, the deployment's /api/pages takes it, and the plane's instance.yaml endpoint regenerates from its row on demand. A deployment's console can also edit a LIVE page's services (the Services register, docs/manual/03) — an out-of-band edit lands in the deployment's registry and is surfaced on the plane when its emission is regenerated. The precedence, stated plainly: new pages are the plane's; live service edits are the deployment operator's, audited like every other act.

Org-wide membership

The plane is the source of truth for WHO belongs to an org across its whole page fleet: POST /v1/orgs/:id/members (email + role owner|operator — the same role grammar the engine's org surface resolves), GET /v1/orgs/:id/members, DELETE by email. Emails normalize exactly as the engine's invites do.

The bridge, stated honestly: adding a member on the plane does not write any page's local org rows. A deployment redeems membership through its own invite flow (the operator sends an invite, the member's verified email claim redeems it) — the plane is the roster, the deployment is the gate.

The API (Bearer CONTROL_TOKEN)

Method Path Purpose
GET /v1/orgs List orgs
POST /v1/orgs Register an org (id, name, ownerEmails)
GET /v1/orgs/:id The org + its bootstrap members
GET /v1/orgs/:id/members The org-wide roster
POST /v1/orgs/:id/members Add a member (email, role)
DELETE /v1/orgs/:id/members Remove a member (email)
GET /v1/pages?org= List page summaries
POST /v1/pages Register a page; returns the emitted YAML
GET /v1/pages/:slug The page row + the YAML
GET /v1/pages/:slug/instance.yaml The emitted instance (text/yaml)
POST /v1/pages/:slug Record a binding, or retire
POST /v1/pages/:slug/op-client RFC 7591 registration at the plane's issuer; re-emits the YAML with admin.oidc real