Repository navigation
Show in the explain panel how a realm treats callers who aren't signed in - #809
Conversation
…d in, who each grant opened to them writes as, and each grant's own issues Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…an-anonymous' into cs-13609-policy-explain-what-a-realm-does-with-a-caller-who-isnt
…an-anonymous' into cs-13609-policy-explain-what-a-realm-does-with-a-caller-who-isnt
habdelra
left a comment
There was a problem hiding this comment.
[Claude Code 🤖] Independent review of this PR
I compared the local widening against boxel's ExplainedAnonymousAccess, ExplainedGrant.anonymous and ExplainedGrant.issues on the paired branch, and it matches exactly: limit {requests, windowSeconds}, limitFrom 'realm'|'platform', invalidBlocklistEntries: string[], actingUserKey?/actingUser?/actingUserFailure?: 'key-missing'|'not-a-matrix-id'|'no-write', and issues?: PolicyIssue[] (the same type DraftIssue resolves to). The {{#let (this.grantDetail grant)}} cast, MessageText and the Pill @tag='span' warning colors follow patterns already in the file. The new .grant-note and .grant-issue rows take flex-basis: 100%, so they wrap below the existing .matched-grant flex row (which already has flex-wrap: wrap) and leave it intact. Comments carry no ticket IDs or history. I found no blockers. The inline comments cover the Blocklist row's warning styling, wording, and a pluralization nit.
Not tied to a line:
- minor, description: the description says "the paired boxel PR pins this PR's head" but doesn't name that PR, and has no
Merges before: cardstack/boxel#Nline under the callout. #808 has its own. The boxel branch exists and pins this head, but no PR is open for it yet. Once one is open, name it in the callout and add the pairing line, because the pairing checks in both repositories read that line. - minor, test coverage on the paired boxel branch: I traced the host test
an explanation for someone who is not signed in shows the limit, and who each grant opened to them writes asagainst this code. Its exact strings match what the panel renders:7 requests per 30 seconds from one address, set by this realm, plus bothdata-test-explanation-grant-anonymouslines forsubmitter→user andmissing→key-missing. Nothing exercisesdata-test-explanation-anonymous-blocklist,data-test-explanation-grant-issue, thethe platform defaultbranch, or thenot-a-matrix-id/no-writemessages. The Blocklist row matters most of these because it's the one that warns a realm is closed. Please add an assertion for it, and for one grant-issue row. - nit, follow-up: after the paired boxel PR merges and the platform types carry these fields, the
AnonymousAccessDetail/GrantAnonymousDetail/ExplainedGrantDetailwidening and thegrantDetailidentity cast are no longer needed. If they're meant to be removed later, that needs a ticket linked from the description, per the deferred-follow-up rule.
| <dd data-test-explanation-anonymous-limit>{{this.anonymousLimit}}</dd> | ||
| {{#if this.anonymousAccess.invalidBlocklistEntries.length}} | ||
| <dt>Blocklist</dt> | ||
| <dd class='warning' data-test-explanation-anonymous-blocklist> |
There was a problem hiding this comment.
[Claude Code 🤖] minor: this row is meant to read as a warning, but .facts .warning only sets color: var(--warning-foreground, …). --warning-foreground is the text color for content sitting on a --warning fill (that's how the grant-issue Pill below uses it), so on the card's own background it renders as ordinary body text in a light theme and can drop to low contrast in a theme whose warning foreground is dark. Nothing visual or semantic marks the row as a warning, and this case is the one that closes the realm to every caller who isn't signed in. Suggested fix: lead the dd with the same warning Pill the grant issues use (@pillBackgroundColor='var(--warning, var(--boxel-warning))' …, text warning) and drop the .facts .warning color rule, so the cue is the same everywhere in the panel and isn't carried by color alone.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 783dc306. The Blocklist row leads with the same warning Pill the grant issues use, so the cue matches the rest of the panel and isn't carried by color alone.
| align-items: baseline; | ||
| gap: var(--boxel-sp-xs); | ||
| } | ||
| .facts .warning { |
There was a problem hiding this comment.
[Claude Code 🤖] minor: see the Blocklist row. --warning-foreground is a foreground-on-warning-fill token, not a warning text color, so this rule doesn't do what the class name says. Remove it in favor of a warning Pill on the row, or, if a text color is wanted, use a token meant for text on the page background.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 783dc306. The .facts .warning rule is gone; the row's Pill marks it.
| margin: 0; | ||
| font-size: var(--boxel-font-size-xs); | ||
| font-weight: normal; | ||
| color: var(--muted-foreground, var(--boxel-450)); |
There was a problem hiding this comment.
[Claude Code 🤖] nit: .grant-issue shares the muted, extra-small styling with .grant-note, so a compile warning's message renders dimmer than the grant's own outcome label next to it. DraftIssues and the edit-view grant warnings show issue messages in the normal foreground. Suggest keeping color: var(--muted-foreground, …) on .grant-note only.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 783dc306. Only .grant-note is muted. An issue's message renders in the normal foreground, as DraftIssues does.
| return ''; | ||
| } | ||
| let { requests, windowSeconds } = access.limit; | ||
| return `${requests} requests per ${windowSeconds} seconds from one address, ${ |
There was a problem hiding this comment.
[Claude Code 🤖] nit: no pluralization, so a limit of one gives "1 requests per 1 seconds from one address". anonymousRateLimit accepts any positive integer, so a realm that sets 1 request or a 1-second window will show this. Suggest choosing request/requests and second/seconds from the count.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 783dc306. counted() picks request/requests and second/seconds from the count.
| if (anonymous.actingUserFailure) { | ||
| return `${opened}, but ${ACTING_USER_FAILURES[anonymous.actingUserFailure](key)}`; | ||
| } | ||
| return `${opened}, writing as ${anonymous.actingUser} (the realm's "${key}" setting).`; |
There was a problem hiding this comment.
[Claude Code 🤖] minor: two wording points on the acting-user line. (1) The grant detail is attached for every actor (detailer in boxel's explain.ts adds anonymous whether or not the caller is signed in), so a signed-in teacher's explanation also reads "writing as @x (the realm's "submitter" setting)", which suggests the teacher's own writes go out as that user. Something like "Open to people who aren't signed in; their writes are made as @x (this realm's "submitter" setting)." says whose writes. (2) If actingUserKey is present with neither actingUser nor actingUserFailure, this renders "writing as undefined". boxel never sends that shape today, but the panel is written to tolerate platforms that differ, so guarding anonymous.actingUser costs nothing.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 783dc306. The line now says whose writes it means: "Open to people who aren't signed in; their writes are made as @x (this realm's "submitter" setting)." A key with neither a user nor a failure reads "…made as whoever this realm's "key" setting names" instead of rendering undefined.
| (key: string) => string | ||
| > = { | ||
| 'key-missing': (key) => | ||
| `this realm's settings have no "${key}", so it admits none of them.`, |
There was a problem hiding this comment.
[Claude Code 🤖] nit: "so it admits none of them": "it" reads as the realm (the subject of the clause is "this realm's settings"), but it's the grant that admits no one. The realm may still admit anonymous callers through other grants. Suggest "so this grant admits none of them" in all three messages (here, 762, 764).
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 783dc306. All three messages say "so this grant admits none of them".
Staging Submissions PreviewThis PR's content is pushed to the staging submissions realm: https://realms-staging.stack.cards/submissions/ Changed folders:
Updated at 2026-10-06 21:21:43 UTC for commit |
…ant makes, and count limits in the singular Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
[Claude Code 🤖] On the review's line-less findings:
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…line as prettier wants it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Important
Merge order: #808, then cardstack/boxel#6579, then this PR, then cardstack/boxel#6581.
This is stacked on #808 and changes
realm-policy/realm-policy.gts, a file in boxel's catalog test subset. Retarget it tomainonce #808 merges. It merges after boxel#6579, so that #6579's pin, #808's head, still matches catalogmainwhen #6579 merges. The paired boxel PR pins this PR's head.Merges before: cardstack/boxel#6581
In service of CS-13521, which lets a realm's policy admit a caller who isn't signed in. Boxel's explain now reports, for such a caller, how the realm limits and blocks them, who each grant opened to them writes as, and what compiling the policy recorded against each grant. This PR shows that in the explain panel:
The panel reads these fields where an explanation carries them, through a local widening of the explanation type, so it answers the same on a platform that doesn't report them. That is what lets it merge ahead of the boxel PR that adds them.
🤖 Generated with Claude Code