v0.105.1 — A character's owner on a token the GM placed
Shipped 2026-09-21 — built on hotfix/0.105.1, one commit; the merge
hash and tag are in the changelog. A hotfix from the first
prod session on v0.105.0: a player who owns a character
could not pick up the token the GM had placed for it, and the admin's
ways around that were shut too.
- The linked character's owner is in the impersonatable set.
GET /api/tokens/impersonatableanswered from three sources: the token'sownercolumn (whoever placed it), the token allows, and the character's grant rows. The owner never has a row — that is the one implicit permissionresolveCharacterAccessexists to apply — so a player whose character the GM dropped onto the map was in none of the three. The client guard reverted every attempt they made at their own character with "You don't have permission to impersonate…", and an admin impersonating them saw the same notification over a selection that otherwise held. The per-token check (canImpersonateToken) had reached the owner through the permission cascade all along; the list had not. It now resolves each owned linked character throughresolveCharacterAccess, so a sheet a campaign locks (players.sheetEditing: gm-only) answerscontrolthere rather thanedit, as it does everywhere else. - Superuser Mode can transfer a character's ownership — including to
the admin.
POST /api/characters/:id/transfercompared the owner column and nothing else. The mode passes every other check on the character, and the page therefore offered the menu item, and the route refused it with "Only the owner can transfer ownership"; and "Cannot transfer to yourself" was precisely the superuser's case. Now: the owner or the mode may transfer; the target being the current owner is the 400; the previous owner (not the caller) keepsedit; and the new owner is not notified when they did it themselves. The modal offers "Me" when the viewer is not the owner and says who keeps edit access. - The Transfer Ownership modal goes through
authFetch. It was a barefetchwith the bearer token, which never sentX-Impersonate-User-Id— so an admin impersonating the owner opened it as the owner and submitted it as themselves, and was refused as a non-owner. - CI — the pre-deploy snapshot prunes itself. Every push to main took
a manual RDS snapshot and nothing ever removed one; manual snapshots
never expire and the account is capped at 100 per region, so
v0.104.4's deploy died at
SnapshotQuotaExceededwith 93 of them. The step now deletes its own older<instance>-pre-deploy-*snapshots (never a hand-made one) down toRDS_PRE_DEPLOY_SNAPSHOTS_KEEP(repo var, default 20) before taking the new one; the 7-day automated backups remain the longer net.
characterOwner.play.test.ts runs the first two against the real routers
on the harness: the owner in the set at edit, the GM still in, a member
with no grant still out; the superuser's transfer to themselves refused
without the mode, accepted with it, the previous owner left at edit.