carlos / platform Public

Canary teardown needs verbs: retire a channel, revoke a grant, purge a deleted instance's files Feature Request open

Ask: CLI verbs to finish tearing down a session canary, so that "every session brings its own canary" (titogo AGENTS.md) doesn't leave debris on the box and in S3 every time.

What happened (titogo, 2026-09-26): a session stood up a throwaway canary (ssespike-canary.go.tito.io, channel canary/ssespike) with the documented sequence: carlos promote → carlos instances create → titogo add → auth grant. It tore it down with titogo remove + carlos instances delete. Three things had no verb:

  1. Retiring the channel. canary/<name> can only be retired from the console web UI (POST /a/{sqid}/apps/{app}/canary/retire). A headless session can't retire its own channel, so the pointer at apps/bae/titogo/channels/canary/<name>.json stays. The only alternative is deleting the S3 object by hand, which bypasses the console's follower check. → Request: carlos channels retire -app <app> -account <acct> canary/<name>, with the same follower check the console does.
  2. Revoking a grant. auth grant writes memberships/<slug>.json to the identity bucket; nothing removes it. → Request: a revoke verb (or have instance deletion drop that slug's membership file).
  3. Leftover files. After carlos instances delete, the instance's parked DB files, its /opt/carlos/live/<host> link, /data/titogo/instances/<host>/, and its litestream replica in S3 need an operator to clear by hand. → Request: carlos instances delete either removes them, or offers a -purge flag that does (after its safety checks), and says what it left otherwise.

Also worth knowing: because the host name ended in -canary, controld additionally started an old-style titogo-canary@<name>-canary systemd unit with its own database, alongside the platform exec instance. Nothing told the session this had happened. Either don't match platform-created instances on the -canary suffix, or make instances create warn about it.

Filed from a titogo session (Claude, for Paul Campbell). The cleanup of this one canary was done by hand with Paul's authorisation.

Follow-up from the cleanup of this canary:

  • The console's retire endpoint needs a browser session and a CSRF value. There's no token path, so a headless session can't retire its channel at all, even with a valid console token. A CLI verb, or accepting the API token there, would close that.
  • carlos canary rm only knows canaries created by carlos canary deploy, so it can't retire a channel made by promote.
  • A deleted platform exec instance left these for an operator: /data/<host>.db{,-wal,-shm}, /data/<host>.env, /data/.activator-<host>.db-litestream/, the /opt/carlos/live/<host> link, a titogo-instance-dir@<host> oneshot unit, and its litestream replica under instances/<host>/ in the region bucket.

Update: retired canary/ssespike by deleting its pointer (apps/bae/titogo/channels/canary/ssespike.json) by hand, with Paul's authorisation. First we checked that no console instance or box route followed it. That's the check the console makes before it retires a channel, but done by hand, which is exactly the workaround a CLI verb would remove.