Repository navigation
Teach the realm-policy-authoring skill anonymous grants and a realm's anonymous traffic controls - #174
Conversation
… anonymous traffic controls Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Staging Workspace Sync SuccessfulSuccessfully synced changes to staging workspace Sync Details |
habdelra
left a comment
There was a problem hiding this comment.
[Claude Code 🤖] This review checked each claim in the new §12 and the edited tables against the code. For the anonymous-access settings that means the realm's current code. For compile, the compile change. For reads, the in-progress reads branch. Writes and search haven't been built, so those claims were judged against the project's design decisions. The review also checked the § cross-references across the skill tree, the frontmatter, author-facing scope (no server logs, telemetry or internal source paths) and evergreen wording.
No blocking issues beyond the merge gate the description already holds. The compile-time claims (eligible operations, the three issue codes and their severities, the actor() rule, filterless query grants opening nothing) and the realm settings (bounds, /64 counting, malformed-blocklist fail-closed, header-only addresses) match the code. The reads behavior matches the in-progress branch. Cross-references and the YAML parse cleanly.
canInvokeguidance gates on admission, which contradicts "hide onfalse, never onundefined" and hides the button in the prerender (see the thread on "Showing a control only when it works").- A visitor gets 401, not 500, when the policy won't compile. Add a row to "What visitors see" and scope the §9 sentence (see that thread).
- When writes land, give the batch row its status and its per-entry cost, including that a batch larger than
requestsnever passes. Also reconcile §4's 404 subdirectoryPOSTwith "every visitor refusal is 401" (see the thread on those rows). - Evergreen nit on "Off by default".
| **Showing a control only when it works.** `@context.canInvoke` answers for a | ||
| visitor too, so a public page can show a submit button only when the visitor's | ||
| submit would be admitted (`card-operations-authoring` §3). A capability check | ||
| invokes nothing and never counts against the limit. |
There was a problem hiding this comment.
[Claude Code 🤖] Please phrase this as "hide the submit button when canInvoke answers false". As written ("show a submit button only when the visitor's submit would be admitted"), it contradicts card-operations-authoring §3, which this sentence cites: hide on false, never on undefined. That matters most on a public page. The prerender always answers undefined, and so does a failed or still-pending check. An author who gates on true serves visitors prerendered HTML with no submit button, and the button stays hidden whenever the check fails. The worked example's last sentence ("asks @context.canInvoke('create', Feedback, { realm }) before showing its submit button") has the same problem and needs the same fix. Keep the explicit { realm } there, since a visitor has no default writable realm.
Regression in this PR's guidance. Non-blocking, but cheap to fix before merge.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 2532a74. §12 now says to hide the submit button when canInvoke answers false, never on undefined, and says why for a public page (a prerender or a pending or failed check answers undefined). The worked example hides its button on false and keeps { realm }, noting that a visitor has no default writable realm.
| | No anonymous grant admits the request, or the card is missing | 401 `actor-required`, the same bytes either way | | ||
| | The visitor's address is blocked, or the blocklist is malformed | 401 `actor-required`, as above | |
There was a problem hiding this comment.
[Claude Code 🤖] Please add a row: the policy won't compile → 401 actor-required for a visitor. The realm reads a policy that won't compile as opening nothing to callers who aren't signed in, so those callers get the ordinary unauthenticated answer. A failure answer would reveal that the realm names a policy. §9 says something different: "a card-level issue makes the whole policy uncompilable, and every caller the realm's permissions decline gets 500 (§13)". An author who breaks the policy behind a public page will see 401s and go looking for a missing grant, not a compile issue. Scope the §9 sentence to signed-in callers. The tip to check the policy's issues (§9) is the useful pointer for this row.
The §9 wording was already loose for anonymous callers, but this PR is what makes visitors a case authors design for. Non-blocking.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 2532a74. "What visitors see" gains a row: a policy that won't compile answers a visitor 401 actor-required, with a pointer to the policy's issues (§9). §9's 500 sentence now covers signed-in callers only, and points visitors to the 401 in §12. §13's "policy won't compile" row also names the 401 for a caller who isn't signed in.
| | An anonymous create is admitted | The realm chooses the new card's id (§4) | | ||
| | A batch has one entry no grant admits, or over the limit | The whole batch is refused, and nothing is written | |
There was a problem hiding this comment.
[Claude Code 🤖] Anonymous writes haven't shipped yet, so these two rows rest on the design decisions alone. When the writes change lands, please pin down two things:
- The batch row needs its status and its cost. "Refused" covers two different answers: 401 when an entry has no grant, and 429 when the batch is over the limit. A visitor's client handles those differently, so the row should give both. The realm's rate limiter also charges a batch once, at a cost equal to its number of entries, and it refuses any charge larger than
requestsoutright. If writes keep that design, a batch with more entries than the realm'srequestscan never succeed, whatever the window. With the feedback example'srequests: 5, a 6-entry batch always gets 429 and aRetry-Afterit can never satisfy. Authors setting a tight limit need to know this. - The create row and §4 conflict. §4 says a card+json
POSTto a subdirectory "answers 404" for a caller who can't read the realm. This section says every refusal a visitor sees is 401, for reads and writes alike. Please state which one a visitor gets.
These depend on the writes PR, so they belong to the merge gate the PR's callout already holds. Blocking only in the sense that callout already is.
There was a problem hiding this comment.
[Claude Code 🤖] (1) Fixed in 2532a74. The batch row is now two rows: 401 actor-required for an entry no grant admits, and 429 rate-limited when the batch would go over the limit. A new paragraph says a batch counts once per entry, so a batch with more entries than requests gets 429 however long the visitor waits, and that requests should be set above the largest batch a page sends. This matches the limiter on main, which refuses any charge larger than requests. (2) Whether a visitor's card+json POST to a subdirectory gets the §4 404 or the 401 depends on the writes change. It's on the merge-gate re-check list, and I'll settle the text against what ships.
There was a problem hiding this comment.
[Claude Code 🤖] (2) settled in f0cf7d2, as the writes change will ship it: a visitor's card+json POST to a subdirectory answers 401 actor-required, not 404. §4 now says so. §12 now says that wherever a caller who can't read the realm would get a 404, a visitor gets the 401. Batch charging is confirmed as written: one charge per batch costing its number of entries, refused outright when that exceeds requests.
|
|
||
| - **Off by default.** A grant without `anonymous: true` never admits a visitor, | ||
| an unconditional one included, so no grant written before this field starts |
There was a problem hiding this comment.
[Claude Code 🤖] "No grant written before this field" ties the rule to when the field was introduced. That stops being useful once it has shipped. Suggest stating the rule as it stands:
| - **Off by default.** A grant without `anonymous: true` never admits a visitor, | |
| an unconditional one included, so no grant written before this field starts | |
| - **Off by default.** A grant without `anonymous: true` never admits a visitor, | |
| an unconditional one included. |
Evergreen nit. Non-blocking.
There was a problem hiding this comment.
[Claude Code 🤖] Fixed in 2532a74. The bullet now states the rule alone: a grant without anonymous: true never admits a visitor, an unconditional one included.
…policy answers, and state the opt-in as it stands Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… in the pointer table and the card shape Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… through one element Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hen the limit can't be counted Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bound a visitor's federated search Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ting as anonymous writes ship them Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s grants Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ctor() Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… issues Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pe, federated 401, public-realm exemptions, batch credit and infra callers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Important
Merge order (each must merge before the next):
Compile a policy grant's opt-in to callers who aren't signed in boxel#6534: compile a grant's anonymous opt-in (merged)Policy: admit anonymous reads through a grant that opts in, rate-limited per realm and address boxel#6568(CS-13536): anonymous reads (merged)Policy: admit anonymous searches through a query grant that opts in boxel#6575(CS-13578): anonymous search (merged)Policy: admit anonymous writes as a user the realm's config names boxel#6577(CS-13579): anonymous writes (merged)4a.
Policy: let a grant on a declared operation admit a caller who isn't signed in boxel#6579(CS-13605): anonymous named operations (merged). This PR's §12 describes it4b.
Policy: explain how a realm treats a caller who isn't signed in boxel#6581(CS-13609): explaining a visitor's refusal (merged, with its panel Show in the explain panel how a realm treats callers who aren't signed in boxel-catalog#809). This PR's §10/§12 describe it4c.
Report a visitor's explain refusal as the 401 they receive, and remove the unused OperationScope.proposed boxel#6589(CS-13348, carrying CS-13612): merged; explain gives a visitor's refusal as the 401 they receiveAdd the judgment calls to the realm-policy-authoring skill #175(CS-13108): merged, and merged into this PR. This section is §12, Refusals §13, its section §14 and the checklist §15.5a.
Say that no policy grant reaches a path the realm ignores #178(CS-13336): merged, and merged into this PR7a. Say that a policy's rules are not secret from the writers of other realms #181 (CS-13398): a realm-policy-authoring §9 bullet, stacked on State every limit of a realm policy in the skills #179, merges right after it
Every PR in this order carries this callout. Whoever opens one of the PRs above tells the owners of the others, and each owner updates its own callout.
In service of CS-13521, which lets a realm's policy admit a caller who isn't signed in: an
OperationGranton a base operation opts in, an anonymous write is made as a user the governed realm names, and the realm limits and blocks such callers by address. This PR teaches card authors that surface through therealm-policy-authoringskill, so a request like "anyone may read the published articles" or "a public feedback form" gets guidance.The new §12 covers:
anonymous: trueopt-in, which operations can carry it (anonymous-not-base-operation), and scoping a grant by what the card holds rather thanactor()(anonymous-grant-reads-actor);actingUser, a key in the governed realm'srealm.jsonconfignaming a user who must hold write on the realm (anonymous-write-without-acting-user), and why the realm names it rather than the policy;RealmConfiganonymousRateLimitandanonymousBlocklistsettings, including what counts against the limit and what a malformed blocklist entry does;actor-requiredfor a refusal or a missing card, 429rate-limitedwithRetry-After, realm-minted ids, all-or-nothing batches), anonymous search, and@context.canInvokefor visitors;The rest of the skill follows: the card shape gains the two grant fields, the issues table gains the three codes, explain's
actor-requiredreason and the refusals table account for anonymous grants and 429, and the checklist gains an anonymous item. "Refusals a caller sees" and the checklist become §13 and §14, and the cross-references incard-operations-authoringand the glossary follow.card-operations-authoring's federated-search table and access posture now mention anonymous grants, and the glossary gains entries for anonymous grants, acting users and the two realm settings.🤖 Generated with Claude Code