Smeldr has had an auto-bootstrap admin token since A83: boot a fresh instance with ENABLE_TOKENS, and if smeldr_tokens is empty, one gets minted for you automatically, logged once, ready to use. Convenient, and until this fix, quietly incomplete for one specific thing: it couldn't ratify or supersede a Decision.
Same word, two different mechanisms.
The word is "admin"
A bearer token in Smeldr carries a role field, one of "author", "editor", "admin", baked into the signed JWT at creation time. That field gates the generic Role hierarchy: create, publish, archive, the everyday CRUD surface. The bootstrap token has role="admin", so it can do all of that.
Separately, Smeldr's newer governance layer (RoleStore, smeldr_role_grants) grants roles to tokens explicitly, with scope. This is what D34's Strict: true transitions check: the ratify/supersede edges on Decision. RoleGranted queries smeldr_role_grants only. It never looks at the token's own role field.
Two systems, one shared vocabulary. The bootstrap token's role="admin" satisfied the first and said nothing to the second.
The ID mismatch underneath
Fixing this meant granting the bootstrap token a role via RoleStore.Grant, which takes a TokenID to grant against. The obvious move, grant it against smeldr_tokens.id, is wrong. RoleGranted is checked against ctx.User().ID, the random ID embedded in the JWT's own claims at signing time. smeldr_tokens.id is a completely different value: the SHA-256 fingerprint of the raw signed token, used as the table's primary key. The two are computed independently and, before this change, never cross-referenced anywhere in the codebase.
TokenStore.Create never surfaced the JWT's User.ID, only the raw token string. Recovering it after the fact meant decoding a token you just signed yourself, purely to read back a value you already had.
The fix
A small shared helper, createToken, factors Create's existing logic out and returns the User.ID alongside the raw token. Create's public signature doesn't change: it just discards the extra value. ensureBootstrap (unexported, so free to change) now returns that ID, and App.Handler() grants it admin via RoleStore.Grant in the same boot sequence, whenever governance is wired:
if created && a.governance != nil {
a.governance.Grant(ctx, RoleGrant{TokenID: userID, RoleName: "admin"})
}
Fail-open, logged on error, idempotent: the same shape ensureBootstrap/seedDefaultRoles already established for this kind of thing. No new exported API.
The takeaway
If your app has two authorization systems that happen to share a role name, don't assume a value that satisfies one satisfies the other. Trace the actual check, not the field that looks like it should matter. The bootstrap token had "admin" written all over it and still couldn't do the one thing a governance-enabled instance exists for.