carlos / platform Public

"Delete now" leaves an app's domain claims and its instances' parked data behind; a same-host instance inherits the old database Issue open

Found while tearing down and rebuilding the letsjelly.dev testbed on carlos.exciting.io (2026-10-05), on behalf of James. The #233 review asked for it as its own thread, because one half of it crosses a boundary between tenants of one app. It extends #219.

1. A re-created instance at the same host restores the old instance's database

carlos instances delete and the console's purge ("Delete now") both keep each instance's parked data by design (provision.go around :1067, apiinstances.go around :1590):

  • the Litestream replica at <CARLOS_HIBERNATE_BUCKET>/<prefix>/<host>/ (activator.go HibernatePrefix);
  • move staging at moves/<host>/;
  • the box's /data/<host>.db*, /data/.activator-<host>.db-litestream, and /data/<host>.imports directory.

No CLI verb removes these, and purge never touches the data bucket.

ExecBackend.Restore (backend_exec.go around :264-353) then keys on the host alone:

  • A local /data/<host>.db is used as a warm cache, and the seed does not run.
  • Otherwise the box restores from the replica.

So whoever next creates an instance at that host gets the previous instance's database, under any app or account that can create the host, and never runs its seed.

createleftovers.go (#199, labelled a stopgap) refuses the create when it finds LTX files in the console region's data bucket. But:

  • it never sees the box's local DB;
  • its refusal tells the member to "ask an operator to purge it", and there is no purge command to run.

For managed Jelly that means a released team label, re-used, could inherit the old team's mail. The manager avoids this only because it never re-uses an instance host (instancehost.go).

2. Domain claims outlive the app

domains/<host>.json is deleted only by DetachClaim, and the detach door needs a live app (domainsGate → apiAccountApp → HasAppIn with deleted_at = 0). So:

  • Trashed apps: their claims can't be detached by anyone through the API. They are inert, because their routes are gone, but they still refuse every other account and app those names.
  • Purged apps: their claims are orphaned. A later app with the same name in the same account inherits them, including an instance catch-all.

Asks

  1. Purge should remove or neutralise the app's claims. It could detach them in the same walk that already tears down its stores and mail. release can't import domains (an import cycle), so that walk belongs in the console's purge door.
  2. A supported way to remove an instance's parked data, as a member or an operator: - an instances purge-data verb, or a "Delete data" step on the console's instance page; - it would remove the replica, the move staging, and each box's local files, the box part through the existing agent request path.

Until it exists, #199's refusal message points at a command nobody has.

  1. Whether Restore should refuse to adopt a local DB or replica whose stored owner (account and app) differs from the new record's. That is defence in depth for the cross-app case.

The workaround used on the testbed was bucket deletes by an operator plus moving the /data files aside. It is written up in jam's docs/runbook-letsjelly-dev-domains.md, "Tearing the testbed down".