You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(security)!: enforce book audience at the REST read layer; finish the ADR-0090 D2/D3 cleanup the P1 wave missed (#2774)
* feat(security)!: enforce book audience at the REST read layer; finish the ADR-0090 D2/D3 cleanup the P1 wave missed
Follow-through on the { permissionSet } book-audience rename (#2732), in
ADR-0049 discipline — the gated arm existed but nothing enforced it:
- rest: /meta/book, /meta/doc, and /meta/book/:name/tree now enforce the
ADR-0046 §6.7 audience model. Anonymous callers see only public
books/docs; { permissionSet }-gated books require holding the named set;
a doc's effective audience is the union over the books that CLAIM it
(unclaimed → org; orphan rendering never inherits public). Fails CLOSED
when holdings cannot be resolved. doc/book item reads bypass the shared
meta cache (per-caller gate vs shared ETag). Nine new route tests.
- spec: pure helpers powering the gate (audienceAllows,
resolveDocAudiences, docAudienceAllows, resolveBookClaimedDocs) with
unit tests; the REST layer and any future portal share ONE semantics.
- plugin-security: security service exposes resolvePermissionSetNames —
the same resolution as data-plane enforcement.
- D2/D3 leftovers: METADATA_FORM_REGISTRY role→position (the position
type had LOST its form layout in the P1 rename) and profile removed;
artifact ingestion maps positions→'position' (stale roles→'role'
matched nothing and silently dropped compiled positions);
EnvironmentArtifactMetadataSchema declares positions; metadata-form
translations regain position and drop role/profile in all four locales
(+ vocabulary regression test); position.test/audit.zod/security-plugin
identifiers and comments de-role'd; eslint + doc-authoring domain lists
Role→Position; content/docs/ui/role-based-interfaces.mdx renamed to
audience-based-interfaces.mdx with stale permission-vocabulary copy
fixed across five docs.
- lint: books join the D3 role-word scan; new advisory rule
security-book-audience-unknown-set flags a gated audience naming a set
the stack does not declare (runtime fails closed — surface the typo at
author time).
- scripts/check-role-word.mjs: ADR-0090 D3 vocabulary RATCHET over
content/docs + skills (baseline freezes the 45 current files; new
occurrences fail CI; improvements ratchet the baseline down). Wired
into the lint workflow.
Verified: spec/objectql(808)/cli(464)/rest(227+9)/metadata(260)/
platform-objects(76)/plugin-security/lint(32) suites green; eslint,
check:role-word, check:doc-authoring, check:api-surface (regenerated),
check:liveness, check:skill-docs, check-changeset-fixed all pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ph4jEAfWDh6taMbZweWhom
* test(lint): de-flake lazy-deps in-process gate test — explicit 30s timeout
The in-process variant cold-loads sucrase + typescript and took 5.6s on a
loaded CI runner, tripping vitest's default 5s per-test timeout (Test Core
failure on this PR's first run). The test asserts a loading CONTRACT, not
latency — give it an explicit generous timeout like the sibling dist-based
variants effectively have via their spawn overhead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ph4jEAfWDh6taMbZweWhom
---------
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: content/docs/automation/approvals.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,7 +15,7 @@ An approval is a **flow** with an **approval node**. Two access decisions matter
15
15
16
16
### 1. Who can configure it
17
17
18
-
Authoring flows/automations is a builder capability — it needs `manage_metadata` (typically Studio users). End users **submit** records and **act on** approval requests, but they do not edit the automation. Keep the automation surfaces out of consumer apps (see [role-based interfaces](/docs/ui/role-based-interfaces)).
18
+
Authoring flows/automations is a builder capability — it needs `manage_metadata` (typically Studio users). End users **submit** records and **act on** approval requests, but they do not edit the automation. Keep the automation surfaces out of consumer apps (see [audience-based interfaces](/docs/ui/audience-based-interfaces)).
Copy file name to clipboardExpand all lines: content/docs/permissions/access-recipes.mdx
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,7 @@ description: Map a concrete access requirement onto the platform's layers — ob
11
11
12
12
## Recommended solution
13
13
14
-
Authorization splits into three decoupled concerns (ADR-0066): **capability** (what can be done), **assignment** (who holds it — permission sets / roles, maintained at runtime), and **requirement** (what a resource declares it needs). A resource declares *what is required*; it never bakes in *who*.
14
+
Authorization splits into three decoupled concerns (ADR-0066): **capability** (what can be done), **assignment** (who holds it — permission sets / positions, maintained at runtime), and **requirement** (what a resource declares it needs). A resource declares *what is required*; it never bakes in *who*.
15
15
16
16
Pick the layer that matches the requirement:
17
17
@@ -45,7 +45,7 @@ definePermissionSet({
45
45
46
46
Two separate questions:
47
47
48
-
1.**Who can configure it?** Editing flows/automations needs `manage_metadata` (typically Studio users). Don't expose automation config to end users — see [role-based interfaces](/docs/ui/role-based-interfaces).
48
+
1.**Who can configure it?** Editing flows/automations needs `manage_metadata` (typically Studio users). Don't expose automation config to end users — see [audience-based interfaces](/docs/ui/audience-based-interfaces).
49
49
2.**As whom does it run?** A flow's `runAs` (ADR-0049):
50
50
-`runAs: 'user'` (default) — runs as the triggering user; CRUD nodes respect that user's RLS. **Safer default.**
51
51
-`runAs: 'system'` — elevated, bypasses RLS. Make elevation *explicit*, and surface it in the UI as "runs as system".
Copy file name to clipboardExpand all lines: content/docs/ui/field-grouping-and-order.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -64,7 +64,7 @@ Conflating these two is the single most common source of exactly this confusion.
64
64
65
65
## Why
66
66
67
-
Keeping grouping off the field (beyond an optional semantic hint) is what lets **one model project to many surfaces**: a create form, a full edit form, a mobile form, a public intake form, role-specific layouts, a flat table, and an API — each arranging the *same* fields differently. Weld a single layout onto the field and you get exactly one arrangement everywhere, which every surface must then share. The semantic `field.group` is the deliberate middle ground: a default that travels, without dictating layout.
67
+
Keeping grouping off the field (beyond an optional semantic hint) is what lets **one model project to many surfaces**: a create form, a full edit form, a mobile form, a public intake form, audience-specific layouts, a flat table, and an API — each arranging the *same* fields differently. Weld a single layout onto the field and you get exactly one arrangement everywhere, which every surface must then share. The semantic `field.group` is the deliberate middle ground: a default that travels, without dictating layout.
0 commit comments