Docs: desk notes stop saying Bifrost masks answerer ids; pin the door spec at 79d954c
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
e8e1fe65d1
commit
e99e707a0d
2 changed files with 4 additions and 4 deletions
|
|
@ -22,7 +22,7 @@ Ratatoskr's Desk row per Mind is empty "until Bifrost feeds it" (FACT `src/app/f
|
|||
FACT today: a signed-in member holds only the cookie `__Host-bifrost_session`; the default scheme is Cookie (`Program.cs:189-200`); no `AddValidation` exists, so no access token authenticates any `/api/*` route; the desk handler is `.AllowAnonymous()` and judges its own bearer (`DeskEndpoints.cs`, `MapDesk`), and the cookie scheme cannot authenticate `/api/desk/*`. **A signed-in member can call nothing on the desk today.** Chat.gamecult.org never holds the `__Host-` cookie, so "the Heimdall session" cannot be sent by the browser to Bifrost from chat.
|
||||
|
||||
So the session is used as PR #37 uses it: the **sign-in leg of an OIDC code+PKCE flow** (`/connect/authorize` resumes on the Heimdall-backed session, FACT `OidcEndpoints.cs:44-70`, which admits any account that is not Suspended and has an identity: membership is neither required nor written, so the sign-in leg does not prove Active. It works for `desk.read` only because the desk re-checks Active on every request) mints a short access token. Reuse, exactly:
|
||||
1. Token: PR #37's second public PKCE client (Ratatoskr) and OpenIddict validation as a non-default scheme (built as `OidcDeskCredential` on OpenIddict's fixed scheme name `OpenIddict.Validation.AspNetCore`; the label `BifrostBearer` in PR #37 section 5 names the role, not the scheme string; handler `IDeskCredential` production impl, the credential port; H4 in the desk cut map, a label used only there). Request scope `desk.read` only, audience `bifrost-desk`. No `desk.answer`, no `votes.*`.
|
||||
1. Token: PR #37's second public PKCE client (Ratatoskr) and OpenIddict validation as a non-default scheme (built as `OidcDeskCredential` on OpenIddict's fixed scheme name `OpenIddict.Validation.AspNetCore`; the label `BifrostBearer` in PR #37 section 5 names the role, not the scheme string; handler `IDeskCredential` production impl, the credential port; H4 in the desk cut map, a label used only there). Request scope `desk.read` only, audience `bifrost-desk`. No `desk.answer`, no `votes.*`.
|
||||
2. Judgment: unchanged `DeskEndpoints.HandleAsync`: audience, scope, then `OidcEndpoints.ActiveMemberIdAsync(db, sub)` re-read every request (non-Active gets 403 `forbidden`).
|
||||
3. CORS: existing `DeskCors` (exactly `Desk:AllowedOrigin`, GET and POST, no credentials).
|
||||
Rejected: a cookie-authenticated same-origin JSON route (works for a Bifrost page, not for the chat origin; a second auth path on the same data), and letting chat present its Matrix/MAS token (Bifrost does not validate it; INFERENCE per PR #37 section 1).
|
||||
|
|
@ -70,10 +70,10 @@ Already ruled: the answer-binding D1 above, visibility ruling c (any `desk.read`
|
|||
|
||||
## 8. Privacy: what a member may see per Mind
|
||||
|
||||
- **Filter owner: Huginn.** The list and read calls carry the viewer's `subject` and that Mind's `deciders` (no `authority`; an answer carries it); Huginn projects "for this viewer". The policy is in Huginn's `docs/desk-feed-door.md` ("Listing", Huginn main 9dde93e) and is not copied here: not listed unless a listing record is in force, deciders see every showable fork, a hidden fork reads as `fork_unknown`. That gate is not live on Huginn main yet (`cut-desk-feed-listing`), so no Mind that holds private questions goes behind a live receiver before it lands. Bifrost holds NO audience rule: no filter on which forks appear, no per-card visibility rule, no copy of the policy (two copies would drift). Bifrost's three new acts are the per-request Active check, the `desk.read` bearer, and answerer masking on what the door returns (below), beside two already built: it nils every `note` for a non-decider (`DeskNotes.Strip`), and it resolves `{mind}` only against the configured list (unknown Mind and unknown fork return the same 404 bytes, so Minds are not probeable).
|
||||
- **Filter owner: Huginn.** The list and read calls carry the viewer's `subject` and that Mind's `deciders` (no `authority`; an answer carries it); Huginn projects "for this viewer". The policy is in Huginn's `docs/desk-feed-door.md` ("Listing", Huginn main 79d954c or later) and is not copied here: not listed unless a listing record is in force, deciders see every showable fork, a hidden fork reads as `fork_unknown`. That gate is not live on Huginn main yet (`cut-desk-feed-listing`), so no Mind that holds private questions goes behind a live receiver before it lands. Bifrost holds NO audience rule: no filter on which forks appear, no per-card visibility rule, no copy of the policy (two copies would drift). Bifrost's two new acts are the per-request Active check and the `desk.read` bearer (below), beside two already built: it nils every `note` for a non-decider (`DeskNotes.Strip`), and it resolves `{mind}` only against the configured list (unknown Mind and unknown fork return the same 404 bytes, so Minds are not probeable).
|
||||
- **Fields safe for a member:** `fork_id`, `title`, `options`, `recommendation`, `depends`, `status`, `order`, `options_hash`, counts, `stamp`, and `raised_at` (string or nil). **Not safe, and not in the feed:** `note` (stripped), the decider list beyond ids already on the card, anything from the mind that is not a fork card (follow-ups, findings, research, persona state, room text).
|
||||
- **Answerer ids (X3, closed by the steward in PR #37 section 14):** `ForkEntry.record.answers[]` carries each answerer's account id (a Bifrost `UserAccount.Id`) to any `desk.read` holder; ruling c permits cards and outcomes, but a member list of who answered what is more than a feed needs. The feed does not need `record` at all. SUPERSEDED by Eureka's ruling `eureka-substrate:ruling:desk-read-visibility-withheld-ids` (Huginn #56, 79d954c): the door, not Bifrost, withholds decider ids and answerer ids from a non-decider and returns counts. Bifrost masks nothing it cannot read (the card is opaque to it) and keeps no answerer mask; it still nils `note` for a non-decider. Until Huginn's cut and the CultLib change land, the door sends ids whole, so no receiver goes live on a Mind before then.
|
||||
- **Content privacy:** a fork's `context` and `title` are Mind-authored prose and may name people or private matter. The door is the only gate: Bifrost requires the list wire to carry only cards the member may see, passes them through and masks answerer ids. The audience policy is Huginn's door spec's, not here. The feed ships no free text Bifrost wrote.
|
||||
- **Content privacy:** a fork's `context` and `title` are Mind-authored prose and may name people or private matter. The door is the only gate: Bifrost requires the list wire to carry only cards the member may see, passes them through; the door withholds answerer and decider ids. The audience policy is Huginn's door spec's, not here. The feed ships no free text Bifrost wrote.
|
||||
- **Consent and receipts:** reading creates no member record, so no receipt and no consent object; the member's act is the token grant (read-only, implicit). Answer receipts and the append-only record belong to the answer path and stay out. Audit: log client id and `sub`, not the response. The training-notice receipt gates the chat token only (Meta's ruling, 2026-11-11): a desk token issues for a member with no receipt.
|
||||
|
||||
## 9. Failure modes
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ Decided by the steward of Bifrost (the Persona), recorded by Heimdall (Self). Sc
|
|||
**Decided with the feed.** The Desk feed never carries answerer account ids (the door withholds them from a non-decider, `eureka-substrate:ruling:desk-read-visibility-withheld-ids`; Bifrost masks nothing it cannot read, and this supersedes the Bifrost mask that closed X3 of `docs/desk-feed-design.md`).
|
||||
|
||||
**Flags to other stewards.**
|
||||
- Eureka: the Huginn door list and read wire and its audience policy are written in Huginn's `docs/desk-feed-door.md` (Huginn main 9dde93e, with golden fixtures); the host path of the D5 marker is still open. The listing gate that implements the audience policy is NOT live on Huginn main (cut `cut-desk-feed-listing`): until it lands, the door returns every showable question to every reader, so no Mind that holds private questions goes behind a live receiver.
|
||||
- Eureka: the Huginn door list and read wire and its audience policy are written in Huginn's `docs/desk-feed-door.md` (Huginn main 79d954c or later, with golden fixtures); the host path of the D5 marker is still open. The listing gate that implements the audience policy is NOT live on Huginn main (cut `cut-desk-feed-listing`): until it lands, the door returns every showable question to every reader, so no Mind that holds private questions goes behind a live receiver.
|
||||
- Libby (CultLib steward), ruled 2026-11-11: ForkCard gains `raised_at`, which stays OUT of `options_hash` (that hash keeps its five members: title, context, options, recommendation, depends, so digests are unchanged) and is a REQUIRED card key, an RFC3339 string or nil; the card goes from 16 to 17 keys and its hex changes. This closes X1 and X2 of `docs/desk-feed-design.md` (X2 withdrawn).
|
||||
|
||||
**Who sees which card: Huginn's door (Eureka), not Bifrost.** The policy is written in Huginn's `docs/desk-feed-door.md` ("Listing"), and this note holds no copy of it: a fork is shown to a reader who is not on the call's `deciders` only while a listing record for it is in force; a decider sees every showable fork; a hidden fork reads as `fork_unknown`, with the same bytes as a fork that does not exist. Bifrost applies no audience rule of its own: it relays exactly what the door returns. Reads carry `subject` and `deciders` (no `authority`; that is only on an answer), so anyone who must see unlisted forks is put on the Mind's decider list in Bifrost's admin. Bifrost adds three acts on the desk path: the per-request Active membership check, the `desk.read` bearer (audience `bifrost-desk`), and nilling notes for a non-decider; they sit beside the two already built (the note strip for non-deciders and the configured-Mind 404). Bifrost does NOT filter by audience, hold any per-card visibility rule, or keep a second copy of the policy (two copies would drift). The card is opaque to Bifrost (its `raised_at` key and order are being re-pinned in CultLib), so it is relayed as the door sent it.
|
||||
|
|
|
|||
Loading…
Reference in a new issue