Skip to Content
Operate EnergonAdminister Energon

Use /admin when you need to see platform storage health, cut off another account’s API tokens, or retire sites and files across this Energon. The page exists because ordinary hub cleanup only covers work the signed-in person can already write, while leftover tokens and quota drift keep failing publishes after someone leaves. Set ADMIN_EMAILS first, then mint an admin token from /tokens. Connect never grants that scope.

Administer Energon

Who can open /admin

ADMIN_EMAILS is a comma list in deploy vars. Only those addresses can mint an admin token (scope: admin) at /tokens. Admin tokens last at most 7 days, default 1 day, and cannot be never. Admin routes also check that the owner is still on the list, so removing an email strips admin immediately. Ordinary /v1 calls with that token still act as the account.

Put /admin on the Access allow list with the other hub pages. An account token calling /v1/admin is 403 forbidden_admin.

Repair storage health

/stats already shows used versus MAX_PLATFORM_BYTES to every signed-in user. /admin adds pending-purge counts, stale purge claims, locked share gates, and three repairs that do not delete live work:

  1. Recompute the quota ledger from stored sizes.
  2. Run one expiry sweep. If remaining is greater than zero, run it again.
  3. Unlock a share gate by scope (obj:… or ip:…). After too many bad passwords, unlock both.
export ENERGON_ORIGIN="https://hub.your-company.example" test -n "$ENERGON_TOKEN" || { echo "Set an admin ENERGON_TOKEN" >&2; exit 1; } curl -fsS "$ENERGON_ORIGIN/v1/admin/health" \ -H "Authorization: Bearer $ENERGON_TOKEN" curl -fsS "$ENERGON_ORIGIN/v1/admin/quota/recompute" \ -X POST \ -H "Authorization: Bearer $ENERGON_TOKEN" curl -fsS "$ENERGON_ORIGIN/v1/admin/sweep" \ -X POST \ -H "Authorization: Bearer $ENERGON_TOKEN" curl -fsS "$ENERGON_ORIGIN/v1/admin/gates/unlock" \ -X POST \ -H "Authorization: Bearer $ENERGON_TOKEN" \ -H "Content-Type: application/json" \ --data '{"scope":"obj:/HANDLE/f/FILE_ID/name.md"}'

Hub confirms recompute and sweep. Unlock scope is a gate_attempts key such as obj:… or ip:…. After too many bad passwords, unlock both. Responses return counts and scopes, never published bytes or secrets. Every execute is recorded in the audit log.

Revoke tokens across accounts

When someone leaves, /tokens only lets that person revoke their own keys. On /admin, type their handle in Tokens across accounts, List, then Revoke stale or Revoke all. Type the count to confirm. The calling admin token stays live.

curl -fsS "$ENERGON_ORIGIN/v1/admin/tokens?owner=HANDLE" \ -H "Authorization: Bearer $ENERGON_TOKEN" PREVIEW=$(curl -fsS "$ENERGON_ORIGIN/v1/admin/tokens/revoke" \ -X POST \ -H "Authorization: Bearer $ENERGON_TOKEN" \ -H "Content-Type: application/json" \ --data '{"owner":"HANDLE","target":"all"}') CONFIRM=$(printf '%s' "$PREVIEW" | jq -r .confirm) curl -fsS "$ENERGON_ORIGIN/v1/admin/tokens/revoke" \ -X POST \ -H "Authorization: Bearer $ENERGON_TOKEN" \ -H "Content-Type: application/json" \ --data "{\"owner\":\"HANDLE\",\"target\":\"all\",\"confirm\":\"$CONFIRM\"}"

Listing is metadata only: owner, label, hint, scope, created, last used, expires, status. Never the secret or the hash. Drift is 409 token_revoke_drift. Missing owner is 400 bad_owner.

Retire work across accounts

POST /v1/admin/cleanup reuses the personal cleanup preview, confirm hash, skip reporting, and 100-object cap, without involvement scope. Filter by owner handle. last_read_before matches never-read objects and those whose last_read_at is older than the timestamp. Sample includes owner and last read. Last read is a floor: edge cache hits never stamp it, and hub cells say No recorded read. Reads can lag up to about a day. Do not treat a quiet last-read cell as proof nobody still opens the link.

PREVIEW=$(curl -fsS "$ENERGON_ORIGIN/v1/admin/cleanup" \ -X POST \ -H "Authorization: Bearer $ENERGON_TOKEN" \ -H "Content-Type: application/json" \ --data '{"target":{"owner":"HANDLE","last_read_before":"2026-01-01T00:00:00.000Z"},"action":"set_ttl"}') CONFIRM=$(printf '%s' "$PREVIEW" | jq -r .confirm) # Show the human matched, eligible, bytes, and sample. Then: curl -fsS "$ENERGON_ORIGIN/v1/admin/cleanup" \ -X POST \ -H "Authorization: Bearer $ENERGON_TOKEN" \ -H "Content-Type: application/json" \ --data "{\"target\":{\"owner\":\"HANDLE\",\"last_read_before\":\"2026-01-01T00:00:00.000Z\"},\"action\":\"set_ttl\",\"confirm\":\"$CONFIRM\"}"

Set expiry is the safe default. It defaults to 7 days so the owner sees Expires and can push it back. Expire soon is refused on anyone else’s content (400 expire_not_own) and appears on the hub only when the Owner filter is your own handle. Delete is explicit. The admin surface never returns bytes or secrets.

Offboard a person

There is no transfer-ownership feature. The handle is in the public URL and the R2 key, so a move would break saved links. Copy under the new owner at a new address, then expire the original.

  1. Remove the person from Cloudflare Access. That stops the human: no hub, no new tokens.
  2. Revoke their API tokens as above.
  3. On /admin, set Owner to their handle and Preview. You get metadata only.
  4. Keep the few things that matter with Duplicate from the hub, or duplicate_from on POST /v1/sites and POST /v1/files. Anyone with a token can already read over /v1.
  5. Back on the same preview, choose Set expiry (7 days unless you pick otherwise) or Delete, type the count, confirm. The 7-day grace is notice for colleagues who still hold links.
  6. Sweep now if expired objects remain, and check the storage health readout.

Do not d1 execute an UPDATE ... SET owner_id to move content. The audit log will not know about a hand edit. Use copy then expire.

Read Configure Energon policy for ADMIN_EMAILS, and Manage shared work for personal hub cleanup that does not need admin.

Last updated on