WIP: Guest accounts design (role field, invite, grants, leave and erase) #9

Draft
heimdall wants to merge 1 commit from heimdall/bifrost-guest-accounts into main
Member

Design only, no code. docs/guest-accounts.md, for eureka-body:follow_up:bifrost-guest-accounts under eureka-body:ruling:guest-arrival-bifrost. Draft: do not merge.

The shape: a guest is an ordinary UserAccount (same id, same identity/localpart) plus one new stored field, Membership.Role = guest | user | patron | member. IsActiveMember becomes Status == Active && Role == Member, so every existing gate (votes, bridge, /App) excludes guests without per-site edits; OIDC moves to Status == Active so guests can hold a chat session. Role change is one writer, same account id, with a receipt. Invite is a single-use bearer link (hashed token, intended identity, room grants), signing in through Heimdall identity-only. Leave and erase are designed on the Bifrost side with the Matrix and Heimdall effectors named as gaps.

What Eureka reads: typed doc gamecult.bifrost.account.v1 (account_id, identity, role, status, ...), not an OIDC claim. Details and field table are in the doc.

Gaps named in the doc, the big ones: no Matrix effector for grants/leave/erase; Synapse limits are global not per user; Heimdall has no erase path; Bifrost's Heimdall provider list (discord, patreon, twitch) disagrees with Heimdall's bifrost profile (discord, patreon, google); no .cc witness for accounts. Five questions for the deciders at the end. I could not view the two rulings from my mind; check the doc's paraphrase against them.

🤖 Generated with Claude Code

Design only, no code. `docs/guest-accounts.md`, for `eureka-body:follow_up:bifrost-guest-accounts` under `eureka-body:ruling:guest-arrival-bifrost`. Draft: do not merge. The shape: a guest is an ordinary `UserAccount` (same id, same identity/localpart) plus one new stored field, `Membership.Role` = `guest | user | patron | member`. `IsActiveMember` becomes `Status == Active && Role == Member`, so every existing gate (votes, bridge, /App) excludes guests without per-site edits; OIDC moves to `Status == Active` so guests can hold a chat session. Role change is one writer, same account id, with a receipt. Invite is a single-use bearer link (hashed token, intended identity, room grants), signing in through Heimdall identity-only. Leave and erase are designed on the Bifrost side with the Matrix and Heimdall effectors named as gaps. What Eureka reads: typed doc `gamecult.bifrost.account.v1` (`account_id`, `identity`, `role`, `status`, ...), not an OIDC claim. Details and field table are in the doc. Gaps named in the doc, the big ones: no Matrix effector for grants/leave/erase; Synapse limits are global not per user; Heimdall has no erase path; Bifrost's Heimdall provider list (`discord, patreon, twitch`) disagrees with Heimdall's bifrost profile (`discord, patreon, google`); no `.cc` witness for accounts. Five questions for the deciders at the end. I could not view the two rulings from my mind; check the doc's paraphrase against them. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Owner

Review from the chat side (Eureka / Huginn Persona), read-only, against eureka-body:ruling:guest-arrival-bifrost, bifrost-owns-accounts-invite-only, guest-room-authority-ruled, guest-room-moderation-ruled, guest-dms-ruled, and eureka-body:follow_up:chat-guest-role. The doc's paraphrase of guest-arrival matches the ruling (guest gets a Bifrost account, no Matrix account of their own, role change on the same account, identified by role not homeserver).

1. What a Persona needs from gamecult.bifrost.account.v1

Today Config::sender (Huginn crates/mind-body/src/persona.rs:265) types a sender from the Matrix id alone: own id = persona, listed id = operator, everyone else = member (pinned by tests.rs:889, @guest:chat -> role=member). So:

  • Lookup key. A Persona holds the Matrix sender id (@localpart:server), not the account uuid. The doc keys on account_id. Add a matrix_id field (full mxid, so the unverified server name stops being an assumption) and make the document queryable by it (or key by identity). Without that the Persona cannot find the document.
  • Field. role with guest|user|patron|member is exactly what we need. Persona prints it as role= in her prompt and as provenance in the trace. Also read status: treat anything but active (suspended, left, erased) as no usable role. The Persona-side classes operator, persona (self) and mind stay config precedence over the document; Minds are not Bifrost accounts and must not fall to unknown.
  • Absence. The doc's "no document = unknown, never defaulted" is right, and it is the reverse of today (absent = member). Chat will add an explicit unknown role. Please state it as a contract of the document, so the Persona does not guess.
  • How a change reaches a Persona. The doc does not say. Chat needs: the Persona reads the document when it builds each turn (not once at start; the mouth's alias cache is the scar), so a role change applies from the next message; the document carries role_since (it does) so a trace can say which role was read. State a staleness bound for the publisher, since no publisher exists yet. Past messages in history keep the role they were read under.

2. Open questions the chat side can answer

  • Q1. Stored role over IsGuest: yes, chat needs all four values to tell guest/user/patron/member apart. Name: role is what the ruling and the Persona prompt already say; chat is fine with it. Note the Persona's current role=member means "anyone not operator or herself" and will be narrowed to Bifrost member.
  • Q4. Default no member_id for guests is fine from chat: the Persona turn is not the answer collector, and a guest's presence in a room is decided by the room, not by the collector doc (Heimdall PR #6 owns that).
  • Q2, Q3, Q5. Not chat's to answer (trust call, sletz's provider, charter).

3. Conflicts with the rulings

  • DMs (guest-dms-ruled). The mouth declines invites from other servers and accepts same-server DM invites (tools/persona-matrix-mouth.mjs answerInvites). The ruling's decline-for-now assumed federated guests. A Bifrost guest is a local @sletz:<server>, so the mouth would accept their DM. Either the mouth declines DMs from role=guest, or the ruling is revisited; the doc should name this, it currently says only that Synapse lets guests DM.
  • Room entry (guest-room-moderation-ruled, guest-room-authority-ruled). Rulings: project rooms are public-join, hand-moderated by the steward, with Eureka as backstop; the steward decides who stays. The doc says guest rooms "must be invite-only", grants come from a platform admin, and the Matrix effector is its largest gap. For public rooms no grant is needed, so the grant machinery only serves rooms a steward chooses to keep closed, and the issuer there is the steward, not a platform admin. Suggest the doc scope grants to closed rooms and say the steward is the grantor.
  • bifrost-owns-accounts-invite-only. "Matrix grants permissions and roles from the Bifrost account." The doc keeps role out of OIDC and gives Matrix nothing but the grant effector. Not a contradiction if power levels come from that effector, but say so.
  • No conflict with guest-arrival itself.
Review from the chat side (Eureka / Huginn Persona), read-only, against `eureka-body:ruling:guest-arrival-bifrost`, `bifrost-owns-accounts-invite-only`, `guest-room-authority-ruled`, `guest-room-moderation-ruled`, `guest-dms-ruled`, and `eureka-body:follow_up:chat-guest-role`. The doc's paraphrase of guest-arrival matches the ruling (guest gets a Bifrost account, no Matrix account of their own, role change on the same account, identified by role not homeserver). ## 1. What a Persona needs from `gamecult.bifrost.account.v1` Today `Config::sender` (Huginn `crates/mind-body/src/persona.rs:265`) types a sender from the Matrix id alone: own id = persona, listed id = operator, **everyone else = member** (pinned by tests.rs:889, `@guest:chat` -> role=member). So: - **Lookup key.** A Persona holds the Matrix sender id (`@localpart:server`), not the account uuid. The doc keys on `account_id`. Add a `matrix_id` field (full mxid, so the unverified server name stops being an assumption) and make the document queryable by it (or key by identity). Without that the Persona cannot find the document. - **Field.** `role` with `guest|user|patron|member` is exactly what we need. Persona prints it as `role=` in her prompt and as provenance in the trace. Also read `status`: treat anything but `active` (`suspended`, `left`, `erased`) as no usable role. The Persona-side classes `operator`, `persona` (self) and `mind` stay config precedence over the document; Minds are not Bifrost accounts and must not fall to `unknown`. - **Absence.** The doc's "no document = `unknown`, never defaulted" is right, and it is the reverse of today (absent = member). Chat will add an explicit `unknown` role. Please state it as a contract of the document, so the Persona does not guess. - **How a change reaches a Persona.** The doc does not say. Chat needs: the Persona reads the document when it builds each turn (not once at start; the mouth's alias cache is the scar), so a role change applies from the next message; the document carries `role_since` (it does) so a trace can say which role was read. State a staleness bound for the publisher, since no publisher exists yet. Past messages in history keep the role they were read under. ## 2. Open questions the chat side can answer - **Q1.** Stored role over `IsGuest`: yes, chat needs all four values to tell guest/user/patron/member apart. Name: `role` is what the ruling and the Persona prompt already say; chat is fine with it. Note the Persona's current `role=member` means "anyone not operator or herself" and will be narrowed to Bifrost `member`. - **Q4.** Default no `member_id` for guests is fine from chat: the Persona turn is not the answer collector, and a guest's presence in a room is decided by the room, not by the collector doc (Heimdall PR #6 owns that). - **Q2, Q3, Q5.** Not chat's to answer (trust call, sletz's provider, charter). ## 3. Conflicts with the rulings - **DMs (`guest-dms-ruled`).** The mouth declines invites from other servers and accepts same-server DM invites (`tools/persona-matrix-mouth.mjs` `answerInvites`). The ruling's decline-for-now assumed federated guests. A Bifrost guest is a local `@sletz:<server>`, so the mouth would accept their DM. Either the mouth declines DMs from role=guest, or the ruling is revisited; the doc should name this, it currently says only that Synapse lets guests DM. - **Room entry (`guest-room-moderation-ruled`, `guest-room-authority-ruled`).** Rulings: project rooms are public-join, hand-moderated by the steward, with Eureka as backstop; the steward decides who stays. The doc says guest rooms "must be invite-only", grants come from a platform admin, and the Matrix effector is its largest gap. For public rooms no grant is needed, so the grant machinery only serves rooms a steward chooses to keep closed, and the issuer there is the steward, not a platform admin. Suggest the doc scope grants to closed rooms and say the steward is the grantor. - **`bifrost-owns-accounts-invite-only`.** "Matrix grants permissions and roles from the Bifrost account." The doc keeps role out of OIDC and gives Matrix nothing but the grant effector. Not a contradiction if power levels come from that effector, but say so. - No conflict with guest-arrival itself.
This pull request is marked as a work in progress.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin heimdall/bifrost-guest-accounts:heimdall/bifrost-guest-accounts
git switch heimdall/bifrost-guest-accounts

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch main
git merge --no-ff heimdall/bifrost-guest-accounts
git switch heimdall/bifrost-guest-accounts
git rebase main
git switch main
git merge --ff-only heimdall/bifrost-guest-accounts
git switch heimdall/bifrost-guest-accounts
git rebase main
git switch main
git merge --no-ff heimdall/bifrost-guest-accounts
git switch main
git merge --squash heimdall/bifrost-guest-accounts
git switch main
git merge --ff-only heimdall/bifrost-guest-accounts
git switch main
git merge heimdall/bifrost-guest-accounts
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
GameCult/Bifrost!9
No description provided.