From 3bf9e8a17bb239a8c8c058fd41c91e8202945269 Mon Sep 17 00:00:00 2001 From: Hassan Abdel-Rahman Date: Tue, 6 Oct 2026 15:35:12 -0400 Subject: [PATCH 1/3] Say that a grant on a declared operation may admit a caller who isn't signed in Co-Authored-By: Claude Opus 5.5 --- realm-policy/realm-policy.gts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/realm-policy/realm-policy.gts b/realm-policy/realm-policy.gts index d3df35beb..1f0b71e2b 100644 --- a/realm-policy/realm-policy.gts +++ b/realm-policy/realm-policy.gts @@ -62,13 +62,14 @@ export class OperationGrant extends FieldDef { @field where = contains(PolicyPredicateField); // Whether the grant also admits a caller who isn't signed in. Off, it // admits only signed-in callers, so a grant written before anonymous - // callers could reach a policy never starts admitting them. Only a grant on - // a base operation under its own name (`read`, `update`, …) may set it, not - // one on a custom operation or a named query. The realm doesn't evaluate a - // `where` that reads `actor()` for such a caller, so a grant whose `where` - // does never admits one: an anonymous grant is scoped by what the target - // holds. How hard anonymous callers may use the realm, and which addresses - // it refuses, are the governed realm's own settings, not the policy's. + // callers could reach a policy never starts admitting them. A grant on a + // base operation (`read`, `update`, …) may set it, and so may one on an + // operation a card declares on one of them, but not one on a named query. + // Such a caller has no actor, so a grant whose `where` reads `actor()`, or + // whose operation does, never admits one: an anonymous grant is scoped by + // what the target holds. How hard anonymous callers may use the realm, and + // which addresses it refuses, are the governed realm's own settings, not + // the policy's. @field anonymous = contains(BooleanField); // For an anonymous grant on a write: the key, exactly as written, in the // governed realm's `realm.json` settings, whose value is the user the write From ee54a06ee4090c5c09a17de8d6b0e7f49cc7fa5f Mon Sep 17 00:00:00 2001 From: Hassan Abdel-Rahman Date: Tue, 6 Oct 2026 15:57:10 -0400 Subject: [PATCH 2/3] Name the base operations a grant can't open to callers who aren't signed in, and what each refused opt-in does Co-Authored-By: Claude Opus 5.5 --- realm-policy/realm-policy.gts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/realm-policy/realm-policy.gts b/realm-policy/realm-policy.gts index 1f0b71e2b..8ea7036ea 100644 --- a/realm-policy/realm-policy.gts +++ b/realm-policy/realm-policy.gts @@ -63,13 +63,15 @@ export class OperationGrant extends FieldDef { // Whether the grant also admits a caller who isn't signed in. Off, it // admits only signed-in callers, so a grant written before anonymous // callers could reach a policy never starts admitting them. A grant on a - // base operation (`read`, `update`, …) may set it, and so may one on an - // operation a card declares on one of them, but not one on a named query. - // Such a caller has no actor, so a grant whose `where` reads `actor()`, or - // whose operation does, never admits one: an anonymous grant is scoped by - // what the target holds. How hard anonymous callers may use the realm, and - // which addresses it refuses, are the governed realm's own settings, not - // the policy's. + // base operation other than `explain` or `validate` may set it, and so may + // one on an operation a card declares on such a base. A grant on a named + // query may not: setting it there leaves the whole grant out, for + // signed-in callers too. Such a caller has no actor, so a grant whose + // `where` reads `actor()`, or whose operation does, never admits one, + // though it still applies to signed-in callers: an anonymous grant is + // scoped by what the target holds. How hard anonymous callers may use the + // realm, and which addresses it refuses, are the governed realm's own + // settings, not the policy's. @field anonymous = contains(BooleanField); // For an anonymous grant on a write: the key, exactly as written, in the // governed realm's `realm.json` settings, whose value is the user the write From e143743806edaafef4959ec6336ea0d2f55c988f Mon Sep 17 00:00:00 2001 From: Hassan Abdel-Rahman Date: Tue, 6 Oct 2026 16:11:11 -0400 Subject: [PATCH 3/3] Say in the explain panel that an empty person field asks about someone who isn't signed in, and explain a refusal for an operation that reads the caller Co-Authored-By: Claude Opus 5.5 --- realm-policy/realm-policy.gts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/realm-policy/realm-policy.gts b/realm-policy/realm-policy.gts index 8ea7036ea..7c7a6d45b 100644 --- a/realm-policy/realm-policy.gts +++ b/realm-policy/realm-policy.gts @@ -204,8 +204,9 @@ export class PolicyRule extends FieldDef { } // What each reason an explanation gives means, in the words a policy author -// reads it in. -const REASONS: Record = { +// reads it in. `reads-actor` is named here as well as through the explanation +// type, so the map covers it whichever platform version this realm runs on. +const REASONS: Record = { acl: "The realm's own permissions already allow this, so the policy isn't needed.", granted: 'A grant in this policy allows it.', 'no-grant': "No rule for this card's type grants this operation.", @@ -223,6 +224,8 @@ const REASONS: Record = { 'not-resolved': "This card doesn't have this operation.", 'actor-required': "Someone who isn't signed in is turned away before the policy is checked.", + 'reads-actor': + "This operation depends on who is asking, and someone who isn't signed in can't be identified, so a grant that opens it to them doesn't apply.", 'policy-unloadable': "The realm's policy couldn't be loaded.", }; @@ -1215,6 +1218,9 @@ class ExplainPanel extends GlimmerComponent { data-test-explain-actor /> +

+ Leave this empty to check what someone who isn't signed in can do. +

{{#if this.isCard}}