docs(permissions): the attachments 401 row covers the posture-driven key refusals - #16254
Merged
baozhoutao merged 1 commit intoSep 6, 2026
Merged
Conversation
…refusals The `AUTH_REQUIRED` row on the attachments download gate listed only the anonymous case. It was already a simplification — an unknown, revoked or expired API key has always produced the same 401 — and the tenancy-posture admission now landed on the four seam doors adds two more credentials that reach it, so the row is extended once for the family rather than per door. The two non-uniformities are stated, not flattened: an org-stamped key whose owner has left is refused under both `group` and `isolated`, while an organization-less key is refused under `isolated` only (`group` uses union read scope, so such a key stays admitted). No outage status is documented — that divergence is tracked separately. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8
This was referenced Sep 6, 2026
baozhoutao
marked this pull request as ready for review
September 6, 2026 08:21
baozhoutao
enabled auto-merge
September 6, 2026 08:21
baozhoutao
deleted the
claude/issue-16018-attachments-access-401-posture
branch
September 6, 2026 09:11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #16018
The attachments download gate's
AUTH_REQUIRED(401) row listed only the anonymous case. Now that the four tenancy-posture doors have landed, the same 401 is also the answer for two more credentials. This extends the row once for the whole family rather than writing the same paragraph on four cards.What changed
One file, one section —
content/docs/permissions/attachments-access.mdx, the "Download — authenticated & parent-scoped" table (theAUTH_REQUIREDrow) plus a follow-on passage under it. The enforcement-summary row further down is untouched: its wording does not contradict the extended row.It is an addition, not a correction
The row was already a simplification before this family — an unknown, revoked or expired API key has always produced the same 401 — so no published claim was false. This is an extension of a simplifying row, not a repair of one.
The two measured non-uniformities are stated, not flattened
Re-verified on the merge base against the door this page actually documents (
service-storage), not assumed:postureEnforcesWall(posture)isposture !== 'single';postureUsesUnionScope(posture)isposture === 'group'(packages/spec/src/security/tenancy-posture.ts:53and:80).organization_membership_endedis gated onpostureEnforcesWallalone (packages/core/src/security/resolve-authz-context.ts:480) so an ex-member's org-stamped key is refused under bothgroupandisolated.organization_requiredadditionally requires!postureUsesUnionScope(packages/core/src/security/api-key.ts:370) so an organization-less key is refused underisolatedonly — undergroupit stays admitted.packages/services/service-storage/src/file-read-tenancy-posture-admission.test.tssection 6 asserts 401 for the ex-member's key and 200 for the organization-less key undergroup.So the page says "refused under both
groupandisolated" for one key kind and "refused underisolatedonly" for the other. It never says "undergroupthese keys are refused".No outage status is documented. The outage answer differs per door and is not the declared 503 anywhere measured; #15999 remains open for that divergence and is not addressed here.
The 401 also stays the generic answer on the wire: both refusals leave
userIdunset, so the response is byte-identical to sending no credential at all and the reason goes to the server log only. The page says that, because it is why the refusal is a 401 rather than a 403.Verification
All 39 gate families derived for this diff were run locally and are green:
node scripts/pm/dispatch-gates.mjs --changed --ran RAN_FILEreports39 derived famil(ies) accounted for — 39 run, 0 NOT-MEASURED.That includes the docs corpus gates (
check:doc-anchors,check:doc-authoring,check:docs-single-h1,check:docs-redirects,check:docs-audit-scope,check:docs-transcript-drift,check:role-word,check:nul-bytes,check-doc-frontmatter,check-doc-route-spelling,check-docs-section-name,check-section-landing-index), the two lint-owned docs gates (check:doc-formula-expressions,check:doc-security-posture) and the spec docs gates (check:docs,check:skill-examples,check:liveness,check:empty-state,check:strictness-ledger,check:variant-docs,check:yaml-examples), each after@objectstack/spec/@objectstack/lint/@objectstack/client-reactwere built under the shared verify lock.pnpm lintwas narrowed to the changed file, and the narrowing is measured rather than asserted:eslint --no-inline-config --format jsonon the changed file returnsFile ignored because no matching configuration was supplied—.mdxis in nofiles:glob ofeslint.config.mjs, whose population is{ts,tsx,mts,cts,js,jsx,mjs,cjs};errorCount: 0;parserOptions.project, no typed rules — stated ineslint.config.mjsand confirmed by grep), so a docs-only diff cannot move the verdict on any untouched file.skip-changeset: the diff publishes from no package (content/docs/**only).Generated by Claude Code