Skip to content

docs(architecture): Layer 7 — STRIDE on the Storage broker north face#219

Merged
Yambr merged 1 commit into
next/v1from
docs/layer7-northface-threats
May 31, 2026
Merged

docs(architecture): Layer 7 — STRIDE on the Storage broker north face#219
Yambr merged 1 commit into
next/v1from
docs/layer7-northface-threats

Conversation

@Yambr

@Yambr Yambr commented May 31, 2026

Copy link
Copy Markdown
Collaborator

What

Extends the Layer 7 threat model (06-threat-model.md) to cover the Storage broker north face that Layer 6 (#215) added — OCU's authenticated SPA + file/artifact API, reached by a new external actor (the data-plane client) over a new boundary flow. The model already covered the broker's south mount (F7) and backend leg (F10); the north face had no STRIDE coverage.

Rows added (§3.2)

Six rows on the north face — actor E5 data-plane client (A2-class), flow F12, all PARTIAL:

ID STRIDE Threat Anchor
P4-S3 S embed-token replay/forgery + clickjack→session-fixation NFR-SEC-82/83
P4-T3 T CSRF on the SameSite=None cookie-bound API + token-in-URL leak NFR-SEC-84/82
P4-I3 I cross-filesystem_id read / preview leak / non-downloadable bytes to browser NFR-SEC-49/73/83
P4-D3 D inbound byte-path + UI-face flood, archive-bomb on ingest NFR-SEC-78/80
P4-R2 R north-face file-activity attribution NFR-SEC-79
P4-E3 E intent/downloadable bypass + artifact-id traversal via the HTTP API NFR-SEC-49/73/80

Scope / register updates

  • §1 → twelve flows, five actors. §2 → E5 actor, F12 flow, P4 north-face sub-element.
  • §5 residual register: extend exhaustion (+SEC-78/80), per-action-authz, downloadable themes; add three north-face theme rows.
  • §7 unchanged (stays at the 5-entry cap).

New tracking issues

Two gaps with no prior issue, surfaced by this pass:

Anchors only NFRs already merged via #213; no new NFR invented. Worded to the merged NFR-SEC-82/84 text (the UI verifies / sets the session; the peer backend mints the token).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation
    • Expanded threat model to cover an additional north‑face data‑plane client and more boundary flows between elements.
    • Added detailed live-threat entries for north‑face storage-broker interactions (token/session replay, CSRF/cookie risks, token leakage, cross-tenant leakage, resource exhaustion, attribution/repudiation gaps, confused‑deputy escalation).
    • Updated residual-risk tracking with new north‑face containment themes and per‑action authorization guidance.

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a2a6929-d08f-4c89-8411-d1d8b7493dcc

📥 Commits

Reviewing files that changed from the base of the PR and between 5f9fe79 and 08ad82e.

📒 Files selected for processing (1)
  • docs/architecture/06-threat-model.md
✅ Files skipped from review due to trivial changes (1)
  • docs/architecture/06-threat-model.md

Walkthrough

This PR revises the Layer 6 threat model documentation to add an E5 data‑plane client and expand the DFD to twelve flows (F1–F12), adds six north‑face STRIDE live‑threat entries for the storage broker, and expands residual‑risk thematic tracking for north‑face concerns.

Changes

North-face data-plane threat model expansion

Layer / File(s) Summary
DFD scope and element inventory
docs/architecture/06-threat-model.md
Updated model to enumerate five external actors (added E5) and twelve boundary flows (F1–F12). Expanded P4 to include both south‑face mount and north‑face SPA/file‑artifact surfaces and adjusted trust‑failure attribution notes.
North-face live threats
docs/architecture/06-threat-model.md
Added six new STRIDE live‑threat rows for F12 data‑plane client→broker: P4‑S3 (embed‑token/session fixation), P4‑T3 (CSRF/state mutation and token leakage), P4‑I3 (cross‑tenant object/preview leakage and cross‑origin framing), P4‑D3 (inbound byte‑path resource exhaustion), P4‑R2 (file‑activity attribution/repudiation gaps), and P4‑E3 (preview→download/write confused‑deputy and downloadable‑state manipulation).
Residual risk tracking update
docs/architecture/06-threat-model.md
Expanded the residual‑risk thematic table: broadened "Resource‑exhaustion containment", expanded "Per‑action authorization", extended "Downloadable axis", and added north‑face themes for file‑activity attribution, embeddable‑UI authentication, and preview‑render parser isolation with updated issue links.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'docs(architecture): Layer 7 — STRIDE on the Storage broker north face' accurately reflects the main change: extending STRIDE threat modeling documentation to cover the Storage broker's north-facing API surface.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/layer7-northface-threats

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

L6 (#215) gave the Storage broker a north face — OCU's authenticated SPA
plus the file/artifact API — reached by a new external actor (the
data-plane client) over a new boundary flow. The threat model covered the
broker's south mount (F7) and backend leg (F10) but not this surface.

Add six STRIDE rows on the north face (P4-S3/T3/I3/D3/R2/E3, flow F12,
actor E5 data-plane client, A2-class):
- S: embed-token replay/forgery + clickjack→session-fixation (SEC-82/83)
- T: CSRF on the SameSite=None cookie-bound API + token-in-URL leak (SEC-84)
- I: cross-filesystem_id read / preview-render leak / non-downloadable bytes
  to browser (SEC-49/73/83)
- D: inbound byte-path + UI-face flood, archive-bomb on ingest (SEC-78/80)
- R: north-face file-activity attribution (SEC-79 fail-closed)
- E: intent/downloadable bypass + artifact-id traversal via the HTTP API
  (SEC-49/73/80)

Update §1 (twelve flows, five actors) and §2 (E5 actor, F12 flow, P4
north-face sub-element) to match. §5 residual register: extend the
exhaustion / per-action-authz / downloadable themes, add three north-face
theme rows (file-activity attribution #181; embeddable-UI auth #217;
preview-render parser isolation #218). Two new gaps with no prior issue
get tracking issues #217 (embed-token replay-binding) and #218
(preview-render parser isolation). §7 unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Yambr Yambr force-pushed the docs/layer7-northface-threats branch from 5f9fe79 to 08ad82e Compare May 31, 2026 17:26
@Yambr Yambr merged commit eb8c1ae into next/v1 May 31, 2026
17 checks passed
@Yambr Yambr deleted the docs/layer7-northface-threats branch May 31, 2026 17:31
Yambr added a commit that referenced this pull request May 31, 2026
The contracts predated the file-surface stack (#213/#214/#215/#216/#219).
Add the Storage broker north face — the data-plane client HTTP API and
embeddable SPA — alongside the existing south-face mount and file-op RPC.

New schema contracts/storage/file-artifact-api.schema.json:
- operation set (upload/listFiles/getManifest/download/downloadArchive/
  previewRender/delete) mapped to the PoC route shape; per-operation bodies
  left tbd, like the south face — no invented wire bodies
- three-axis authz reuse (scope/intent/downloadable, NFR-SEC-49/73)
- embed-token verify contract (peer mints, north verifies, exp ≤120s,
  NFR-SEC-82); first-party cookie + CSRF + CSP envelope (NFR-SEC-83/84)
- inbound byte ceiling (NFR-SEC-78), archive validation (NFR-SEC-80),
  content classification (NFR-SEC-81) as x-ocu-default, not frozen
- OCSF File System Activity event (class_uid 1001, NFR-SEC-79)
- embed-token binding (#217) and preview-render parser isolation (#218)
  carried as tracked tbd items; opaque object id, list pagination,
  resumable upload, and share-by-link are not modelled (not sourced)

08-contracts.md: §1 north-face surface row, §2 OpenAPI-3.1 note, §3 seven
contract-enforced north-face mitigation rows (SEC-78/79/80/81/82/83/84),
§5 the new schema file. Diagram: data-plane client actor + north-face edge.
Stays v1alpha — bodies tbd and open issues mean beta would overclaim
stability; the storage set bumps together when sourced (NFR-IC-04).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Yambr added a commit that referenced this pull request May 31, 2026
…ts (#204)

* docs(architecture): Layer 8 — contract-surface overview (08-contracts.md)

Maps every boundary that carries a wire contract to its format and to
OCU's role (define / conform / relying-party), with the versioning policy
and the Layer 7 mitigations each contract must carry.

The inventory is the ten internal boundaries from Layer 6 §4 plus the
external actors from Layer 4 §4. Five external surfaces are integration
contracts OCU conforms to (MCP authz, SAML/OIDC, PKCS#11/KMIP,
chained-proxy, ICAP), consistent with the Layer 5 context map; the rest
OCU defines. Four formats cover the defined surfaces: MCP JSON-Schema for
the agent edge, OpenAPI 3.1 for inbound REST, Protobuf/gRPC for internal
RPC, AsyncAPI 3.0 over the OCSF Published Language for audit fan-in.

Versioning is additive-only; breaking changes take a major version with a
deprecation header (NFR-IC-04) for OCU-defined surfaces, and the MCP edge
uses date-revision negotiation as its compatibility signal. Executable
schema files, mock servers, and the SkillProvider contract are deferred.

diagrams/08-contracts.mmd overlays the format on each container crossing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(architecture): correct MCP dialect default + split run-on sentence

The MCP §2 bullet wrongly implied the 2025-06-18 revision pins no dialect.
The revision defaults embedded tool schemas to JSON Schema 2020-12 and lets
`$schema` declare an alternative; correct the bullet to state default +
override, and recast the OpenAPI bullet's one-dialect note on that basis.

Split the §4 versioning run-on into shorter sentences for readability.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(architecture): correct exec/mount formats + transport-fence split

The exec/PTY+CDP channel is a single bidirectional WebSocket per session
carrying tagged-JSON control frames and raw binary stdio frames, not a
unary gRPC call — aligned with NFR-IC-03. The file-operation mount is an
HTTP+JSON mount config (filesystem_id, broker-signed lease) over a
FUSE/virtio-fs/9p substrate, not gRPC — aligned with NFR-SEC-25. gRPC is
scoped to the unary internal RPC legs (session set-up, lease pull); §2
names WebSocket as the fifth format.

Add the transport-vs-direction split: the transport substrate
(TCP/UDS/vsock; FUSE/virtio-fs/9p) is a deployment-overlay and
component-spec choice, not a contract (NFR-SEC-26/25); the contract fixes
channel direction — control/exec host-dialled with non-host peers
rejected (NFR-SEC-43), outbound guest-out under egress policy
(NFR-SEC-27).

Fix the audience-authz citation to trust-boundaries §8 (Workload-identity
floor), the canonical home of token-class material, not §3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(architecture): Layer-8 schema drafts + contracts-lint gate

Add the five OCU-defined contract schemas the overview maps to, drafted
against the wire sources and bounded by the NFRs:

- contracts/mcp/2025-06-18/ocu-constraints.schema.json — MCP conform
  profile (pins the revision, layers OCU bounds; defines no MCP types).
- contracts/exec/exec-channel.schema.json — exec/PTY WebSocket envelope;
  CreateProcess, the server/client message union, capability negotiation.
- contracts/storage/mount-config.schema.json — mount config; a guest
  config carries no broker lease by construction.
- contracts/storage/file-ops.schema.json — file-op names; bodies tbd.
- contracts/audit/audit-fanin.asyncapi.yaml — OCSF fan-in over a durable
  bus; compute-metering and saturation payloads tbd (#150).

Unsourced numeric bounds are x-ocu-default annotations the operator
retunes within the NFR-SEC-46/51 floor, not frozen contract values.

Add .github/workflows/contracts-lint.yml: ajv 2020-12 meta-validation,
asyncapi validate, and a provenance guard, triggered on contracts/**.

Update 08-contracts.md: §5 lists the drafted schemas by real path and
keeps openapi/proto, the transparency-log envelope, and mock servers as
not-built; fix the §3 audience citation to trust-boundaries §3, the SOAR
webhook anchor to NFR-COMP-27, and scope the gRPC claim to the unary
session/lease legs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci(contracts): ajv --strict=false so spec-legal vendor keywords pass

ajv strict mode rejected the x-ocu-* vendor extensions and the
$comment-* convention, which JSON Schema 2020-12 permits as unknown
keywords. Validate against the meta-schema without keyword-policing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(contracts): exec frame union, provenance, TBD links — final review pass

A multi-lens adversarial review surfaced one correctness defect and four
cleanups, all confirmed:

- exec-channel: TraceEvent is a valid frame in both directions, so it
  appears in both the server and client message sets. The top-level
  oneOf then rejected every TraceEvent frame (matches two branches).
  Switch to anyOf — frame direction is a transport property, not an
  envelope distinction, so the envelope asserts no mutual exclusivity the
  wire does not have.
- exec-channel: reword the V2-payload open question from "is observed" to
  "is the expected payload" — state the fact, not a derivation.
- contracts-lint: extend the provenance guard to catch `anthropic` and
  `observed`.
- 08-contracts.md: link every open question and not-built artifact to a
  tracking issue (#151, #158, #205-#209); no bare placeholders remain.
- audit AsyncAPI: drop the restated "TTL <=15 min" from a channel summary;
  the NFR-SEC-29 anchor already owns the value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(contracts): CodeRabbit review — enforce tools-only, mount scope XOR, u64 maximum, pin CI tools

- mcp profile: capabilities now additionalProperties:false — rejects every
  non-tools capability (sampling, experimental, …), not just a four-key
  denylist; the documented tools-only surface is now enforced.
- mount-config: a mount is scoped by filesystem_id XOR memory_store_id
  (oneOf), so a memory-backed mount no longer has to invent a filesystem id;
  the RW `mounts` array pins writes:true to mirror readonly_mounts' writes:false.
- exec-channel: memory_limit_bytes drops the 2^64-1 JSON-number maximum (a
  validator rounds it past the IEEE-754 safe-integer range); the u64 domain
  is enforced by the wire type, documented in $comment.
- contracts-lint: pin ajv-cli@5.0.0 and @asyncapi/cli@6.0.0 for deterministic
  runs (keep --spec=draft2020 — ajv-cli@5 defaults to draft-07 and errors
  without it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(architecture): Layer 8 — north-face file/artifact contract surface

The contracts predated the file-surface stack (#213/#214/#215/#216/#219).
Add the Storage broker north face — the data-plane client HTTP API and
embeddable SPA — alongside the existing south-face mount and file-op RPC.

New schema contracts/storage/file-artifact-api.schema.json:
- operation set (upload/listFiles/getManifest/download/downloadArchive/
  previewRender/delete) mapped to the PoC route shape; per-operation bodies
  left tbd, like the south face — no invented wire bodies
- three-axis authz reuse (scope/intent/downloadable, NFR-SEC-49/73)
- embed-token verify contract (peer mints, north verifies, exp ≤120s,
  NFR-SEC-82); first-party cookie + CSRF + CSP envelope (NFR-SEC-83/84)
- inbound byte ceiling (NFR-SEC-78), archive validation (NFR-SEC-80),
  content classification (NFR-SEC-81) as x-ocu-default, not frozen
- OCSF File System Activity event (class_uid 1001, NFR-SEC-79)
- embed-token binding (#217) and preview-render parser isolation (#218)
  carried as tracked tbd items; opaque object id, list pagination,
  resumable upload, and share-by-link are not modelled (not sourced)

08-contracts.md: §1 north-face surface row, §2 OpenAPI-3.1 note, §3 seven
contract-enforced north-face mitigation rows (SEC-78/79/80/81/82/83/84),
§5 the new schema file. Diagram: data-plane client actor + north-face edge.
Stays v1alpha — bodies tbd and open issues mean beta would overclaim
stability; the storage set bumps together when sourced (NFR-IC-04).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(architecture): Layer 8 — fleet-audit fixes across the contract set

A per-contract fleet audit (one auditor per file + cross-contract link
check, each finding adversarially verified with quoted evidence) caught
unsourced concrete values that earlier reviews missed — mostly in the
pre-existing schemas, not the new north-face file.

BLOCKs (invented values with no source / NFR figure):
- file-ops: drop "~4MB / 128M" chunk constants (NFR-SEC-46 states no
  figure) and the "opaque pagination cursor" hint (no source; the north
  face already refuses one); soften the chunk-model assertion to what
  NFR-SEC-46 carries
- mount-config: mark the unsourced `source` field x-ocu-tbd (not frozen)
- exec-channel: drop the concrete algorithm name — accept_zstd/supports_zstd
  → accept_compression/supports_compression as negotiation flags, algorithm
  pinned to a new x-ocu-open-questions entry

FLAGs:
- mount-config: rename ca_cert → ca_cert_pem to match the sourced wire name
  (and its `required` entry)
- audit-fanin: storageBrokerAudit channel names both broker faces (NFR-SEC-79)
- 08-contracts.md: getMetadata → getManifest (the sourced op name)
- unify schema $id authority host to schemas.open-computer-use.dev

NITs: Signal bound cites NFR-SEC-51 (input validation) not SEC-46; MCP
missing-header fallback qualifier made exact; audit envelope maxLength
fields annotated x-ocu-design (NFR-SEC-51-derived, not frozen).

ajv 2020-12 compile green on all five schemas; AsyncAPI valid; provenance
clean. The new file-artifact-api.schema.json passed the audit unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(architecture): Layer 8 — restore sourced storage shapes, cover gaps

A fleet restore/gap pass (grounded in a sanitized field-name brief, never
provenance) recovered storage shapes the earlier audit had wrongly dropped
as "invented" — they are real field/message shapes, the audit agents just
could not see the source.

file-ops (south face, broker RPC):
- promote ReadFileRequest_Range (ranged read offset/length),
  ListDirectoryRecursiveCursor (opaque pagination cursor),
  FileUploadRequest_Params (chunked chunk/numChunks) from prose hints to
  real $defs, wired into the request envelope as op-gated carriers
- add SizeLimits $def: RPC message ceiling (~4 MiB default), read-chunk
  default (128 MiB), broker max-file-size (tbd, server policy), per-mount
  VFS cache ceiling (1 GiB, owner is mount-config) — figures are
  x-ocu-default, not frozen consts
- AuthorizationMetadata noted at request field 4/5
- add getFileMetadata + listFiles as distinct ops; honest tbd hints for
  fileDownload / importFiles / importZip / migrateFilesystem / removeFilesystem

mount-config:
- per-mount cache_duration_s with role-directional defaults
  (uploads 1s, tool_results 3s, transcripts 10s, outputs 3600s)
- MountRole enum + role↔RW binding (uploads/tool_results/transcripts RO,
  outputs RW), host-enforced; crypt modelled off (isolation by scope)

ajv 2020-12 green on all three storage schemas; no dangling $refs;
south/north names stay distinct; provenance clean (field names only).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(architecture): Layer 8 — contracts/ navigator README + §5 count fix

Add contracts/README.md: per-file surface/format/validator table, how to
read a schema, the x-ocu-* annotation conventions (sourced vs design vs
default vs tbd), and the change/versioning pointer. Closes the navigation
gap — 08-contracts.md is the surface map, this is the reader's guide.

Fix §5: six schema files drafted (storage carries three), not five; link
the new README.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(architecture): close inter-layer seams for the file-surface thread

An inter-layer cross-review (vertical L2→L8 seams + horizontal cross-doc
links, every finding adversarially verified) found the file/artifact surface
landed L4→L8 but never reached L3, plus contract/diagram/glossary seams.

L3 trust-boundaries (the BLOCK): the Storage broker had only its south face.
Add the north data-plane face — §2 zone 3 names two faces, the embed-token →
first-party-session crossing, archive-validation + content-classification on
ingest (SEC-78/79/80/81/82/83), and the per-tenant broker rule (SEC-76);
§3 gains a Data-plane client external-actor row; §7.1 notes the non-guest
north path (host-side caller↔broker, NFR-SEC-43 unaffected); the zone-3
secondary-anchor line carries the north-face NFRs.

L8 §3: add the Three-axis authz (SEC-49) and Downloadable-at-read (SEC-73)
mitigation rows — the controlling NFRs of threat rows P4-I3/P4-E3 were
unrepresented.

Audit fan-in: the FileSystemActivity message gains the NFR-SEC-79
required-field overlay (filesystem_id/intent/downloadable) so the mandated
field set is pinned on both faces, not deferred to the bare OCSF class; the
message summary names both faces.

L7 diagram: add the data-plane client (north face / F12) and the P4 north-face
STRIDE cluster + #217/#218. L7 §1: the data-store, IdP/SOAR, and F-number
elements are named STRIDE elements, not nodes on c4-container.mmd — wording
corrected to not over-claim the diagram.

L6 §3: add NFR-SEC-83 to the broker NFR-anchor column (prose already cited it).

glossary: add Data-plane client, South/north face, Downloadable, Embed token;
extend Storage broker with the two-face model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(architecture): Layer 8 — reconcile mount-config drift vs the real surface

A drift-reconcile pass (research the underfilled provisioning fields, adjudicate
the design-added ones, adversarially verified) cleaned the mount-config against
the real guest-mount surface.

INCLUDE — the one genuinely-missing in-scope field:
- backend_cache_ttl: a config-level broker-side cache window, distinct from the
  per-mount cache_duration_s; added to ProvisionMountConfig only (broker-side,
  not the guest variant), x-ocu-tbd pending unit/scope confirmation.

DROP — design-adds that are derivable-inside or redundant:
- source: not on the wire; the broker derives the backend source path from
  filesystem_id + the mount's directional intent. Least-data (NFR-SEC-25) —
  a derivable value the guest cannot be trusted to set is not a wire field.
  Documented in the MountBase $comment.
- (no schema change for fuse_mounts / readonly_dev_start_index: the RO/RW split
  they encode is already carried, more cleanly, by mounts[]/readonly_mounts[] +
  the writes boolean.)

REFRAME — provenance honesty:
- role / MountRole: the role NAMES are the directional-window vocabulary, but
  role-as-a-stored-field is an OCU design construct, not a sourced provisioning
  field. Comments corrected (were "Sourced role set").

OUT-OF-SCOPE (not added; tracked separately): resolv_conf / etc_hosts belong to
the guest network/init surface (Compute plane), not the file-mount contract;
mount_model_tools / mount_rclone_tools belong to the SkillProvider surface (v1
non-goal). No §5 not-built line added without a real tracking issue.

ajv 2020-12 green; provenance clean; field names only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(architecture): Layer 8 — drop the MountRole field (derivable, not sourced)

role / MountRole was an OCU design construct, not a provisioning-struct field:
the real surface carries RW/RO structurally (the readonly_mounts split) and the
freshness window as cache_duration_s itself, with no role field. role only
duplicated values already carried by writes + cache_duration_s, bound by two
if/then constraints — redundant and unsourced.

Drop the role property, its $def, and the role→writes allOf blocks; remove it
from required. RW/RO stays in writes + the mounts/readonly_mounts split; the
directional window stays in cache_duration_s. The role names survive as the
cache-window vocabulary. Same least-data reasoning that dropped `source`
(NFR-SEC-25): a value the broker derives is not a wire field.

ajv 2020-12 green; provenance clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(architecture): Layer 8 — fix RW-enforcement symmetry + role-comment wording

Final review (post role/source drop) flagged two consequences of dropping role:

- ProvisionMountConfig.mounts items were a bare MountBase $ref with no
  writes:const:true, so a provisioning RW-array entry could carry writes:false
  — contradicting the structural-split invariant the prose asserts. Mirror the
  GuestMountConfig.mounts constraint: writes:const:true on the provisioning RW
  array too. Now both variants pin writes:true on mounts[] and false on
  readonly_mounts[].
- $comment-no-role narrated drafting history ("an earlier draft carried ... it
  was dropped") — a process-narration tell. Reworded to a present-tense design
  record that keeps the rejection rationale without the history clause.

ajv 2020-12 green; provenance clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(architecture): Layer 8 — require x_deny_reason on a deny outcome

CodeRabbit: the FileActivityEvent outcome description says "a deny carries the
structured deny-reason" but the schema only required disposition_id, so a deny
with no reason validated — text and schema disagreed. Add an if/then: when
disposition_id is "deny", x_deny_reason is required. allow outcomes unchanged.

ajv 2020-12 green; verified deny-without-reason now rejected, deny-with-reason
and allow accepted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant