ReviewOS

stacks/analyticshq

A site cannot change hands, so a departing owner strands it

#48
Open glennmichael123 opened this 9 days ago ·
9 days ago

sites.owner_id is set at creation and there is no endpoint that changes it. Nothing in routes/analytics.ts writes owner_id at all — the only way to move a site is scripts/account.ts --attach, run by hand on the box.

Roles make this reachable rather than theoretical. An admin can now do everything except two things: destroy the site, and be the owner. So a team can be fully staffed and still unable to continue without the one person whose name is on the row.

Where it bites

  • Someone leaves. The owner's account is deleted or abandoned. site_members.user_id cascades on user delete, and sites.owner_id has no such constraint, so the site is left pointing at a user id that no longer exists. Every check that compares owner_id then fails closed: no owner, no destructive operations, and planForSite() resolves an ownerless site to free — so a paid site silently loses its plan.
  • An agency hands a client their own site. The natural end of a build-and-handover engagement, and it currently requires our intervention.
  • Billing sits with the wrong person. Entitlement resolves from the owner's subscription by design. If the owner is not the one paying, the only fix is to change the owner.

Suggested

An owner-only POST /api/sites/{siteId}/transfer taking a user who is already an admin on that site. Constraining the target to an existing admin avoids inventing a second invitation flow and means the recipient has already accepted a relationship with the site.

Worth deciding:

  • Does it need the recipient's consent? Ownership carries billing. Handing someone a site whose subscription resolves from their account, without asking, is not obviously acceptable. A confirmation step is probably right.
  • What happens to the old owner? Demoting them to admin keeps them working and keeps the site non-empty. Dropping them entirely is a footgun.
  • Guard the last owner. Transfer is the only safe way to relinquish; there should be no path that leaves owner_id null.

The FK gap is worth fixing regardless of transfer: site_members cascades on user delete and sites.owner_id does not, so deleting a user today can orphan their sites rather than refusing.

Sign in to comment on this issue.