From d23cca451007771b1def4adfdc216497b70df986 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 6 Sep 2026 07:23:00 +0000 Subject: [PATCH] docs(permissions): attachments 401 row covers the posture-driven key refusals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8 --- .../docs/permissions/attachments-access.mdx | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/content/docs/permissions/attachments-access.mdx b/content/docs/permissions/attachments-access.mdx index d4bc791ca4..aeb08efc52 100644 --- a/content/docs/permissions/attachments-access.mdx +++ b/content/docs/permissions/attachments-access.mdx @@ -86,9 +86,27 @@ record, and issue a **short-lived signed URL**: | Code | Status | When | | --- | --- | --- | -| `AUTH_REQUIRED` | 401 | Anonymous download of an attachments-scope file | +| `AUTH_REQUIRED` | 401 | Anonymous download of an attachments-scope file, or a credential the organization wall refuses (see below) | | `ATTACHMENT_DOWNLOAD_DENIED` | 403 | The caller is neither the file's owner nor able to read any record it is attached to | +**The 401 is the generic "unauthenticated" answer, and it has always covered +more than a missing credential** — an unknown, revoked or expired API key reads +the same way. Under a wall-enforcing +[tenancy posture](/docs/deployment/tenancy-modes) two further credentials join +it, refused before this door's ownership and parent-read checks are consulted: + +- an API key **stamped with an organization its owner has left** — refused + under both `group` and `isolated`, the two postures that enforce a wall; +- an API key carrying **no organization at all** — refused under `isolated` + only. `group` reads across the caller's whole membership set, so a key with + no organization is still admitted there. + +Under `single` neither applies — there is no organization wall for a key to be +walled out of. Both refusals deliberately tell the caller nothing more than the +anonymous case does: the response is byte-identical to sending no credential at +all, and the reason is written to the server log instead. The check itself lives +in the shared API-key admission path, not in the attachments gate. + The gate is scoped to attachments files on purpose: **non-attachments files** (avatars, `Field.image` thumbnails, org logos) keep their stable, anonymous capability URL, because they are embedded in `` which cannot carry a