Documentation for Cumments v0.31.0.

Operator API

Enabled by setting security.operator_token. All operator routes require Authorization: Bearer <token>.

Operator routes are rate limited (60 requests/minute per client key).

List sites

QUERY /api/v1/operator/sites

Optional JSON body (an empty body means default pagination):

{ "page": 1, "per_page": 20, "site_id": "my-blog" }

Returns every database-tracked site merged with the operator-declared [sites] overlay. Each origin carries a source of "verified" or "config". Pagination and filtering use page, per_page (1-100, default 20) and optional site_id from the body; the response shape is { "data": [...], "meta": { "total", "page", "per_page", "total_pages" } }.

Revoke a verified origin

POST /api/v1/operator/sites/{site_id}/origin-revocations

Body: { "origin": "https://blog.example.com" }. Origins declared in the config file cannot be revoked here — edit the config instead.

Rotate / revoke the HMAC secret

POST /api/v1/operator/sites/{site_id}/secret-rotations — returns the new secret exactly once.

DELETE /api/v1/operator/sites/{site_id}/secret — removes the secret and falls back to origin auth.

Both refuse to touch sites whose secret is declared in the config file.

Export an adoption snippet

GET /api/v1/operator/sites/{site_id}/config-snippet

Returns { "site_id": "...", "toml": "..." }; toml is the block to paste into [sites] when the operator wants to move a database-tracked site into declarative config.

Rotate the claim token

POST /api/v1/operator/sites/{site_id}/claim-token-rotations

Returns a new claim_token exactly once and invalidates the previous token. Use this when a claim token may have leaked.

Start a site retirement

POST /api/v1/operator/sites/{site_id}/retirement

Operator mirror of the claim-token retire endpoint: marks the site retiring immediately (writes get 410 code=site-retired) and lets the background pass retirement the Matrix Space/rooms and clear local projections. Returns 202 Accepted with the retirement state. See Sites for the full flow. Sites declared in [sites] cannot be retired this way. GET /api/v1/operator/sites/{site_id}/retirement returns the current state while its local row still exists.

List quarantined rooms

QUERY /api/v1/operator/quarantined-rooms

Optional JSON body with the same page / per_page / site_id fields as List sites.

Returns rooms whose adoption failed governance checks and are currently quarantined, with the room id, site/page, quarantine reason, when the room was first quarantined, how many consecutive adoption attempts failed, and when the next automatic retry is scheduled (null means manual attention is required). Quarantined rooms are retried on a 1h/6h/24h schedule; after the fourth consecutive failure they require reinstate. A successful re-registration clears the quarantine automatically. The same pagination/filter fields and { "data", "meta" } shape apply.

Reinstate a room

DELETE /api/v1/operator/quarantined-rooms/{room_id}

Clears a room's quarantine and makes it the canonical room again (any other active room for the same page is superseded). The operation is idempotent: reinstating an already-active room also returns 204; an unknown room returns 404.

Upgrade a comment room

POST /api/v1/operator/rooms/{room_id}/upgrades

Body: {"new_version": "12"}. Upgrades a registered active comment room via the homeserver's native /upgrade and converges the replacement: metadata is repaired, the room is re-linked into its site Space (the old child's via is cleared best-effort), site roles are re-invited, and the new room becomes the registry's active room (the old one is superseded and cleaned up). The operation is idempotent: an existing m.room.tombstone is reused. Spaces, unknown rooms, non-active rooms, invalid versions and versions that are not newer than the room's current version are rejected with a 4xx problem response. This endpoint is the operator mirror of the site-level POST /api/v1/sites/{site_id}/pages/{page_slug}/upgrades (claim token); both execute through the AS bot.

Inspect and recover upgrade intents

QUERY /api/v1/operator/room-upgrade-intents

Lists durable authorization for native upgrades, including the target version, observed replacement, lifecycle state (requested, observed, adopted, failed, manual) and error. The body supports page, per_page and an optional status filter.

POST /api/v1/operator/room-upgrade-intents/{room_id}/recoveries

Body: {"new_version": "13", "replacement_room": "!successor:server"}.

For a failed or manual intent, this confirms the exact reviewed successor and completes convergence if Matrix still agrees: the old room must still carry a tombstone naming that successor, and the successor create event must have the expected version and predecessor. On success, the replacement becomes the registry's active room, the old room is superseded, and the intent is marked adopted. Mismatched input is rejected without moving local identity.

Recovery checklist

Before calling recover, verify all of the following:

  1. The durable intent names the same target version and replacement you intend to approve.
  2. You saw the native upgrade in Matrix (or in homeserver logs), not merely an unexplained room ID.
  3. The old room's m.room.tombstone still exists and its replacement_room exactly matches the intent.
  4. The replacement's m.room.create has the expected room version and its predecessor.room_id points to the old room.
  5. Governance state is safe to converge: the site Space still has trusted admin/manager entries, and no unrelated power-level change occurred during the failure.

Do not recover an intent whose origin is unclear, whose replacement was not created by the Cumments-managed /upgrade call, or whose successor metadata points to another site/page. Recovery never repairs a mismatched successor by guessing; it fails closed and records why.

Inspect and retry projection repairs

QUERY /api/v1/operator/projection-repairs

Optional JSON body: { "page": 1, "per_page": 20, "status": "pending|manual|resolved" }.

Returns the durable repair queue for facts that failed closed during projection. Rows contain only identifiers and diagnostics; the authoritative Matrix event is fetched during repair. The standard { "data", "meta" } pagination shape applies.

GET /api/v1/operator/projection-repairs/{target_event_id}

Returns one repair row.

POST /api/v1/operator/projection-repairs/{target_event_id}/retry

Requeues a pending or manual repair immediately. Resolved repairs return 409; unknown repairs return 404. A successful requeue returns 202 Accepted with the updated row.

Start a comment-room retirement

POST /api/v1/operator/rooms/{room_id}/retirement

Marks the registered active room Retired immediately (new writes stop), then the background reconciler renames the Matrix room [retired], removes its alias, leaves it as the AppService sender and every site virtual user, and clears the local projections. This is the operator mirror of POST /api/v1/sites/{site_id}/pages/{page_slug}/retirement (claim token); both return 202 Accepted and use the same management use case. Unknown or already-retired rooms return 404.

GET /api/v1/operator/rooms/{room_id}/retirement returns the current retirement state for a room that is still in the registry.

Governance fallback

The operator can act on a site's behalf for site-level roles:

  • POST /api/v1/operator/sites/{site_id}/admin-claims / DELETE /api/v1/operator/sites/{site_id}/admins/{user_id}
  • POST /api/v1/operator/sites/{site_id}/manager-claims / DELETE /api/v1/operator/sites/{site_id}/managers/{user_id}
  • POST /api/v1/operator/sites/{site_id}/ownership-transfers — operator mirror of the claim-token ownership transfer.

These use the same handlers and response shapes as the claim-token Governance endpoints, including the pending-claim verification step — the operator registers the claim, but the target MXID must still DM the bot to activate it.