Skip to content

docs(future-arch): add 04b-credential-broker.md#233

Closed
Yambr wants to merge 54 commits into
mainfrom
next/v1
Closed

docs(future-arch): add 04b-credential-broker.md#233
Yambr wants to merge 54 commits into
mainfrom
next/v1

Conversation

@Yambr

@Yambr Yambr commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Description retired during the initial-public-release history consolidation. The canonical content lives in docs/architecture/ at the current tip.

Yambr and others added 30 commits May 24, 2026 01:10
Distill the credential-broker design from sandboxd/vm-anthropic source
material into a first-class architecture doc. Covers:

- Two-planes-of-identity argument: network-bound egress identity vs
  scoped-JWT resource authorization (sandbox-security-teardown.md §2.6).
- Broker contract: JWT validation, upstream signing (SigV4 / x-api-key),
  filestore CRUD semantics, TLS origination.
- Deployment topology matrix per L2 runtime tier (Docker variants,
  Firecracker/CH with vsock channel).
- Vsock-shim pattern: preserve localhost ergonomics on microVM tiers
  without baking vsock awareness into callers.
- Multi-tenancy posture: shared broker vs broker-per-VM vs per-VM with
  delegated STS; vsock CID as unforgeable tenant attribution.
- filesystem_id as a first-class secret-scope dimension.
- Open questions tagged for Phase 4 lockdown (issuer of scoped JWT
  identified as the gating decision).

Source: sandboxd/vm-anthropic/credential-broker-spec.md and
sandbox-security-docs.zip (sandbox-security-teardown.md,
vm-internal-channels.md). Fills Tier-1 gap #1 from the plan at
~/.claude/plans/users-nick-open-computer-use-sandboxd-v-eventual-allen.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace LICENSE with Functional Source License v1.1 + Apache 2.0 Future
License (FSL-1.1-Apache-2.0). Each release automatically converts to
Apache-2.0 two years after publication under the Grant of Future License
clause. Use, modification, forking, internal self-hosting, and
redistribution remain permitted; offering a hosted or embedded competing
service requires a separate commercial agreement.

Past releases keep their BUSL-1.1 terms via the LICENSE file frozen at
each tag — git history is unchanged. CHANGELOG records the transition;
the migration commit is the boundary between BUSL and FSL releases.

Scope:
- LICENSE — replaced with FSL-1.1-Apache-2.0 text.
- NOTICE — updated multi-license model description; GitHub URL aligned
  to Wide-Moat org.
- README.md — license badge + License section updated.
- CLAUDE.md — License Headers section now requires
  SPDX-License-Identifier: FSL-1.1-Apache-2.0 for new core files.
- CONTRIBUTING.md — contributor license terms updated; removed obsolete
  BSL Change-Date release-process section, replaced with FSL Apache
  conversion note (no per-release LICENSE edit needed under FSL).
- SPDX find-replace across 176 source files (Python / shell / YAML /
  TOML / TypeScript / JavaScript / JSON / HTML / Dockerfile / Helm
  templates / Markdown). 186 files now carry the FSL SPDX tag.
- package.json — license field.
- THIRD-PARTY-LICENSES.md — own-source-code license reference.
- helm/computer-use-server/{README,templates/NOTES.txt} — license
  reference.
- computer-use-server/cli-defaults/{opencode.json, codex.json,
  README.md} — _spdx JSON field.
- tests/test-docker-image.sh — assertion updated.
- tests/test_codex_toml_converter.py — fixture values updated.
- CHANGELOG.md — new Unreleased entry documenting the migration.

Skills under skills/public/{describe-image,sub-agent}/ retain MIT.
Third-party skills retain their own LICENSE.txt. Loki/Tempo/Grafana (and
any future AGPL-3.0 deps run as separate services with stable APIs)
remain compatible with FSL-1.1-Apache-2.0 distribution — documented in
the forthcoming docs/architecture/manifesto/05-licensing-posture.md.

Verification:
- grep "BUSL-1.1" outside CHANGELOG / docs/future-architecture / .venv
  / .planning / node_modules returns no source-file hits.
- tests/test-project-structure.sh: 24/24 PASS.

Part of next/v1 Layer 0 scaffolding (PR #1 of 4). Follow-ups:
docs/architecture/ scaffolding + CLAUDE.md sections (PR #2),
documentation linters (PR #3), CI security/test gates + CODEOWNERS
(PR #4).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Catch up with main: pulls in observed execution/egress/secrets axes +
DN-1 design note (PR #130). No conflicts expected — both branches only
touch docs/future-architecture/ in non-overlapping files.

next/v1 continues as the long-lived architecture branch; main is not
modified by this merge.
Without this file CodeRabbit auto-reviews only PRs targeting the repo's
default branch (main) and skips PRs against next/v1 with the message:
"Auto reviews are disabled on base/target branches other than the
default branch".

next/v1 is the long-lived enterprise-architecture branch; we will land
many sub-PRs against it over months. Manual @coderabbitai review per
PR is impractical.

reviews.auto_review.base_branches now lists both main and next/v1.
Profile set to chill (less noise on the architecture work). Drafts not
auto-reviewed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chore(license): migrate BUSL-1.1 → FSL-1.1-Apache-2.0
chore(ci): enable CodeRabbit auto-review on next/v1 PRs
…sections (#134)

* docs(architecture): scaffold next/v1 architecture tree + CLAUDE.md sections

Layer 0 step 2: stub-only scaffolding for docs/architecture/ and seven
new sections in CLAUDE.md governing all architecture work on next/v1.

Pacing rule (locked): no bulk-generation. Each future architecture
artifact appears one PR at a time after discussion. status:stub /
status:tbd are first-class states. See docs/architecture/PROCESS.md.

Created stubs only — no real content, just front-matter + 1-line
purpose. The tree grows per PROCESS.md.

- README.md (navigator: what's here, what's NOT here yet, reading order)
- MANIFESTO.md (stub — planned sections listed, no content yet)
- glossary.md (stub — empty terms list)
- PROCESS.md (draft — 3-step playbooks: component / ADR / NFR /
  dependency / TBD)
- adr/README.md + adr/0000-template.md (Nygard format, status flow,
  hard cap 200 lines, when-to-write-an-ADR test)
- components/0000-template.md (fixed section order, hard cap 600 lines)
- manifesto/, compliance/, diagrams/ as .gitkeep-only empty dirs

Every file carries the mandatory front-matter (status, last-reviewed,
owner, applies-to). Linters in PR #3 will enforce this in CI.

Added under a new H1 "Architecture work on next/v1":

1. Architecture decisions — pre-read MANIFESTO.md; deviations require an
   ADR.
2. Architecture content routing — 10-step decision tree ("where does
   this paragraph go?") with 30-second placement rule.
3. Diagrams — Mermaid first; D2/PlantUML when needed; ASCII forbidden
   in docs/architecture/; PNG only for external-UI screenshots.
4. Dependency policy — license gate, supply-chain gate, bundled vs
   not-bundled, enterprise-grade heuristic, first-class reject reasons.
5. Testing & QA discipline — top-3 CI gates a bank auditor opens first
   (secrets / SAST+SCA / signed SBOM+SLSA-3), full rule set, doc
   linter list, code-review discipline.
6. v1 non-goals — skill registry / hosted models / admin UI / our own
   SaaS are explicitly out; each gets a clean abstraction boundary.
7. Documentation discipline — front-matter rules, hard caps per doc
   type, banned vocabulary + phrases, English-only, no emoji.

These sections are the contract the AI assistant operates under on
next/v1. They do not constrain quick fixes or PoC work on main.

The License Headers section still references BUSL-1.1 in this commit;
PR #132 (license migration BUSL → FSL) updates it. After both land
on next/v1 the file is consistent.

Part of next/v1 Layer 0 scaffolding (PR #2 of 4). Follow-ups: doc
linters (PR #3), CI security/test gates + CODEOWNERS (PR #4).

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

* docs(architecture): address CodeRabbit review (SPDX headers + ADR Status + glossary stub)

CodeRabbit flagged 10 inline issues on PR #134. All addressed:

1. **Missing SPDX/copyright headers (8 files).** Added the mandatory
   `<!-- SPDX-License-Identifier: FSL-1.1-Apache-2.0 -->` +
   `<!-- Copyright (c) 2025 Open Computer Use Contributors -->` as the
   first two lines of:
   - docs/architecture/README.md
   - docs/architecture/MANIFESTO.md
   - docs/architecture/glossary.md
   - docs/architecture/PROCESS.md
   - docs/architecture/adr/README.md
   - docs/architecture/adr/0000-template.md
   - docs/architecture/components/0000-template.md

   Matches the project's CLAUDE.md "License Headers" rule. The headers
   sit above the YAML front-matter, in HTML-comment form (the
   convention used by other Markdown files in docs/future-architecture/
   and docs/operating/).

2. **glossary.md `## Terms` stub heading removed.** Documentation
   discipline rule: "No stub headings. Write the content or remove the
   heading." The intent ("terms added when first used in ≥ 2 docs")
   moved into the body prose. Glossary now has no H2 sections until
   real terms land.

3. **ADR template missing `## Status` section.** The Nygard format
   requires Status / Context / Decision / Consequences / Alternatives,
   per CLAUDE.md. Status was only in YAML front-matter; added a
   `## Status` section at the top with the lifecycle values and a note
   that the section mirrors the front-matter field.

No other PR feedback to address.

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

* docs(architecture): drop forward reference to components/00-overview.md

CodeRabbit follow-up finding on PR #134. The README's reading order
listed "The component diagram in components/00-overview.md (when it
lands)" — a forward reference to a non-existent document, which
violates the documentation-discipline rule "No forward references."

Replaced with a single line that says ADRs and component specs are not
yet populated and points at PROCESS.md for how they will land.

* docs(architecture): add one-sentence purpose line to ADR + component templates

CodeRabbit follow-up finding on PR #134 (adr/0000-template.md:17).
Documentation discipline rule from CLAUDE.md: "First line after
front-matter: one sentence stating purpose and audience. No preamble."

Both 0000-template.md files began with their H1 immediately after
front-matter, skipping the purpose line. Added one sentence to each
naming the document type and audience.

The 5 non-template scaffolding files (README, MANIFESTO, glossary,
PROCESS, adr/README) already carry a purpose-line.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…+ Versioning (#135)

Pure structural refactor. No new rules, no removed rules — just better
grouping and a few stale references corrected.

- **New section `## Branches and scope` at the top.** Explains the
  difference between `main` (PoC / current production) and `next/v1`
  (long-lived enterprise architecture). Mentions that a future PR will
  add a separate "# Architecture work on next/v1" section with rules
  scoped to that branch.
- **Reorganized as `# Global rules (apply to every branch)`.** Same
  rules; clearer group heading.
- **`## Project Structure` updated**: added `settings-wrapper/`,
  `helm/`, `vendor/`, `docs/architecture/`, `docs/future-architecture/`.
  Switched from bullet list to table for readability.
- **`## Versioning` updated**: example version bumped from the stale
  `v0.8.X.Y` to the current `v0.9.X.Y`. Added a line noting that
  `next/v1` will define its own versioning scheme in an ADR; until then
  no tagged releases on that branch.
- **Section titles normalized** to sentence case ("Building the Docker
  image", "npm packages layout", "Project structure").

- License Headers rules (already FSL after PR #132).
- Building / Testing / npm-layout rules — same.
- All wording about FSL-1.1-Apache-2.0, English-only, SPDX headers, etc.

The `# Architecture work on next/v1` section lands in PR #134
(docs/architecture scaffolding). Keeping the structural refactor
separate makes both PRs reviewable: this PR shows the reorganization
diff cleanly; PR #134 shows the new sections cleanly. After both merge
the file reads top-to-bottom: Branches+Scope → Global rules → Architecture work.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
)

* chore(ci): add documentation linters + fix BUSL leak in openwebui tool

Layer 0 sub-PR #3 of 4. Adds the documentation-linter stack defined in
CLAUDE.md "Testing & QA discipline" → "Documentation linter" subsection.

## Linters introduced

| Gate | Tool | What it catches |
|---|---|---|
| Style | markdownlint | base Markdown style; long-line check disabled (tables) |
| Prose | vale | banned vocabulary, banned phrases, marketing-tone tells |
| Links | lychee | broken internal links; forward references = block |
| Size  | wc-budget.sh | ADR ≤ 200, component spec ≤ 600, MANIFESTO ≤ 400 |
| Slop  | ai-slop-detector.sh | TOC in short docs, reflexive Conclusion sections, stub headings |
| Diagrams | ascii-diagram-detector.sh | Unicode box-drawing in docs/architecture/ |
| Front-matter | front-matter-validator.sh | mandatory status / last-reviewed / owner / applies-to |

## Banned vocabulary list

CLAUDE.md lists 14 banned adjectives ("comprehensive", "robust", "seamless",
"powerful", "best-in-class", "industry-leading", "elegant", "battle-tested",
plus 4 additions: "cutting-edge", "next-generation", "state-of-the-art",
"world-class"). All grep-checkable; case-insensitive.

## Banned phrases list

12 phrases including "It's worth noting that…", "In this section we will…",
"This document will…", "Going forward…", "Please note…", "Happy coding",
"In today's fast-paced", "delve into". Regex-based with word-boundary
anchors so technical contexts ("forward references", "going forward into
the merge") don't false-positive.

## Self-test

scripts/docs-lint/test-linters.sh runs intentional-fail fixtures through
each detection rule. 8/8 PASS locally. The CI workflow runs this as the
first job — if any detection rule loses bite, CI fails before the rule
is evaluated against real docs.

## CI orchestration

.github/workflows/docs-lint.yml runs 8 jobs in parallel on every PR that
touches docs/architecture/, CLAUDE.md, README.md, CONTRIBUTING.md, or
the linter configs themselves. Triggered on PR and on push to next/v1.

## BUSL leak fix

PR #132 missed `openwebui/tools/computer_use_tools.py:1` during the SPDX
find-replace (suspected: the file was created via an Open WebUI
filter-tool export rather than the standard source path, but the SPDX
header line itself was still BUSL-1.1). Caught by the gsd-verifier pass
on the plan's P-02 check. Fixed by changing the SPDX-License-Identifier
to FSL-1.1-Apache-2.0; matches the rest of the repo.

## Out of scope here

CI security gates (gitleaks, Semgrep, Trivy, Cosign, SBOM/SLSA),
CODEOWNERS, branch protection, conventional-commits enforcement — these
land in PR #4 of the Layer 0 plan.

Verification:

- `bash scripts/docs-lint/test-linters.sh` → 8 passed, 0 failed.
- All 4 detector scripts run cleanly against current `next/v1` content
  ("OK" or "OK (no files to scan)").
- `git grep "SPDX-License-Identifier: BUSL-1.1"` outside CHANGELOG and
  docs/future-architecture returns zero.

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

* fix(ci): scope markdownlint to docs/architecture/ + fix lychee inputs

Two CI failures on docs-lint workflow for PR #136:

1. **markdownlint** failed on pre-existing style issues in top-level
   CLAUDE.md (1 finding: MD032 blanks-around-lists) and README.md
   (4 findings: MD028, MD034, MD031 x2). These are global rules-area
   docs that were authored before this lint stack landed and aren't
   the target of the architecture linter.

   Scope markdownlint to `docs/architecture/**/*.md` only. Bringing
   the top-level docs into compliance is a separate cleanup task; this
   PR is the linter itself, not a project-wide style overhaul.

2. **lychee** failed with "required arguments were not provided:
   <inputs>...". The `include = [...]` key in lychee.toml is not a
   real lychee config key — inputs are positional CLI args. Moved the
   glob into the workflow `args:` and removed the dead key from
   lychee.toml. Added `--offline` so the gate only checks internal /
   forward-reference link integrity (external HTTP checks are
   rate-limit surfaces and not what the forward-reference rule
   protects against).

Verification: workflow YAML parses; the same docs/architecture/**/*.md
glob is used by both markdownlint and lychee jobs for consistency.

* fix(ci): lychee — failIfEmpty:false on empty docs trees

PR #136 itself doesn't add files under docs/architecture/ (the
scaffolding lives in PR #134). The glob expanded to zero files and the
action default failIfEmpty:true treated empty-result as a CI failure.

The gate exists to catch broken internal links when docs/architecture/
has content; an empty result on this branch is a non-event. Other
docs-lint jobs (front-matter, wc-budget, ai-slop, ascii-diagram)
already emit 'OK (no files to scan)' on empty trees — match that.

* fix(ci): markdownlint — disable MD022, scope MD003 to ATX, drop MD033

After PR #134 scaffolding landed on next/v1, markdownlint started
flagging 47 false-positives across the new template / README files.
Three rules were misfiring:

- MD003 (heading-style): the linter auto-detected setext style from a
  coincidental line in the file and then flagged every ATX heading.
  Pin style=atx explicitly.

- MD022 (blanks-around-headings): the linter parsed the YAML
  front-matter closing '---' as a setext H2 underline, then demanded
  blank lines around the preceding 'status: proposed' line (which is
  YAML, not a heading). Disabled — false positive specific to our
  front-matter shape.

- MD033 (no-inline-html): the templates contain angle-bracket
  placeholders ('# ADR-NNNN: <title>', '<verb>', '<object>',
  '<alternative>', '<name>') that markdownlint reads as inline HTML
  tags. Allow-list approach didn't scale; placeholder leakage into
  real ADRs is caught by ai-slop-detector and human review anyway.
  Disabled.

Net effect: markdownlint still enforces the genuine style rules (list
spacing, fenced-block spacing, no-bare-urls, etc.) but no longer trips
on front-matter parsing artifacts or template placeholders.

* fix(ci): markdownlint — disable MD003 entirely (front-matter parsing)

After scoping MD003 to style:atx, markdownlint still flagged 7 files
with the inverse error: 'Expected: atx; Actual: setext'. Root cause:
markdownlint-cli2 doesn't understand YAML front-matter and parses the
closing '---' delimiter at line 5 of every architecture doc as a setext
H2 underline. That makes line 5 a 'heading', and the linter then claims
the file's heading style is setext, so every subsequent ATX heading is
'wrong style'.

Front-matter is mandatory in docs/architecture/ (enforced by
front-matter-validator.sh), so we can't drop the delimiter. The cli2
default does not strip front-matter pre-parse. Net: MD003 can never
work for our docs as-is, so disable it.

Heading-style consistency in practice is enforced by:
- The component-spec template ('Purpose / Boundaries / Invariants /
  Failure modes / Operational concerns / Open questions' — all ATX).
- The ADR template (Status / Context / Decision / etc — all ATX).
- Documentation-discipline review.

No ATX-vs-setext mixing has been observed; the protection MD003 gave
us was an illusion fighting the front-matter parser.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes Layer 0 of the next/v1 enterprise architecture bootstrap. Lands the
top-3 CI gates a bank auditor opens first, plus the supporting CODEOWNERS,
issue templates, SECURITY.md and gitleaks config.

## What's in

- `.github/workflows/security.yml` — secrets (gitleaks + trufflehog),
  SAST (semgrep), SCA (trivy CRITICAL blocking + HIGH informational),
  IaC (checkov on helm), conventional-commits PR-title gate.
- `.github/workflows/supply-chain.yml` — SBOM (syft) + Cosign keyless
  signature + in-toto SBOM attestations (SPDX + CycloneDX) + cosign
  verify-attestation self-check, gated by `environment: production`.
- `.github/security-exceptions.yml` — schema for 14-day HIGH exceptions.
- `.github/CODEOWNERS` — review routing covering Dockerfile,
  docker-compose, supply-chain entry points (requirements.txt,
  package.json), helm/, computer-use-server/, all build/release/codeql
  workflows, .gitleaks.toml, SECURITY.md, ISSUE_TEMPLATE/, CHANGELOG.md.
- `.github/ISSUE_TEMPLATE/{adr,bug,component,dependency,nfr}-proposal.md`
  + `config.yml` — proposal chooser, blank issues off, security-advisory
  stop banner on bug-report, Fulcio-identity + maintainer health fields
  on dependency-proposal.
- `.gitleaks.toml` — allowlist for documented placeholders and minified
  bundles; unsafe stopwords removed.
- `SECURITY.md` — private vulnerability reporting, 30-day SLA for
  Critical-with-active-exploitation, 90-day SLA for other Critical/High.
- Deleted `.github/SECURITY.md` (duplicate with contradictory policy).

## Hardening

- Every third-party action pinned to 40-char commit SHA.
- Every container image pinned to `name:tag@sha256:<digest>`.
- `persist-credentials: false` on every actions/checkout call.
- Two-source gitleaks pattern: PR head as scan target, base-ref
  .gitleaks.toml fetched via `gh api` (with bootstrap fallback to
  upstream defaults when base lacks the file). Closes the fork-PR
  config-injection bypass.
- supply-chain workflow_dispatch.inputs.image_ref locked to
  `ghcr.io/${owner}/*` prefix + character whitelist (closes identity-
  laundering through arbitrary image signing).
- Anchored `--certificate-identity-regexp` on cosign verify.
- Docker container runs: `--read-only`, non-root user, `:ro` mounts.
- Trivy `limit-severities-for-sarif: true` (works around
  aquasecurity/trivy-action#309).
- SLSA-3 provenance job removed — its `digest: ""` violated the
  generator's contract. Re-added in a follow-up release workflow that
  colocates build/push and provenance generation.

## Verified

All 8 checks pass on commit 64c1963 (CodeRabbit, IaC checkov, SAST
semgrep, SCA trivy filesystem, Trivy, conventional-commits, secrets
gitleaks, secrets trufflehog). Local semgrep against both workflows: 0
findings. Local gitleaks against full history (508 commits): no leaks.

Independent gsd-code-reviewer pass surfaced 9 HIGH + 9 MED + 1 LOW
beyond the CodeRabbit set — all addressed in this PR.
Post-merge gsd-code-reviewer pass on PR #137 commit 64c1963 surfaced
three real defects that PR #137 missed. Two were active bypasses; one
was a guaranteed first-real-release failure.

## HIGH

- H-1 gitleaks bootstrap-fallback was the bypass it was meant to prevent.
  Without --config, gitleaks v8.30.1 autoloads /repo/.gitleaks.toml from
  the PR head per its documented config precedence. A fork PR shipping
  a permissive allowlist would silently win. Fix: always pass --config;
  when the base ref has no real file, write a useDefault stub.

- H-2 supply-chain.yml signed the wrong tag.
  build.yml strips the leading v via type=match,pattern=v(.*),group=1,
  but supply-chain.yml kept the v in the image ref. Fix: tag=${REF_NAME#v}.

- H-3 CycloneDX attestation was created but never verified before the
  job exited green. Fix: mirror cosign verify-attestation --type
  cyclonedx after the spdxjson one.

## MED + LOW

- Trivy CRITICAL pass ignore-unfixed=false (force exception ledger
  entries for un-patched CRITICALs).
- CODEOWNERS dual-team on /.github/dependabot.yml, /.gitleaks.toml,
  /SECURITY.md.
- Dropped dead helm/open-computer-use/values.example.yaml allowlist.
- Narrowed workflow_dispatch cert-identity regex to refs/tags/v* or
  refs/heads/(main|next/v1).
- Added p/dockerfile + p/github-actions to semgrep config.
- Defence-in-depth .. segment check on image_ref.
- Regex-escape REF_NAME and REPO_FOR_VERIFY before interpolation into
  cert-identity anchor.
- Bug-report template Browser qualifier widened to Open WebUI /
  Computer Use UI.
- gh api uses .content // empty to handle directory listings cleanly.
- SECURITY.md back-port window softened to two most recent minors at
  maintainer discretion.

CI green (8/8). CodeRabbit auto-review: No actionable comments.
* chore(ci): exclude main-line legacy from Layer 0 SAST gate

ADR-0001 records the policy: `.semgrepignore` enumerates main-line
legacy paths that v1 GA does not ship (Dockerfile, computer-use-server,
openwebui, settings-wrapper, cron, cleanup, bundled skills). Every
new-architecture component PR removes the corresponding path in the
same commit. PROCESS.md "Adding a component" gains step 4 to enforce
that inheritance.

Layer 0 verifier on `next/v1` HEAD = 13f724e flagged the SAST gate
red on 37 findings in legacy code (root containers, wildcard CORS,
md5 cache keys, stdlib XML parsing, dynamic urllib). The legacy code
is not refactored on next/v1 — it is replaced from scratch in
Layer 6+. The exclusion list shrinks monotonically; new code is never
excluded.

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

* chore(docs): remove LAYER-0-VERIFICATION.md

This file was not in the architecture plan and does not fit any
content-routing tree branch in CLAUDE.md. Verifier output is an
ephemeral process artifact — it belongs in the PR description (where
the verdict and evidence are already captured) or a tracked issue,
not as a committed snapshot that drifts the moment the next pass
runs. ADR-0001 already encodes the policy that resolved the gate-row-0
issue the verifier flagged; the snapshot is redundant.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(ci): extend .semgrepignore to cover missed legacy paths

PR #139 shipped the initial exclusion list but missed three legacy
areas the SAST gate scans: .github/workflows/release-chart.yml,
skills/examples/, and tests/. All three are main-line PoC code
slated for replacement in Layer 6+. The amendment lands them under
the same ADR-0001 policy with no change to the decision or
alternatives.

Discovered by the third verifier pass on commit 709db53.

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

* chore(ci): address CodeRabbit nits on .semgrepignore

- Anchor /tests/ to repo root so future components' nested tests/
  directories are not silently skipped by SAST.
- Reword the skills/ comment so it matches the actual entries: three
  specific public subtrees plus the whole examples tree, not a blanket
  "skills/public/" claim.

Both raised on PR #140 review.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(docs-lint): add architecture-tree whitelist linter

Each file under docs/architecture/ must match an entry in the per-
directory whitelist. Anything else fails CI with a hint pointing at
PROCESS.md and the content-routing tree in CLAUDE.md. This catches
the LAYER-0-VERIFICATION.md class of drift before it lands: scratch
notes, AI snapshots, screenshots in diagrams/, files at the wrong
level, etc.

When a PR legitimately needs a new file kind, it updates ALLOWED in
architecture-tree-whitelist.sh in the same commit, with the tightest
pattern that fits the use case.

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

* fix(docs-lint): segment-aware glob matching in tree whitelist

CodeRabbit caught that fnmatch.fnmatchcase treats `/` as a normal
character. With the original matcher, a pattern like
`compliance/*-mapping.md` would silently accept
`compliance/sub/evil-mapping.md`. Replace the matcher with one that
splits both sides on `/`, requires equal depth, and applies fnmatch
per segment — so `*` can never cross a directory boundary.

Self-test gains a dedicated nested-path denial fixture
(`compliance/sub/x-mapping.md`) plus a positive legitimate fixture
(`compliance/soc2-mapping.md`) so future regressions back to plain
fnmatch fail loudly.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rtifact) (#143)

* docs(architecture): land manifesto §01 audience-and-buyer (Layer 1)

First content-bearing artifact of Layer 1 on next/v1. 54 lines, in budget.

Decisions captured in §01 (per research synthesis in
docs/future-architecture/research/SUMMARY-manifesto-01.md):

- Audience framed as capability ceiling = tier-1-bank-grade, primary
  commercial focus = tier-1 US/EU banks as trust anchor, but the same
  artifact serves any organisation that wants in-perimeter agentic AI.
- Wide-Moat sells an ecosystem subscription plus enablement and
  certification, not a single component. The repository is one component
  of that ecosystem.
- Three coexisting personas inside the customer: business users, agents
  themselves, builder/platform engineers (the enablement target).
- Solo enthusiast is a downstream consequence of FSL, not a co-equal
  persona; contribution channels named.
- Buyer chain has four parallel veto-bearing functions; TPRM is the top
  deal-killer in 2026 driven by NYDFS, FDIC FIL-44-2025, OCC Bulletin
  2025-17, and DORA Articles 28-30. SR 26-2 (17 Apr 2026) shifts the
  veto path from MRM to TPRM/operational-risk/cyber.
- Why-now uses post-Omnibus EU AI Act dates: Annex III standalone
  2 Dec 2027, product-embedded 2 Aug 2028.
- Positioning thesis stakes four clauses (in-perimeter incl. agent loop,
  model-neutral by MCP, compliance evidence as first-class build artifact,
  FSL anti-SaaS with Additional Permissions instrument). UiPath
  (5 May 2026) and Anthropic (19 May 2026) shipped partial in-perimeter
  answers; in-perimeter alone is necessary but no longer sufficient.

Eight research-buffer files land alongside as input evidence:

- bank-buyer.md, enthusiast-audience.md, widemoat-thesis-advisor.md
  (initial gsd-research briefs)
- proof-uipath-anthropic-2026-05.md (primary-source verification of the
  shock events with URLs)
- proof-anthropic-sdk-license.md (Anthropic open vs closed components:
  srt Apache-2.0, Claude Code binary closed, MCP tunnel proxy closed)
- proof-anthropic-sandbox-runtime-vs-ours.md (verdict: adopt selectively
  as BoM dep for in-microVM secondary defence, not a competitor)
- advisor-fsl-internal-use.md (Option 3: stock FSL + published
  LICENSE-ADDITIONAL-PERMISSIONS.md, draft text included)
- SUMMARY-manifesto-01.md (conflicts resolved, decisions taken)

Plan and memory updates queued for post-merge follow-up (EU AI Act dates,
new thesis in widemoat memory, regulator triad memory, Wide-Moat scope
clarification memory).

Local docs-lint runs PASS: wc-budget, front-matter-validator,
ai-slop-detector, ascii-diagram-detector, architecture-tree-whitelist.
Vale banned-vocab/phrase check deferred to CI.

* docs(architecture): address gsd-code-reviewer Critical findings on §01

Code review by gsd-code-reviewer (REVIEW-pr-143.md, 2 Critical / 7 Warning / 6 Info, verdict: ship with notes) flagged two pre-merge blockers plus reconciliation issues with the research buffer. This commit addresses both Criticals and four of the Warnings; remaining Info and Warning items are queued as post-merge follow-up per the review's verdict.

Critical fixes:

- CR-01: dropped FDIC FIL-44-2025 and OCC Bulletin 2025-17 citations from the buyer-chain paragraph. Neither identifier was verified in the research buffer (only NYDFS 21 Oct 2025, DORA Art. 28-30, and SR 26-2 are cited with primary-source backing). Bank Legal would dereference both on first read and find no source; safer to remove until a proof-fdic-occ-2025.md buffer lands in a follow-up PR.
- CR-02: reworded "published LICENSE-ADDITIONAL-PERMISSIONS.md instrument" to "planned" with explicit tracking-issue link (arch/additional-permissions-instrument) and pointer to the draft in advisor-fsl-internal-use.md. The instrument exists in draft only; calling it "published" was a forward-reference violation per CLAUDE.md doc-discipline.

Warning fixes:

- WR-02: added the four 2026 closing use-cases (KYC/AML, IT helpdesk, dev productivity, compliance evidence) to the Audience block per SUMMARY-manifesto-01.md Decision #4. §01 had silently dropped them post-synthesis; restored.
- WR-04: trimmed the eight-component enumeration in the commercial-model paragraph to one sentence. Component detail belongs in §06 or component-spec index, not Audience.
- WR-05: added the Apache-2.0-conversion qualifier ("during the FSL term... each release converting to Apache 2.0 two years after publication") to the Wide-Moat self-imposed SaaS prohibition. Without the qualifier the claim read as absolute and would not survive Legal review.
- WR-06: split agents out of the persona-numbered-list into a separate paragraph framed as architectural concern, not buyer. Personas list now contains only humans (business users + builder engineers).

Queued for post-merge follow-up (per review verdict):

- WR-01: rephrase "four-clause" to avoid overload with adjacent counts.
- WR-03 + WR-07: reconcile SUMMARY-manifesto-01.md line counts and Open Question 4 swap with the shipped §01.
- IN-01: add hedge marker to "CAIO function exists at every tier-1" (uncited assumption).
- IN-02 + IN-03: stylistic refinements on veto-power phrasing and 65-word sentence split.
- IN-04: convert Open Question slug placeholders to clickable issue links once issues are filed.
- IN-05: verify post-merge update list still accurate.
- IN-06: add SPDX license headers to six research-buffer files on next touch (not blocker; research-buffer discipline is lower than canonical architecture).

File grew from 54 to 55 lines after the use-case addition; still well within 80-line section practical cap. All five local linters PASS (wc-budget, front-matter-validator, ai-slop-detector, ascii-diagram-detector, architecture-tree-whitelist).

* docs(architecture): fix lychee forward-ref failure on §01

CI docs-lint / lychee step failed on PR #143 with one broken link:

  [ERROR] manifesto/05-licensing-posture.md | Cannot find file

CLAUDE.md doc-discipline rule is intentional: "No forward references.
Don't link to docs that don't exist." Lychee gate enforces this with
no exclude-path overrides for in-Manifesto cross-refs (and we should
not loosen the gate).

§01 had two clickable forward-refs to ./05-licensing-posture.md.
Replaced both with plain-text §05 references that carry tracking-issue
slugs (arch/manifesto-05-licensing-posture, arch/additional-permissions-instrument).
Removed the inline link to ./../../future-architecture/research/advisor-fsl-internal-use.md
in the FSL paragraph for the same reason — the path resolves on disk but
points into the research buffer (CLAUDE.md doc-discipline: architecture
docs should not link into the buffer; the buffer links INTO architecture
on synthesis, not the other way around).

When §05 lands in a later PR, that PR converts the plain-text §05
references back to clickable links in the same commit.

File still 55 lines. All five local linters PASS.

* docs(architecture): fix CodeRabbit Minor finding in REVIEW-pr-143.md

CodeRabbit caught an internal inconsistency in our gsd-code-reviewer output:
IN-06 heading said "Two research-buffer files" but the list enumerated six.
Updated the heading to match the listed set (six).

The seven CodeRabbit Critical comments on this PR are all false positives
from the known BUSL-1.1 -> FSL-1.1-Apache-2.0 stale-cache trap (PR #132
migrated the licence; CodeRabbit's coding-guideline DB still references
BUSL-1.1; per memory project_next_v1_layer_0_status: "do NOT flip headers").
Ignoring those seven; not changing any SPDX header.
…ng them (#144)

Research cycles surface architectural primitives Wide-Moat will need
(evidence-as-code bundle, ModelProvider abstraction, kill switch SLA,
replay bundles, DORA RoI fields, MCP allow-list, etc.). Without a
discipline for capturing them, primitives stay buried in research-buffer
files and get lost on the next cycle.

Three changes:

1. `docs/architecture/PROCESS.md` — new section "Capturing primitives
   discovered during research" with the one-line-per-entry format and
   the two drain directions (resolve into NFR/principle/spec, or escalate
   to GitHub issue).

2. `docs/architecture/primitives-backlog.md` — initial backlog with
   16 entries surfaced during Layer 1 §01 research (PR #143). Each entry
   names: primitive, purpose, source research-file, target section/spec.
   File carries the same front-matter + SPDX header discipline as the
   rest of the architecture tree.

3. `scripts/docs-lint/architecture-tree-whitelist.sh` — add
   `primitives-backlog.md` to the top-level whitelist. Self-test (10/10)
   still passes.

Backlog shrinks monotonically — entries delete in the PR that lands their
destination artifact (NFR catalogue, principle list, component spec, BoM
row, or tracking issue).

Local docs-lint runs PASS: wc-budget, front-matter, ai-slop, ascii,
architecture-tree-whitelist + self-test.
Post-merge follow-up #6 from PR #143 backlog. Applies the remaining
Warning and Info fixes from REVIEW-pr-143.md (gsd-code-reviewer output)
that were not blocking but deferred for a single tidy-up PR.

Changes to docs/architecture/manifesto/01-audience-and-buyer.md (57 lines,
still under the 80-line section cap):

- WR-01: rephrased the four-clause thesis to drop the bare "four-clause"
  shorthand that competed with adjacent counts (four buyer chains, four
  forcing functions, four compliance certifications). The Positioning
  paragraph now reads "intersection of model-neutrality, source availability,
  fully in-perimeter execution, per-release compliance evidence" — same
  semantics, no overloaded count.
- IN-01: added an explicit hedge marker on the CAIO-function-exists claim
  ("the function is assumed to exist... uncited assumption — see Open
  Question 1"). The claim was carried forward from bank-buyer.md where it
  was already labelled uncited.
- IN-02: rephrased "four parallel chains, each with veto power" to "each
  chain holds at least one veto, and the gatekeeper chain contains four
  independent vetoes" — removes the ambiguity flagged by the reviewer.
- IN-03: split the 65-word TPRM sentence into two sentences (regulator
  triad / consequence) for grep-ability.

Changes to docs/future-architecture/research/SUMMARY-manifesto-01.md:

- WR-03: line-count claim updated from "76 lines" (draft) to "57 lines
  shipped after pre-merge tightening".
- WR-07: added an Open-Question-reconciliation paragraph noting the swap
  from FedRAMP-v1-or-v2 to ModelProvider-scope and the added fifth
  Additional-Permissions-edge-cases question.
- IN-05: the "Required plan + memory updates" block converted from
  "queued" to "completed" with one-line status per item (all four items
  applied during this same follow-up cycle: plan-file dates, widemoat
  thesis memory, layer-0 status memory entry, new regulator-triad memory).

Changes to six research-buffer files (IN-06):

Added the standard two-line SPDX/copyright header to bank-buyer.md,
enthusiast-audience.md, widemoat-thesis-advisor.md,
proof-uipath-anthropic-2026-05.md, proof-anthropic-sdk-license.md,
advisor-fsl-internal-use.md. They were the six research files that landed
in PR #143 without headers; SUMMARY-manifesto-01.md and 01-audience-and-buyer.md
already carried them.

Not addressed (intentionally deferred):

- IN-04 (Open Question slugs → clickable issue links): the GitHub issues
  do not exist yet. Conversion to clickable links happens when the issues
  are filed (separate PR or inline with the §05 / §02 PRs that consume
  the answers).

Local docs-lint runs PASS: wc-budget (57), front-matter, ai-slop, ascii,
architecture-tree-whitelist. Vale + lychee defer to CI.
…2) (#146)

* docs(architecture): manifesto §02 non-functional requirements (Layer 2)

§02 catalogues 142 NFR rows across 9 ISO/IEC 25010:2023 categories with
hybrid Compliance + Cost sections. Each row carries a measurable target,
verification, and source — no aspirational prose.

Three trust planes locked in:
- inbound: Ed25519 JWT on WebSocket bound to container_name
- data access: scoped JWT through credential broker bound to filesystem_id
- egress: network-bound, single path through MITM-proxy

Tier-aware design preserves one-click solo install:
- default tier: Apache-2.0 + TCP + single docker-compose up
- bank tier: HSM-signed Merkle audit, FIPS image variant, mTLS substrate

Standing rules respected:
- zero refs to sandboxd/, process_api_re, "reverse-engineered",
  "Anthropic-observed", or sysbox (security-policy)
- 259 lines (under the 400-line MANIFESTO/sub-doc cap)
- gitignored-ref-detector passes on §02

Adds support tooling:
- scripts/docs-lint/gitignored-ref-detector.sh — fails CI when any doc
  under docs/architecture/ or docs/future-architecture/ cites a path that
  is in .gitignore (reader from a clean clone sees a dead link)
- .gitignore — translations (*-ru.md) marked disposable

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

* docs(docs-lint): correct header comment in gitignored-ref-detector to match implementation

CodeRabbit caught that the "Allowed exceptions" header listed four cases
(`.planning/`, URLs, `docs/`, code-span heuristic) but the script body
only implemented two (URLs + absolute non-repo paths + a system-path
heuristic). Rewrites the comment block to describe what is actually
filtered out vs what is intentionally left to `git check-ignore`.

No behaviour change. Honest documentation only.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(gitignore): extend research-buffer exclusions for Layer 3 tooling

Adds ADVISOR-, NYQUIST-AUDIT-, PATTERN-MAP-, and layer*.md patterns.
Layer 3 introduced three new review-tooling shapes (gsd-advisor-researcher,
gsd-nyquist-auditor, gsd-pattern-mapper) on top of the §02 set
(REVIEW, CROSS-REVIEW, PLAN-CHECK, SUMMARY, CONTRADICTION-CHECK).

The buffer files cite gitignored paths and carry phrases we keep out of
committed prose. Canonical work lives in docs/architecture/; these stay local.

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

* docs(architecture): manifesto Layer 3 trust boundaries (draft)

Lands docs/architecture/02-trust-boundaries.md (226 lines) and the canonical
trust-zone diagram at docs/architecture/diagrams/02-trust-boundaries.mmd.

Five drawn zones: Control plane / Credential broker / Compute plane /
Egress trust-edge / Audit pipeline. Eleven external actors. Eight content-keyed
data classes mapped to NYDFS, GLBA, SEC, GDPR, EU AI Act, PCI DSS. T0-T5
tenant-isolation menu. Three egress modes (transparent / MITM / DLP-ICAP).
Workload-identity floor minimal vs full-capability. Signer-identity-per-boundary
table (§8.1) covering egress JWT, internal RPC, broker scoped-JWT, MITM cert,
Merkle head, image admission. Encryption matrix and regulator citation map
verbatim per §02 NFR anchors.

Closes follow-up #71 (transport variants documented in §7).

Goes through six review passes: gsd-code-reviewer, gsd-advisor-researcher
cross-review, gsd-plan-checker, gsd-advisor-researcher on three structural
decisions, gsd-nyquist-auditor gap audit, gsd-pattern-mapper. Re-review verdict
READY-TO-COMMIT: all 5 blockers RESOLVED, all 5 cross-review CRs RESOLVED,
advisor PKI table verified, all 11 Nyquist gaps closed.

Open Questions tracked in §13 with five entries (placeholder GH-issue slugs
land as real URLs in a follow-up PR before draft → proposed).

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

* chore(docs-lint): allow top-level numbered cross-cutting artifacts

Adds `[0-9][0-9]-*.md` pattern to ALLOWED for files at docs/architecture/
root. Layer 3 (02-trust-boundaries.md) is the first such file; future
Layer 4 (C4 Context) and Layer 5 (deployment topologies) follow the same
shape. Per PROCESS.md, cross-cutting artifacts cited by every component
spec live at the top level rather than under manifesto/ or components/.

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

* docs(architecture): shrink Layer 3 inline mermaid to honour ≤15-line cap

CodeRabbit flagged the §5 inline mermaid as 17 lines (fences + init directive
+ 15 content), exceeding the CLAUDE.md ≤15-line cap. Drops the init directive
(elk renderer still applied via the canonical .mmd source) and shortens four
arrow labels. Block now 16 lines total (2 fences + 14 mermaid). Canonical
diagram at docs/architecture/diagrams/02-trust-boundaries.mmd unchanged.

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

* docs(architecture): drop "bank" wrapper words from Layer 3 prose

Removes four "bank"/"bank-reviewer" occurrences from §1 + §11 prose where
they were marketing wrappers, not load-bearing referents. The audience
stays the same (regulator-reviewer + self-hosting developer per §01);
the prose just doesn't keep saying "bank" in front of it.

Keeps one mention as "(CFR-cited financial-institution rules)" in §6 retention
column — that one is the actual CFR referent, not a wrapper word.

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

* docs(architecture): cut AI-slop from Layer 3 prose

User flagged residual slop. Sweep removed:
- self-referential meta-noise about line caps and CI conventions in §1
- triple "It does NOT restate / NOT describe / NOT carry" formulation in §1
- "no phone-home, no license-check, no telemetry" no-no-no pattern in §1
- "Each is a place where our code runs and where a reviewer can point and ask"
  preamble in §2
- "the contract is ready when X lands" hedge in §2
- repeated "External actors appear on the diagram but are not zones we own"
  preamble in §3 (already in §1)
- repeated "Layer 3 names the isolation tiers we ship" preamble in §4
- "(including elk-renderer init directive, 11 actors, and the optional-marking
  dashed strokes)" list-of-three slop in §5
- duplicate "Optional configurations" paragraph in §5
- "binding artifact for the contract" pompous in §6
- "the minimal-config shape is honest about not being a compliance posture"
  hedge in §6
- "Both NFRs hold in spirit on both shelves; the substrate differs" hedge
  in §8 (replaced with what actually happens)
- "the signer's key custody and rotation are properties of that boundary,
  not a separate zone" design-defence in §8.1
- "The full matrix lives in the canonical encryption table here; component
  specs cite this matrix and never restate it" cite-rules in §9
- duplicate substrate-specific TLS paragraph in §9 (already in §2)
- "robust(ness) appears verbatim... preserved against banned-vocab list"
  meta-noise in §11
- "useful as background while the buffer is migrated" framing in §12

Layer 3 down to 211 lines from 225. Inline mermaid 16 lines including
fences. Zero "bank" / "hardened" wrapper words.

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

* docs(architecture): drop LLM/S3 from External actors; clarify Control plane is not a model proxy

User flagged that LLM upstream as a named External actor with a ModelProvider
abstraction contract overstates what we own. Outbound traffic from the
sandbox or Control plane to an LLM endpoint, a customer MCP server, or
a customer object store is just traffic through the Egress trust-edge —
the egress policy decides where it can go, the credential broker decides
which scoped token reaches it. None of these are zones with a contract
we hold at the boundary.

Removed from §3: LLM upstream row, Customer object store row. Both fold
into "endpoints behind the egress policy" framing added at the top of §3.

Reworded §2 Control plane row: dropped "LLM-upstream proxy (we proxy, we
do not host)" phrasing that read like we host a model proxy. Replaced with
"outbound to LLM and any other upstream goes through Egress trust-edge like
any other request; the Control plane is not a model proxy".

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

* docs(architecture): cut second-pass slop from Layer 3 + add TTL to diagram labels

Removed 6 more slop patterns user flagged:
- preamble "Names the trust zones, the data classifications, ..." list-of-five
- §1 first paragraph "Layer 3 is the zoning map: what zones exist, ..." duplicate
- triple "X stay in Y; X' stay in Y'; X'' stay in Y''" parallelism in §1
- list-of-six "LLM hosting, chat UI, the caller of ..." in §1 scope sentence
- "Five zones appear as subgraphs on the canonical diagram" preamble in §2
- "These are the only systems with which we hold a published contract"
  pompous framing in §3
- "(elk renderer, 11 external actors, optional-marking dashed strokes...)"
  internal-kitchen list in §5
- "the inverse mapping (our class → customer class) is what the contract binds"
  pompous in §6

Diagram label fix from v3 reviewer: appended TTLs to two boundary edges so
the §5 inline mermaid is self-sufficient without cross-checking §8.1:
- "Ed25519 JWT" → "Ed25519 JWT (session ≤4h + RPC ≤60min)"
- "scoped JWT" → "scoped JWT (≤15min)"

Reconciled §1 / §3 contradiction (v3 reviewer Focus 2): §1 scope sentence
now says "Everything else is either an external actor (§3) or an outbound
endpoint behind the egress policy"; LLM endpoints stay outside §3 per
explicit user direction.

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

* chore(docs-lint): extend ai-slop-detector with 6 new pattern classes

User flagged that the slop seen in Layer 3 should be caught by the linter
on every future PR, not relied on for manual sweeps. Adds six new checks:

- 6. Self-referential CI / doc-rules meta-noise: "kept within the X budget",
  "≤N-line form", "CLAUDE.md rules/conventions/budget", "backticks preserve …
  against banned-vocab". Reader does not need rules-about-itself; rules live
  in CLAUDE.md.

- 7. Hedge / pompous phrasing: "holds in spirit", "honest about (not) being",
  "the binding artifact for the contract", "is what the contract binds".

- 8. Boastful "is the only X and …" superlative without measurable referent.

- 9. Triple-parallel "X verbs in Y; X' verbs in Y'; X'' verbs in Y''"
  construction (the verb appearing three times in one semicoloned line).

- 10. Triple-negation "It does not X. It does not Y. It does not Z."

- 11. List-of-three "(no X, no Y, no Z)" parenthetical construction.

All patterns observed in Layer 3 drafts before manual cleanup; none should
have reached the doc in the first place. Linter would have caught them.

Tested: passes on Layer 3 (zero hits); existing pre-existing failure on
adr/0001-layer-0-gate-legacy-exclusion.md "Amendments" stub heading is
not new.

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

* docs(architecture): drop prompt-redaction + Merkle-signing claims from Layer 3 scope

User flagged two scope grabs we should not be making:

1. Prompt-redaction is not our zone. AI-guardrail policy (PII masking,
   prompt-injection detection, content filtering) is the customer's AI
   gateway responsibility — LiteLLM, Lakera, Lasso, in-perimeter model
   with its own guardrails, customer's own redactor service. We route
   the traffic and audit the egress event; what the gateway does with
   the prompt is its contract, not ours. §6 paragraph rewritten; §2
   Egress trust-edge row updated; NFR-COMP-26 flagged for revisit in §02.

2. Merkle-head signing is not ours either. We submit audit batches to a
   transparency log of the customer's choice (public Sigstore Rekor,
   customer-private Rekor, customer-operated CT log). The log operator
   signs the Merkle head; we sign only the submission envelope (envelope
   auth, not log integrity). §8.1 signer row reframed; §10 paragraph
   updated; NFR-SEC-03 flagged for revisit in §02 to match this split.

Both NFRs in §02 need a follow-up rev to match (separate PR).

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

* docs(architecture): name scope ownership per NFR — DELIVER vs ENABLE vs REVISIT

User flagged that several §02 rows tighten the platform's responsibility
in ways Layer 3 has already corrected (prompt-redaction, Merkle-head
signing). Adds a Scope-ownership section to §02 that classifies every
row as one of three:

- DELIVER: we ship the code, we are accountable for the measurable target.
  Sandbox escape, egress proxy, credential broker, audit pipeline,
  RTO/RPO of our planes, encryption defaults.
- ENABLE: we publish the contract / telemetry / integration point; the
  customer is the principal and owns the policy/content. DORA major-
  incident timeline, NYDFS § 500.17, IdP posture (relying-party).
- REVISIT: claims more than our scope or names a customer-side
  responsibility (AI gateway / data-controller / regulator-facing
  process). Eight current rows listed by ID with the corrected position:
  NFR-FS-03 (LLM cache), NFR-REL-04 (LLM failover),
  NFR-SEC-21 (model-level threats), NFR-COMP-09 (PMS Art. 72 is deployer),
  NFR-COMP-10 (DPIA is data controller), NFR-COMP-14 (model accuracy),
  NFR-COMP-18 (42001 binds the deployer), NFR-COMP-25 (ZDR is customer
  contract), NFR-COMP-26 (prompt redaction is AI gateway),
  NFR-SEC-03 Merkle-head wording (tx-log signs the head, not us).

REVISIT rows keep their IDs and current wording in §02 until the next
revision; Layer 3 already takes the corrected position. Layer 3 §1 now
links the Scope-ownership section so the row-by-row decision is reachable
in one hop.

§02 grew 33 lines (246 → 279); Layer 3 grew 2 lines (209 → 211). Both
under cap.

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

* docs(architecture): citation map indicative-not-verbatim; drop legacy buffer refs; NFR-IDs as links

User flagged that the §11 "Reviewers copy this into their workpapers; section
/ control IDs are verbatim, not paraphrased" claim is the dangerous bit, not
prose slop. A doc that invites verbatim regulator-citation reuse without a
verification trail and then carries misattributed citations becomes an audit
liability the moment a reviewer copies a cell.

§11 reframed: "Mapping is indicative, not verbatim. Verify every cell against
the source text before reproducing in an audit workpaper. Layer 3 does not
represent these citations as audit evidence by itself." Inline a list of
confirmed defects tracked at `arch/regulator-citations-verify-pass`:

- DORA Art. 12 is backup / restoration / recovery, not retention. 10-year
  retention floor lives in SEC 17a-4 / FCA SYSC 9 / EU AI Act Art. 19(1).
- DORA Art. 9 is "Protection and prevention". ICT-risk framework is Art. 6;
  detection / logging is Art. 10.
- NYDFS § 500.7 is access privileges (PAM, least privilege, JIT). Segmentation
  is not a § 500.7 requirement.
- DORA Art. 28(2)(c) "location of processing" likely lives in Art. 30 key
  contractual provisions; source-check pending.
- CRI Profile v2 column was lifting CSF 1.1 subcategory codes (PR.PT-N,
  DE.DP-N) which were restructured in CSF 2.0; CRI Profile uses its own
  diagnostic-statement numbers. Whole CRI column removed pending source pass.

§12 "See also" section listing legacy buffer files dropped — legacy doc
references in a canonical artifact are an antipattern.

NFR-IDs converted to markdown links pointing at manifesto/02-nfrs.md. Reader
clicks to land on §02; Ctrl+F finds the row. 64 NFR-ID occurrences linked.
A per-ID anchor pass is a separate task (would need anchor headings or HTML
anchors in §02 tables).

Doc preamble after front-matter dropped (user flagged it as boilerplate the
reader does not need).

§11 verbatim/copy-into-workpapers self-praise removed. §11 inline `robust(ness)`
linter-comment removed.

Line count 207 / 600.

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

* chore(docs-lint): catch verbatim-citation claims + 'is the only' superlatives

User flagged citation-map "verbatim / copy into workpapers" claim as the
dangerous slop pattern that pure-prose tools miss. Adds two more checks:

- 12. "Reviewers copy this … workpapers" / "verbatim, not paraphrased"
  pompous audit-evidence framing. A draft doc cannot represent itself as
  a verbatim regulator-citation source.

- 13. "is the only X" / "is unique among/in X" superlatives without a
  measurable referent (CLAUDE.md "no adjectives without measurable
  referent").

Both observed in Layer 3 v1 before cleanup; both should not have reached
the doc.

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

* docs(architecture): collapse double blank line in Layer 3 (markdownlint MD012)

Trailing empty line left after removing the `robust(ness)` linter-comment
created two consecutive blanks before §12 heading. Markdownlint MD012
caught it.

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

* docs(architecture): consolidated tree sweep — slop / consistency / scope fixes

Cross-reviewer triangulation (code-reviewer FLAG, plan-checker ON-TRACK,
pattern-mapper 3 top-risk) converged on this fix batch.

§02 NFRs:
- drop default-tier vs bank-tier framing in NFR-SEC-03 / COMP-07 / COMP-15
  (was contradicting §01 "one product, not a tiered SKU"); rewrite as
  configuration-conditional on HSM-rooted custody per NFR-FLEX-04
- flip NFR-FLEX-15 egress default to transparent pass-through (was MITM;
  contradicted the one-click solo-install invariant); DLP-ICAP demoted
  to a configuration of the MITM mode, not a third mode
- replace the §02 embedded trust-zone diagram with a link to the Layer 3
  canonical (single source of truth)
- rename data-plane → Compute plane, egress-proxy / egress gateway →
  Egress trust-edge across all NFR rows
- drop tech-brand parentheticals from NFR-PERF-03 / NFR-SEC-02 / NFR-SEC-25 /
  NFR-SEC-28 / NFR-FLEX-03 / NFR-COMP-29 (brand picks belong in component
  specs, not portability-NFR rows; vendor lists kept only in NFR-FLEX-01/04/13
  where the matrix IS the testable property)
- strip self-referential CI / doc-rules meta-noise from the intro

Layer 3 trust-boundaries:
- fix four forward references (§12.4/5, components/<egress-proxy>.md,
  "Layer 7", MANIFESTO non-goals)
- drop T4 / T5 isolation tiers (no named workload) into open question 1
- collapse egress posture from three modes to two; DLP-ICAP is an MITM
  configuration
- reconcile §3 actor table with diagram: LLM / object-store drawn for
  orientation only; SIEM / KMS marked optional
- shrink §9 encryption matrix to a single-sentence invariant citing
  NFR-SEC-37; per-boundary table moves to component specs
- shrink §8.1 signer-identity table to a single paragraph; per-boundary
  table moves with the PKI decision
- strip remaining tech names from prose (SIEM brand list, PKI tool
  shortlist, vendor lists)

Diagram:
- remove vendor lists from external-actor labels (LLM upstream / SIEM /
  outbound proxy / DLP-ICAP / KMS / transparency log)
- LLM upstream + customer object store reclassified as `endpoint` nodes
  (dashed grey) to match prose "drawn for orientation, not actors"

Glossary:
- backfill 10 entries for terms used in ≥ 2 architecture docs (Control
  plane / Compute plane / Egress trust-edge / Credential broker / Audit
  pipeline / Capability shelf / Isolation tier T0-T3 / Egress posture /
  Egress JWT / OCSF / Transparency log / Compute-time metering)

Primitives backlog:
- prune 8 drained primitives that landed in §02 NFR rows (kill switch /
  replay bundle / tamper-evident audit / WORM retention / BYOK / SPIFFE /
  MITM egress / MCP allow-list)
- remove Anthropic-srt entry — internal-research reference must not load-
  bear in the canonical tree
- drop "first FSL adopter" superlative
- record drained-list at the file end as audit trail

ADR-0001:
- fix Amendments stub heading (ai-slop-detector hit on every prior run)

Linter:
- ai-slop-detector check 14 — block internal research-artifact phrasing
  (anthropic srt / sandbox-runtime / sandboxd paths / reverse-engineering
  wording) from leaking into the architecture set

Out of scope (deferred): ADR stubs for PKI / FIPS / k8s-distros / licence-
allowlist are per-plan on-demand artifacts, not bulk-generated stubs; the
open-question slugs in Layer 3 §12 + §02 stay as bare slugs until the
TBD-to-issue batch lands. Layer 3.5 §03 non-negotiables slot decision
also deferred.

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

* docs(architecture): v2 review follow-ups — collateral cleanup

Eleven small fixes surfaced by gsd-code-reviewer v2 + gsd-plan-checker v2
+ gsd-pattern-mapper v2 after the consolidated batch landed. Each fix
≤ 5 lines; CI green locally before push.

§02 NFRs:
- drop NFR-SEC-03 from REVISIT bullet list — already reframed earlier
  in same batch (NEW-01)
- §02 "Five zones meet around the Compute plane: ... Compute plane, ..."
  duplicate listing → "Five zones interact" (NEW-03)
- drop LiteLLM / Lakera vendor names from REVISIT bullets NFR-REL-04 +
  NFR-COMP-26 — same vendor names were stripped from Layer 3 last batch
  but resurfaced here (NEW-04)
- capitalise "egress trust-edge" → "Egress trust-edge" in NFR-FS-03
  REVISIT bullet (plan-v2-1)
- NFR-FLEX-10: bare "compute" → "Compute plane" (plan-v2-2)
- NFR-PERF-07/08/09 brand-baked IDs (runc / gVisor / kata-fc) rewritten
  as substrate-named: container-substrate / user-space-kernel substrate /
  microVM substrate (pattern-v2-R3) — symmetric to NFR-SEC-02 brand-
  agnostic rewrite from prior batch

Layer 3 trust-boundaries:
- §2 over-claim "every inter-zone arrow is encrypted" → align with
  NFR-SEC-37 wording (carve-outs named) (NEW-06)
- §10 "to be revisited in §02 to match this split" — stale, §02 now
  matches (NEW-02)

Glossary:
- Transparency log entry: drop "Sigstore Rekor" vendor brand → vendor-
  neutral wording (NEW-05); the term itself was held vendor-neutral in
  Layer 3 prose, glossary entry shouldn't reintroduce the brand
- Compute plane entry: drop "formerly named data-plane" hedge — §02
  rename is complete, hedge no longer needed (pattern-v2-R1)

Diagram:
- CPROXY (customer outbound proxy) and SOAR reclassified ext → extOpt
  (dashed border) to match the prose "(optional)" label in Layer 3 §3
  actor table (pattern-v2 diagram dashes)

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

* docs(architecture): Layer 3 v3 — close 5 contradictions before proposed

User-flagged before draft → proposed transition. Each item was a real
contradiction or ambiguity inside the canonical doc, not a stylistic one.

1. Retention floor — §6 REGULATED-AUDIT row cited "DORA Art. 12(2) 10 yr
   for critical functions" as the retention basis. §11 had already
   documented that DORA Art. 12 is backup/restoration, NOT retention; the
   10-year floor lives in SEC 17a-4 / FCA SYSC 9 / EU AI Act Art. 19(1).
   §10 prose said "7-year minimum (NFR-COMP-01)" without naming the
   "10y configurable" half. Three numbers, one defective citation. Now:
   §6 cell + §10 prose both say "7 y default / 10 y configurable per
   NFR-COMP-01; 10 y floor from SEC 17a-4 / FCA SYSC 9 / EU AI Act
   Art. 19(1)". Single machine-enforced retention statement.

2. Revoke ≤5 min vs in-flight TTL ≤4 h — §7 said "IdP unreachable →
   in-flight sessions continue until TTL expiry"; §8 declared revoke
   latency target ≤5 min. Read together those promise that revoke holds
   during an IdP outage even though the only named mechanism is
   re-authentication. Made the revoke mechanism explicit: kill-switch
   maintains a Control-plane session denylist checked on every Compute
   RPC + every Egress request via the broker token; revoke is
   independent of IdP reachability, NFR-SEC-04 ≤5 min and NFR-SEC-01
   ≤30 s kill-switch share the denylist. IdP participates in token
   issue, not in revoke.

3. T1 namespace + shared kernel for agent-execution — §4 placed T1
   (namespace + netpolicy, shared kernel) under "non-NPI workloads",
   which is a data-classification argument, not a sandbox-escape argument.
   For LLM-issued tool calls / code, the attack surface is the container
   boundary regardless of data class. Added a "Multi-tenant
   agent-execution invariant" paragraph under §4: multi-tenant agent
   execution requires microVM substrate (the full-capability shelf);
   T1 namespace remains valid only for single-tenant agent execution or
   for multi-tenant workloads that do not execute LLM-issued code.

4. Token-name taxonomy — §5 diagram and §02 token TTL taxonomy used
   different spellings for the same three classes ("session JWT" vs
   "Egress JWT"; "RPC token" vs "Generic internal token"). Now: the
   canonical class names live in §8 + §02, the §5 diagram + canonical
   .mmd both use the same canonical names, and §8 carries a one-line
   diagram-label retirement note for the old wording.

5. Mermaid convention encoding — the §5 inline block declared "solid =
   always, dashed = optional" but did not encode it (all default solid),
   while the canonical .mmd does encode it. Now: caption says the
   convention applies in the canonical file, the inline block is a
   simplified overview that does not encode dashed-vs-solid, and the
   reader is pointed at the canonical file or §3 actor table for the
   optional reading.

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

* chore(gitignore): extend research-buffer ignore pattern to CONSOLIDATED-*

Sibling review artefacts (REVIEW-*, PLAN-CHECK-*, PATTERN-MAP-*) are
already ignored under docs/future-architecture/research/. The
CONSOLIDATED-* family (multi-reviewer synthesis docs the architect-loop
produces between review passes) belongs to the same working buffer and
gets the same treatment.

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

* docs(architecture): mark REVISIT NFR rows informational / non-gating

CodeRabbit Major: REVISIT bullets de-scope NFR-FS-03 / REL-04 / SEC-21 /
COMP-09/10/14/18/25/26, but the rows themselves sit in the catalogue
tables with active-looking Target columns. A CI gate or verifier that
walks §02 row-by-row would enforce a Target the bullets explicitly
de-scoped — contradiction between the gating mechanism and the prose.

Fix:
- §"Scope ownership" intro now states explicitly that REVISIT rows are
  informational / non-gating until re-cut; CI gates, release acceptance,
  compliance attestations, and verifier passes MUST NOT enforce a
  REVISIT row's Target.
- Each of the 9 affected rows carries an inline **[REVISIT — non-gating]**
  prefix in its Scenario cell, so a gate-author walking the table cannot
  miss the deferral.

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

* docs(architecture): Layer 3 — close peer-review findings M1 / M2 / M3 / m6

Peer review on PR #147 surfaced four substantive items. Each was a real
defect verifiable against the doc itself; all closed in this commit.

M1 (inline mermaid §5 — content defect):
- Edge "CP → VM" was labelled "Egress JWT (≤4h) + Generic internal
  (≤60min)". Per §8 token taxonomy + canonical .mmd, Generic internal
  token rides Control plane ↔ broker ↔ audit (host-side), NOT
  CP → Compute plane. Label now reads "Egress JWT (≤4h)" — matches §8
  + canonical .mmd line 51.

M3 (§1 prose stale):
- Said "the §02 catalogue still carries the old wording … until its next
  revision". §02 was updated in this same PR (NFR rows marked
  [REVISIT — non-gating] + enforcement-status block). Rewritten to
  reflect actual state: "§02 marks each REVISIT row inline as
  [REVISIT — non-gating] so CI and verifier passes do not enforce it;
  the substantive re-cut lands in a follow-up PR." Also: §"Scope-ownership"
  → §"Scope ownership" (heading spelling match).

m6 (§2 zone 1 prose semantic):
- Said "Outbound to LLM and any other upstream goes through the Egress
  trust-edge like any other request" while drawn from Control plane.
  Control plane never originates upstream traffic — only Compute plane
  does. Rewritten: "Holds no outbound path to upstream; all upstream
  traffic originates in the Compute plane and traverses the Egress
  trust-edge."

M2 (link-cap structural):
- §2 zone table carried 30+ NFR cross-doc links inline (5+ per zone).
  CLAUDE.md doc-discipline: "one outbound link cap: ≤ 3 cross-doc links
  per H2 section". Restructured: each zone row keeps a single primary
  NFR-anchor link; the rest move to a consolidated "Secondary NFR
  anchors per zone" footer immediately below the table. Same pattern
  for §8: tables carry bare NFR-ID text in §02-anchor column, single
  consolidated link-footer below. Reader still has every cross-reference;
  ≤3-per-H2 rule no longer broken across the doc.

NOT changed:
- m1 (inline mermaid 16 lines fence-inclusive) — wc-budget.sh counts
  content-only; CI passes; trace agreed with peer.
- m7 (§11 retrospective-defects prose) — user explicitly requested this
  audit trail earlier in the PR session. Sweeping it to a one-liner
  loses the *why* the verification pass exists. Kept as-is.
- m5 (PR title contains "manifesto") — accepted artefact, will use
  cleaner naming for next top-level cross-cutting PR (Layer 4 C4
  Context).
- n1 / n2 / n3 — all cosmetic; defer.

PR description (m4) will be updated outside the commit.

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

* docs(architecture): align canonical mmd with §3 actor table + §2/§8.1 wording

Cross-check after peer-batch surfaced three diagram-vs-doc deltas:

1. IdP: was :::ext (solid border = required). §3 actor table now says
   "required on full shelf" — minimal-capability solo install runs
   without IdP. Reclassed :::extOpt (dashed) to match.

2. Credential broker node label: "no master key full-capability" parsed
   poorly. §8.1 + zone 2 wording is "delegated STS on full shelf"
   (host-local on minimal, delegated STS on full). Relabeled to match.

3. Egress proxy node label carried "NFR-SEC-08 MCP allow-list" — the
   only NFR-ID embedded in a node label across the whole diagram.
   Replaced with role wording "MCP allow-list enforcement". NFR-IDs
   live in §02 + §-anchor tables in the prose, not in diagram nodes.

No edge changes; classDef set unchanged.

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

* docs(architecture): Layer 3 — paper-cut polish before merge

Sweep after the prior batch landed: remove rudiments and meta-comments.

§11 Regulator citation map:
- Drop the 5 "Confirmed defects from a prior pass" prose bullets — the
  defects they listed were already fixed in the table itself in earlier
  passes (DORA Art. 12 → 19(1); NYDFS § 500.7 → § 500.5; CRI Profile
  column removed entirely). The bullets paraphrased the table state
  from three revisions ago. CLAUDE.md "If a paragraph paraphrases the
  data, delete it." Tracking slug `arch/regulator-citations-verify-pass`
  stays in the preamble; full defect list lives in git history.
- Row "Audit pipeline → SIEM" cell: drop trailing "not DORA Art. 12"
  — without the prose bullets above it, the negation has no context;
  rewritten as "(retention floor cited from SEC 17a-4 / FCA SYSC 9,
  see §10)".
- Rows "Credential broker" / "Compute plane" EU AI Act cells: drop
  defensive "(direct Art. 15 wording)" parentheticals — leftovers from
  earlier vale-lint battle over banned-vocab escapes. The Art. 15(4)
  wording stands on its own.
- Drop "(subpoint TBD)" from "Art. 28 ICT third-party general" — TBD
  without a tracking slug is just noise.
- Preamble: "before reproducing in an audit workpaper" → "before
  reuse" (overkill phrasing).

§6 REGULATED-AUDIT row:
- Retention-floor cell collapsed from long inline explainer to
  "7 y default / 10 y configurable (see §10)". The full citation
  (SEC 17a-4 / FCA SYSC 9 / EU AI Act Art. 19(1)) already lives in
  §10 NFR-COMP-01 anchor.

§2 footer Compute plane:
- "Performance targets … live in component specs, not as zone
  properties." → "live in component specs." Drop the meta-justification
  by negation.

§8:
- Drop the "diagram-label note" about the older "session JWT" / "RPC
  token" retired wording. That was about a rev that already shipped;
  reader of current doc does not need the history.

§12 item 5:
- Drop hedge "when one of the candidate paths reaches a decision
  point" — the per-boundary signer table lands with the ADR, no need
  to qualify when that happens.

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

* docs(architecture): align Layer 3 canonical diagram with §7 / §8

Five gaps caught before draft→proposed transition, all in the canonical
.mmd (doc body unchanged):

1. Add ORCH→BR edge with Generic internal token label (host-side RPC,
   session policy + scope, TTL ≤60 min). §8 token taxonomy lists three
   token classes; without this edge the third class was invisible and
   the broker looked free-standing despite §8 stating "Control plane ↔
   broker ↔ audit, host-side."

2. Add two dashed revoke edges ORCH-.->VM and ORCH-.->PROXY with
   NFR-SEC-04 ≤5 min label. §7 fixed revoke-independent-of-IdP via
   Control plane denylist checked on every Compute-plane RPC and Egress
   request; this control channel was missing from the canon entirely.

3. Symmetrise PROXY→LLM and PROXY→OBJ labels — both now read
   "strict TLS validation / broker-issued credential / fail-closed".
   Previous asymmetry ("fail-closed" only on LLM, "broker-issued token"
   only on OBJ) misrepresented that both upstreams use broker-issued
   credentials.

4. Drop "per DoD ZTRA" claim from palette comment — DoD ZTRA v2.0 does
   not normatively define red/amber/green/blue subgraph palette. Honest
   label is "project convention". Avoids the same overclaim §11 flags
   for regulatory citations.

5. Remove `defaultRenderer: elk` from init. GitHub-native mermaid
   renderer and many CI/Mintlify pipelines silently fall back to dagre
   when the elk plugin is absent, producing inconsistent layouts across
   audiences. Dagre default is stable everywhere.

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

* docs(architecture): disambiguate Layer 3 revoke mechanism on egress path

The §7 sentence "denylist checked on every Compute-plane RPC and on
every Egress trust-edge request via the Credential broker token"
was grammatically ambiguous between two designs: (a) direct denylist
push to the Egress trust-edge, (b) indirect revoke via Credential
broker scoped-JWT non-reissue. The previous .mmd encoded (a) with a
dashed ORCH→PROXY edge; the rest of the architecture only works under
(b):

- §8 sets broker scoped-JWT TTL ≤15 min — the short TTL exists for
  revoke-via-non-reissue, otherwise it is unmotivated cost.
- §2 zone 4 keeps the Egress trust-edge intentionally dumb (single
  egress, MCP allow-list, no policy state); direct denylist would
  add control-plane coupling for no extra coverage.
- PROXY → LLM / OBJ edges already carry "broker-issued credential";
  revoke through credential non-reissue uses the same channel.

Two changes, no semantic drift:

1. .mmd — remove the dashed ORCH→PROXY edge. Compute plane keeps its
   direct denylist edge because the Egress JWT TTL (≤4 h) makes
   indirect-revoke too slow for that path.
2. §7 prose — split the sentence: Compute plane is checked directly;
   Egress trust-edge revoke is "indirect via Credential broker
   scoped-JWT non-reissue, TTL ≤15 min caps propagation on that path."
   Anchor the TTL bound to NFR-SEC-29. Add an explicit note that the
   NFR-SEC-01 ≤30 s p99 SLA is the Compute-plane stop bound, not the
   upstream-credential revoke bound.

Kill switch SLA is not added to the diagram label: trust-boundary
diagrams show which-channel-carries-what, not timing budgets per
channel. The two SLAs (NFR-SEC-04 ≤5 min revoke, NFR-SEC-01 ≤30 s
kill switch) both live on the same ORCH→VM edge; loading both onto
one arrow degrades readability.

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

* docs(architecture): Layer 3 close-out — wire §12 + §11 to real issues, draft → proposed

Pre-merge close-out for PR #147. Two changes, one status transition:

1. §12 open questions — replace 5 `arch/*` placeholder slugs with real
   GitHub issue URLs (#148 cross-tenant isolation grading, #149 metadata-
   only gate, #150 SIEM-bridge transport + backpressure, #151
   transparency-log publishing path, #152 PKI tool pick + signer-identity
   table). Drop the "Real GitHub issue URLs replace the slugs before draft
   → proposed" footer line.

2. §11 — replace `arch/regulator-citations-verify-pass` with #153.
   Cell-by-cell source verification scheduled as a follow-up batch with
   other REVISIT slots; not gating Layer 3 draft → proposed.

3. Internal references — §4 footer ([#148]) and §8.1 PKI pointer
   ([#152]) updated to match. `grep arch/` returns zero matches.

4. Front-matter — status: draft → proposed. last-reviewed: 2026-05-25.
   Layer 3 is now the second Manifesto artifact at proposed (after §02
   NFRs) and is the source of truth for trust zones, isolation tiers,
   egress posture, audit-pipeline pluggability, and the token taxonomy.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…163)

* arch(next/v1): §02 add "Sandbox tier — workload-driven selection" sub-section (E1)

Adds the workload-trust-profile axis as a first-class §02 concept ahead of
the catalogue rows that reference it. Three named profiles:

- trusted_operator (solo / dev) → runc
- internal_workforce (vetted employees) → gVisor (v1 hardened default)
- untrusted (unknown actors) → microVM (post-v1, #161; not deployable v1 GA)

Cites Anthropic Self-Hosted Sandboxes (May 2026) as the published
multi-provider precedent. Forward-links NFR-SEC-38/39 (enforceability,
landing in the next commit) and AP-13 (anti-pattern, landing with the
row edits).

* arch(next/v1): §02 reframe tier rows; add NFR-SEC-38/39; AP-13; drop tech-name leaks

Row edits (E2-E11) + new rows (N1/N2) + new anti-pattern (AP-13):

- E2 NFR-PERF-03: cold-start target generalised to hardened tier; per-tier
  baselines in PERF-07/08/09.
- E3 NFR-PERF-09: explicitly gated on microVM tier shipping (#161); not
  enforced in v1.
- E4 NFR-SEC-02: dropped "microVM-default" framing; gVisor v1 hardened
  default; microVM hardware-virt post-v1; tier-appropriate escape
  resistance with seccomp BPF + Landlock + cap-drop in every tier.
- E5 NFR-SEC-29: broker binding substrate matches tier (loopback/UDS on
  runc and gVisor; vsock CID + JWT scope on microVM post-v1).
- E6 NFR-SEC-35: KVM precondition limited to microVM tier; Helm
  pre-install probe ≤2 s with clear error on KVM-absent host;
  CI test on KVM-absent runner.
- E7 NFR-FLEX-02: runtime ladder reframed (runc + gVisor v1; microVM
  post-v1, Firecracker named as example, Kata as one packaging option).
- E8 NFR-FLEX-06: same egress invariant qualified by microVM tier ship.
- E9 NFR-FLEX-09: dropped Kata+RKE2 / bare-VM Firecracker tech names.
- E10 AP-13 (new): picking sandbox tier by data classification is
  forbidden; trust profile picks tier, data class governs custody /
  retention / residency. Backed by vale lint rule landing in next commit.
- E11: pruned remaining "minimal-capability shelf" / "full-capability
  shelf" occurrences inside §02 (NFR-SEC-03 only).
- E12: Open Question 5 parenthetical clarifies microVM is post-v1.

New rows:

- NFR-SEC-38: workload-trust profile declaration is a deployment-time
  invariant; mismatch is admission-time hard error; untrusted profile is
  NOT deployable in v1 GA (admission rejects with pointer to #161).
- NFR-SEC-39: tier-downgrade alarm via audit event
  `config.trust_profile.downgraded`; SIEM-bridge HIGH; SOAR webhook ≤30s.

Both new rows ship as commitments; implementation lands when control-plane
code exists.

* arch(next/v1): vale rule AP-13 — block "(NPI|RESTRICTED|CONFIDENTIAL) → microVM" phrasing

Enforces §02 anti-pattern AP-13 at the prose level: data classification
does not pick the sandbox substrate. Trust profile picks the tier; data
class governs custody / retention / residency.

Standalone file rather than appended to banned-phrases.yml because vale
existence rules share message/level per-file; AP-13 needs its own message
pointing the author to the §02 anchor.

* arch(next/v1): Layer 3 shelf→tier rename; multi-tenant invariant in trust-profile terms (L1-L14)

Aligns Layer 3 with the §02 workload-trust-driven tier model.

- L1: §2 Compute plane row reframed in tier terms (runc / gVisor / microVM)
  with forward link to the new §02 "Sandbox tier" sub-section.
- L3: §4 multi-tenant invariant rewritten in trust-profile terms — bare
  runc multi-tenant forbidden; gVisor as v1 default; microVM post-v1.
- L4: T0 tier-table label "single-tenant minimal-capability" →
  "solo / dev / single-operator".
- L5, L11, L13, L14: shelf → tier renames in §3, §6, §8, §9, §10, §12 (key
  custody, audit sink, signer narrative, Open question 4).
- L6, L7, L8: §3 actor-table "Optional?" cells use tier names instead of
  shelf labels.
- L9: §6 data-class-gating sentence reframed — trust profile picks the
  tier; data class still picks BYOK + customer-managed audit sink. AP-13
  is the load-bearing forbidder of data-class-driven substrate selection.
- L10: §8 table column headers renamed (Solo / dev tier vs Hardened tier
  and above).
- L12: §8.1 signer paragraph renamed to tier vocabulary.

last-reviewed bumped to 2026-05-27.

* arch(next/v1): drop competitor-listing slop from §02 sandbox-tier sub-section

The "Anthropic Self-Hosted Sandboxes — Cloudflare microVM, Daytona container,
Modal gVisor, Vercel container" line was AI-slop preamble citing a competitor
product launch as if it justified our design. §02 is a normative NFR catalogue,
not a press-release. Evidence stays in the RESEARCH artifact.

* arch(next/v1): slop sweep on §02 sub-section + tier NFR rows + Layer 3 invariant

Removes self-praise, forward-references, and duplication introduced by the
prior commits in this PR:

- §02 sub-section gVisor row drops "Production precedent at AI scale:
  OpenAI, Modal, Anthropic, Google App Engine" — competitor listing in a
  normative NFR catalogue is marketing, not requirement.
- §02 sub-section drops "Honest v1 deployability" header + the
  re-articulation paragraph (the table already says what the trust profile
  is and what tier it maps to).
- §02 sub-section drops forward references to NFR-SEC-38/39 + AP-13 —
  reader sees those below; preamble pointing forward is slop.
- NFR-SEC-02 drops "Runtime brand pick within a tier is a component-spec
  decision" — already covered by NFR-FLEX-02.
- NFR-SEC-38 / NFR-SEC-39 row cells compressed; same load-bearing content.
- AP-13 drops "Enforced at the prose level by a vale rule on ..." —
  anti-pattern is normative, the implementation note belongs in the vale
  rule itself.
- Layer 3 §2 Compute plane row drops "(see ...)" preamble before the §02
  link.
- Layer 3 §4 multi-tenant invariant drops v1/post-v1 narrative — that
  already lives in §02 + §2 zone table.

* arch(next/v1): fix CRIT F-01 self-contradiction + WARN F-02 matrix count + INFO F-05 back-link

Code-review findings from REVIEW-PR-163-code.md:

- F-01 (CRIT): Layer 3 §6 line 110 was internally contradictory — it
  asserted "CONFIDENTIAL+ workloads require the hardened tier (gVisor)..."
  (the data-class→tier mapping AP-13 forbids) and immediately added "Tier
  selection is workload-trust-driven, not data-class-driven (forbidden by
  AP-13)". Rewritten so data class drives custody / audit sink / residency
  obligations and trust profile drives the tier.
- F-02 (WARN): NFR-SEC-38 admission-matrix count "6 valid v1 + 3 rejected
  v1" was ambiguous. Now spells out: 6 cells valid by pairing rules
  (3 require post-v1 microVM); 3 cells rejected by pairing; v1 GA
  deployable = 3; v1 GA rejected = 6 (3 pairing + 3 microVM-not-shipped).
- F-05 (INFO): NFR-SEC-38 had no back-link to AP-13; added one clause.

* arch(next/v1): plan-check WARN fixes — restore #161 link + AP-13 concrete example

Plan-check findings from PLAN-CHECK-PR-163.md:

- PC-1 (WARN): Layer 3 §4 multi-tenant invariant lost the direct #161
  link in the slop sweep; reader now needs §02→sub-section→#161 (2 hops).
  Restored one inline [#161].
- PC-2 (WARN): AP-13 example was dropped in the slop sweep; without it the
  anti-pattern is abstract. Added the minimal concrete form ("NPI →
  microVM" / "PUBLIC → runc") that names the forbidden phrasing without
  re-introducing the long description.
…lit (#157)

Absorb two egress contract primitives from sandboxd research dump
(sandboxd/sandbox-egress-architecture.md) that are not yet named in
§7:

1. Structured deny header (`x-deny-reason`) on blocked egress so audit
   and SOAR can classify outcomes without free-text log parsing.
2. Two-tier block discipline — SNI pre-filter for unallowed destinations
   (cheaper, lower forensic value) vs L7 inspection for allowed
   destinations (richer, more expensive).

Both are contract-level claims about Egress trust-edge behavior, not
implementation details; Envoy/Cilium/ext_proc wiring remains a
component-spec concern when the egress-proxy spec opens.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…strate + operator-auth canon (#164)

Cross-document consistency sweep of the canonical architecture set.

T-01 — shelf/tier terminology fork. The minimal↔full config axis was named
"Capability shelf" (glossary, C4, diagrams) but "solo / dev tier" / "hardened
tier and above" (02-trust-boundaries §3/§8/§9/§10), with no doc bridging the
two and "hardened tier" defined nowhere. Rename the key-custody-axis usages to
"minimal shelf" / "full shelf" (glossary is the authority); reserve "tier" for
the runtime ladder and T0–T3. Add a `## Sandbox tier` glossary entry for the
runc/gVisor/microVM axis (previously defined only in §02) and cross-link all
three axes as orthogonal. The §8 table header had also conflated the shelf with
the runtime ("Hardened tier and above (v1 gVisor; microVM post-v1)") — split.

T-02 — full-shelf substrate contradiction. glossary said "microVM on the
full-capability shelf" and the trust-zone diagram drew "microVM full", but the
NFRs and §2 say microVM is post-v1 and gVisor is the v1 hardened default. Fix
glossary + .mmd to "gVisor full (microVM post-v1)".

T-03 — operator-auth on the minimal shelf. PR #155 introduced a "host-rooted
local credential" operator path in the C4 doc to close the no-IdP-on-minimal
gap, but left it contradicting the canonical operator row, NFR-SEC-09, and
NFR-COMP-29, and the term was undefined. Land the two-clause operator-auth in
the canon: trust-boundaries §3 actor row, NFR-SEC-09, NFR-COMP-29, and a
glossary definition under Capability shelf. The C4 row is now backed.

Minor: NFR-REL-02 "Data-plane" → "Compute-plane" (single divergent label);
README stale "empty" status cells for manifesto/ + components/ → "partial".

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(architecture): manifesto Layer 4 — C4 Context (draft)

Layer 4 of the next/v1 manifesto stack, following §01 audience-and-buyer
(PR #143), §02 NFRs (PR #146), and §02 trust-boundaries (PR #147). One
canonical Mermaid + one ≤80-line doc, per the plan's "intentionally
minimal" framing.

Scope:
- 03-c4-context.md (54 lines) — purpose / inside-the-box / diagram
  reference / external actor table with required-vs-optional column /
  scope-out / open questions.
- diagrams/c4-context.mmd (44 lines) — flowchart LR + project palette
  (red/amber/green/blue), same engine as 02-trust-boundaries.mmd.
  Convention: solid = required, dashed = optional configuration.

Five research agents (competitor format sweep, legacy mine, OCU value
articulation, OCU-vs-Wide-Moat boundary, diagram-format advisor)
synthesised in docs/future-architecture/research/SUMMARY-layer4.md.
Key agreed decisions:

- OCU is the central box; Wide-Moat is mentioned once as the opinionated
  bundle that ships OCU + peers (n8n, Open WebUI) as one curated stack
  but is not required for OCU to be useful.
- Siblings are an open class: any MCP-speaking peer is a first-class
  integration; REST is a fallback. n8n and Open WebUI are examples in
  the bundle, not the definition of "sibling."
- The §02 §3 actor table stays the single source of truth — Layer 4
  cross-references it instead of duplicating, then adds an "optionality"
  column the actor table did not need at Layer 3.
- LLM upstream is NOT a separate actor on Layer 4 — it is one of the
  MCP-speaking peers (model-neutral by construction). Customer object
  store is not an actor either — §02 line 45 already classifies it as
  an outbound endpoint behind the egress policy, not a contract-bearing
  actor. No backport to §02 §3 needed.
- Preamble framing (Why OCU): practitioner-first value-creation
  ("build automations once, delegate, scale across a team without
  leaving the safety boundary"), with model-neutrality via MCP as the
  integration property. Not TPRM-first, not moat-intersection.

Diagram engine: plain Mermaid flowchart with the project palette
verbatim from 02-trust-boundaries.mmd. Rejected alternatives:
Mermaid C4Context (upstream-experimental, palette divergence),
C4-PlantUML (no GitHub-native render).

Open question §6 #1 (Wide-Moat bundling status for n8n + Open WebUI on
next/v1) tracked at #154. The integration shape is locked here; the
packaging decision is not Layer 4's job.

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

* docs(architecture): apply Layer 4 review batch (13 fixes from 4 reviewers)

Four reviewers (gsd-code-reviewer, gsd-plan-checker, cross-architectural
coherence, competitor comparison) converged on one load-bearing defect
plus 12 follow-ups. Verdict matrix: 2 Error / 4 Warning / 7 Info.

Errors (block merge):

1. LLM upstream as inbound MCP peer — three reviewers caught it.
   §02 trust-boundaries:45 explicitly classifies LLM upstream as an
   outbound endpoint behind the egress policy ("not actors against our
   contracts"), and 02-trust-boundaries.mmd:13 draws it as a separate
   outbound node. Listing it inside the inbound PEER node label put two
   opposing semantics on the same name. Fix: drop "LLM upstream" from
   the PEER node label and from the §4 actor-row example list. The
   §41 paragraph remains the canonical mention.

2. AI-guardrail anchor pointed at §02 §6 (Data classification taxonomy);
   the ownership statement lives in §02 §2 zone 4 / §7. Repointed to
   §2 zone 4 so the link lands where the claim actually lives.

Warnings:

3. SIEM edge was solid in c4-context.mmd:35, but SIEM is :::extOpt
   (dashed-border optional). Layer 4's own header convention says
   "solid border = required; dashed border = optional configuration".
   The Layer 3 diagram already draws it dashed. Fixed.

4. "Layer 6" forward references in three places (doc §2 / §3 + diagram
   BOX node). Layer 6 does not exist as an artifact. Reworded to
   "Internal decomposition is out of scope at this layer" + dropped
   the pointer line from the diagram BOX node.

5. BOX node carried three lines of internal description that duplicated
   §1 prose. Collapsed to a single role-line
   "Open Computer Use / in-perimeter agent-execution platform".

6. §1 first sentence opened with value-prop voice ("users build
   automations once, delegate them to agents..."). Replaced with a
   scope declaration; the practitioner framing remains in §01
   audience-and-buyer where it belongs.

Info / polish:

7. Palette claim in §3 named all four colors (red / amber / green /
   blue) but the diagram only uses red + green. Weakened claim to
   "red-untrusted / green-trusted convention; amber and blue from the
   trust-boundary diagram do not apply at the Context level".

8. Added solo-install callout in §3: "the solid / dashed split makes
   the one-click solo-install path visible at a glance".

9. SIEM Role cell rephrased to "OCSF v1.x event bridge consumed by the
   customer's SIEM" for voice consistency with other customer-* rows.

10. Dropped repetitive `02-trust-boundaries.md §3` link from each actor
    row (the preamble already pins the actor table to §02 §3). Renamed
    column to "NFR anchor"; em-dash where no NFR anchor exists.

11. Operator edge in diagram now labelled `PAM-JIT SAML attr / NFR-COMP-29`
    matching 02-trust-boundaries.mmd:47.

12. PEER edge label simplified to `MCP authz spec / audience-validated`
    matching the Layer 3 wording.

13. Added compliance pointer after the actor table: "Regulator citations
    and measurable targets for each row land in manifesto/02-nfrs.md".

14. Added footnote tracing master-plan `agent user` → folded behind
    `MCP-speaking peer` (humans drive OCU only through an MCP-speaking
    peer; direct human-to-OCU UI is a v1 non-goal).

Length 58 lines (≤80 budget). Banned vocab grep clean.
ai-slop-detector OK. No new ASCII diagrams.

Glossary additions (MCP-speaking peer, REST fallback, Wide-Moat
opinionated bundle) and a few cross-doc reconciliations (NFR-FLEX-14 vs
REST fallback wording, Layer 3 SOAR edge dashed-vs-solid) deliberately
deferred to a follow-up PR — not Layer 4 scope.

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

* docs(architecture): layer 4 v2-review batch (W-01 + 3 polish)

W-01 — IdP optionality row: rewrite to two-clause form mirroring SIEM
("optional on minimal shelf (local auth fallback) — required on
full-capability shelf"); resolves doc/diagram mismatch where doc said
"required on full shelf" but .mmd drew IDP dashed.

I-01 — Trim .mmd palette comment: list only colors actually used at
Context level (red, green); amber/blue carve-out already explained in
§3 prose.

I-03 — Bind "regulated-enterprise" term once in §1 (CLAUDE.md default
audience term) so a cold reader knows what flavor of customer
"customer's existing IdP" means.

I-05 — Drop "Master-plan agent user" phrasing (term not defined
anywhere in architecture tree); rewrite as plain "humans drive OCU only
through an MCP-speaking peer" sentence.

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

* docs(architecture): layer 4 review batch — reconcile operator-auth + dashed-border convention

Follow-up to W-01, which fixed the IdP row's optionality but left two
internal contradictions the prior fix surfaced.

R-01 — Operator-auth path on the minimal shelf. The Operator row was
required everywhere via a "SAML-asserted attribute", but W-01 made the
IdP optional on the minimal shelf — leaving the required operator with no
SAML issuer, and the implied "local auth fallback" forbidden by
NFR-FLEX-03 (no in-house user table). Give the Operator row the two-clause
form: host-rooted local credential on the minimal shelf, short-lived
SAML-asserted attribute on the full shelf. Drop the undefined "local auth
fallback" phrase from the IdP row; the operator-credential story now lives
on the row that owns it.

R-02 — Dashed-border convention vs IdP. The §3 convention read "every
dashed-border actor is opt-in for the full-capability shelf", but IdP is
dashed yet required on the full shelf. Rebind the convention to the real
axis: solid = present on the minimal shelf; dashed = not on the minimal
shelf by default. Per-actor optionality stays in the §4 table. IdP and
SOAR edges switched to dashed to match their dashed nodes; OPER edge label
de-SAML'd to match the dual-credential row.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(architecture): Layer 5 — bounded contexts (draft)

DDD strategic decomposition. Two core subdomains (Agent Execution &
Sandbox Lifecycle; Compliance Evidence & Audit Lineage), two supporting
(Tenancy & Isolation; Operator Access), four generic (Identity, Secrets,
Policy, Model access). Distinguishes bounded context from trust zone;
maps the five Layer 3 zones onto the two core contexts. Context map names
the Published Language (OCSF), Conformist (MCP, ModelProvider), and ACL
(generic integrations) relationships. Feeds Layer 6 C4 Container.

Open questions tracked at #165, #166.

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

* docs(architecture): Layer 5 review batch — fix model-edge canon contradiction

Address gsd-code-reviewer findings on PR #167:

- CR-01 + WR-01: §3 no longer claims all four generic contexts are
  Layer 3 §3 actors. Only IdP and KMS are §3 actors; Model access is an
  outbound endpoint behind the egress policy ("not an actor against our
  contracts"); Policy evaluation is not drawn in Layer 3, introduced here.
- §4 context map reroutes the Model edge through the Egress trust-edge,
  matching the Layer 3 invariant that upstream traffic originates in the
  Compute plane and traverses the egress edge — never a direct context edge.
- WR-02: expand "ACL" to "Anti-corruption layer" in diagram labels; add
  Bounded context, Anti-corruption layer, Published Language to glossary.
- WR-03: align "OCSF events" → "OCSF event" across both diagrams; note §2
  shows only the core-to-core edge.
- WR-04: fix CLAUDE.md anchor to #v1-non-goals-locked-early.

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

* docs(architecture): Layer 5 GSD-review batch — DDD rigor + goal-backward gaps

Address gsd-plan-checker (3 blockers) and gsd-advisor (1 blocker, 4 warnings):

- ModelProvider relabelled Conformist → Anti-corruption layer (self-authored
  adapter exists to absorb provider swaps — the defining ACL property; was
  internally inconsistent with the identical IdP/Secrets/Policy treatment).
- Dropped the Egress trust-edge node from the §4 context map (a trust zone is
  not a context-map node); Model edge now direct AEX→MOD ACL, egress traversal
  stated in prose with the Layer 3 link.
- OCSF core↔core boundary relabelled Open Host Service + Published Language
  (fan-in from four zones, fan-out to multiple SIEMs); added the
  no-shared-identifier invariant so PL does not degrade into a shared kernel.
- Operator Access and Tenancy: value axes rewritten to name a real owned model
  (PAM-JIT contract; T0–T3 selection logic) instead of "cross-cutting property",
  resolving the build-but-undrawn incoherence. Operator drawn in §4 as
  Customer/Supplier; §5 questions reframed from existence to refinement.
- Policy ACL consumer named: Egress trust-edge (MCP allow-list) + Credential
  broker (token scoping); SIEM/SOAR/transparency-log drawn as downstream
  consumers; proxy/ICAP noted as egress configs, not new contexts.
- Compliance Evidence core-ness re-grounded on lineage/replay domain depth,
  not OCSF (generic) or TPRM (go-to-market).
- §3 adds the linguistic-test defense for the four-zones-into-one merge;
  broker and workload-trust grading raised as Open Questions (#168, #169).
- glossary: add Open Host Service, Customer/Supplier.

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

* docs(architecture): reword shared-kernel invariant to clear ai-slop detector

"the only thing that crosses" tripped the boastful-superlative pattern;
restate as "share the OCSF event and nothing else" — same invariant, no
superlative framing.

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

* docs(architecture): Layer 5 bounded contexts — draft → proposed

User sign-off after full GSD review chain (code-review + plan-check + DDD
advisor). CI green.

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

* docs: add slop-pattern rules + doc-slop-reviewer agent; apply to Layer 5

Structural AI-slop kept slipping past the word-level linter (negative-framing
headings, redundant purpose lines, define-by-negation, repeated nouns). Codify
the structural patterns and add a reusable reviewer.

- CLAUDE.md "Documentation discipline" gains a "Slop patterns" subsection:
  headings name content (never "Why X is not Y"), purpose line states purpose
  once, no throat-clearing tails, no hedge-restatement, no rule-of-three
  padding, no define-by-negation, no forced symmetry. With before/after examples.
- .claude/agents/doc-slop-reviewer.md: read-only reviewer for the structural
  tells regex cannot catch. Spawn on every new/edited doc before merge.
- Layer 5 04-bounded-contexts.md, first doc through the new gate:
  - §1 heading "Why this layer is not the trust-zone layer" → "Context layer
    vs trust zones".
  - purpose line: drop the audience-restating second clause and the
    "before any component exists" tail.
  - §2 Tenancy cell: "selection model; a model with its own logic, not a mere
    deployment flag" → "selection logic" (dropped the repeated "model" that
    also collided with the LLM "Model access" context, and the negation).
  - §2 Operator cell: "specific to us, not a differentiator" → positive form.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ose tells (#170)

* docs(architecture): slop sweep — clear negative-framing headings + prose tells

Apply the new CLAUDE.md "Slop patterns" rules across the merged canon
(found by the doc-slop-reviewer sweep; 0 blockers, structural warnings):

- Negative-framing / question headings → noun phrases:
  README "What's here"/"What's NOT here yet" → "Contents"/"Not yet present";
  PROCESS "What this file is not" → "Scope boundary" (negation list folded
  into one sentence); adr/README "When to write an ADR (vs. inline note)" →
  "ADR threshold".
- 01-audience: drop the "security, compliance, and operational scale we know"
  adjective-triple + "we know" hedge; keep the tier-1-ceiling rationale.
- 02-nfrs: drop the defensive tail "not contradictions".
- adr/0001: drop the "replaced from scratch" restatement (already stated in
  Context); fold the rewrite-timing into the rejection clause.
- adr/0000-template: "Neutral but worth noting:" → "Neutral:" (the template
  was seeding the banned hedge).
- 03-c4-context: drop the second "visible at a glance" restatement.

"## Why now" in 01-audience kept — recognized positioning-section label,
body is dated fact; a noun-phrase rewrite reads worse.

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

* docs(architecture): rename README heading to avoid TOC-detector collision

"## Contents" matched the ai-slop-detector TOC regex (treats a literal
"Contents" heading as a table of contents in a short doc). The section is a
file inventory, not a TOC → "## Files in this directory".

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#172)

* docs(architecture): scope OCU correctly — loop and model are external, not ours

The canon conflated OCU (the component we design: MCP server + sandbox
executor) with the Wide-Moat bundle (Open WebUI + n8n + LiteLLM + OCU + …).
The agent loop and model access live in the bundle / calling client, not in
OCU. This produced a direct contradiction: CLAUDE.md called OCU a
"multi-provider proxy" while glossary/Layer 3 say the Control plane "is not a
model proxy". Bring every OCU doc to the correct state:

- CLAUDE.md v1-non-goals: OCU does not host/select/proxy an LLM and does not
  run the loop; an LLM is one allow-listed egress endpoint; the loop and model
  choice live in the calling client. Drops "ModelProvider" + "multi-provider
  proxy" (the contradiction).
- 04-bounded-contexts: delete the "Model access" generic context (node, table
  row, ACL edge, ModelProvider paragraph) — OCU has no model-integration slice;
  an LLM is egress, not a context. Reword "agent loop" → "agent-issued
  tool-calls/code" where it implied OCU drives the loop. Drop bare-"model"
  DDD-sense (use domain/language).
- 03-c4-context: OCU is the tool-execution boundary; the calling client runs
  the loop and owns the model; hosted LLM/model-selection/loop are scope-out.
- 01-audience-and-buyer: keep the bundle moat thesis but attribute "loop
  included" + "model-neutrality" to sibling bundle components; OCU contributes
  the sandbox + audit lineage. Drop "ModelProvider" from OCU's contract list.
- 02-nfrs: re-cut NFR-FLEX-01 (provider-switch → endpoints-are-egress-config),
  NFR-FLEX-14 (MCP server to the client, not "to upstream LLM"), NFR-COST-02
  (LLM-overhead → egress-edge overhead).
- primitives-backlog: drop the ModelProvider primitive (it was a loop/
  generation concern, not OCU's).

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

* chore: ignore COMPETITOR-/PLAN-/RESEARCH- research-buffer files

Extends the existing docs/future-architecture/research/ ignore patterns to
the three new buffer files, keeping the working buffer out of git per the
котлован rule (it stays a local scratch area until Layer 13 migration).

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

* docs(architecture): apply GSD review batch to OCU scope-fix

Address scope-consistency + code reviewers on PR #172:

- c4-context.mmd: BOX label "in-perimeter agent-execution platform" →
  "tool-execution platform" — it lagged the same doc's "tool-execution"
  prose and the loop-is-external correction (the one artifact the prior
  commit missed).
- 02-nfrs NFR-COMP-25: drop the dangling "(NFR-FLEX-01)" ZDR pointer — the
  rewritten NFR-FLEX-01 no longer carries the ZDR-posture clause; state it
  inline instead.
- primitives-backlog: log the ModelProvider removal under a new "Removed as
  out-of-scope (2026-05-30)" section so the audit trail records why the
  entry vanished (scope-correction, not promotion).
- last-reviewed bumped to 2026-05-30 on the five edited docs that lagged
  (03-c4-context, PROCESS, primitives-backlog, 01-audience, 02-nfrs).

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

* docs(architecture): rephrase 03-c4 §1 opening off "OCU is…" scope-restatement

CodeRabbit (Minor): the Purpose-section opener "Open Computer Use (OCU) is …"
restates project scope, which CLAUDE.md bans. Lead with purpose+audience
(names the external boundaries, for architects/security engineers), then the
scope description — no "X is a…" opening, no "Purpose:" throat-clearing.

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

* docs(architecture): drop redundant "not in OCU" restatements

The loop-is-external point was stated three times; twice is noise. Keep the
load-bearing statements (the §4 prose in Layer 5, the "runs the loop" label
on the MCP-caller node, the Layer 4 scope-out) and cut the duplicate negations:

- 03-c4 §2: drop "not the LLM loop" tail (the guest agent is already defined
  as the in-sandbox executor).
- 04-bc §3: drop "the agent loop and model choice live in the calling client,
  not in OCU" tail ("not a context we model" already carries it; §4 states the
  loop location once).
- 04-bc §4: trim the trailing "not in OCU" (the sentence already names the MCP
  caller as where the loop runs).

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…le (#177)

* docs(architecture): reconcile credential model — strict Anthropic style

The canon carried an internally-contradictory credential model: most rows
said "broker issues a scoped-JWT into the guest, guest presents it" while
NFR-SEC-30 already said "broker terminates outbound TLS". The guest-token
framing was never the intent. Converge everything to the strict model
confirmed against Anthropic's process_api / egress design:

- The guest holds NO upstream credential. It sends an unauthenticated
  request; the Egress trust-edge attaches the upstream authorization on the
  outbound leg, fetched from host-side Credential custody.
- "Credential broker" → "Credential custody" (host-side store, rotation,
  delegated STS). Injection folds into the Egress trust-edge. Custody has no
  guest-facing interface.
- Token taxonomy: the guest holds only the Session JWT (session identity,
  ≤4h, CP→guest). The ≤15min lease is held by the edge (custody credential
  lease), never the guest. "Egress JWT" → "Session JWT" everywhere.
- Injection requires the edge to originate the upstream connection (L7 /
  MITM mode); transparent pass-through cannot inject. Cert-pinning /
  client-mTLS / DPoP upstreams tracked at #176.

Touches: NFR-SEC-23/25/27/29/30/31 + token taxonomy + SEC-10/11 rename;
Layer 3 §2/§3/§5 diagram/§7 revoke/§8/§11; Layer 4 §2/§4; Layer 5 zone
names + context-map vocab + open question; glossary entries. The five
containers do not change — custody and edge stay two boxes (custody ≠
enforcement). Layer 6 (PR #173) reconciled separately on its branch.

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

* docs(architecture): soften §8 token-table cross-claim (RC-01)

Scope-consistency review (6/6 PASS, 2 nits): the §8 table claimed to match
the NFR token table "verbatim", but the two now differ in column header
(Consumer vs Holder) after the rewrite. Claim the three classes / scopes /
TTLs match, not the literal layout.

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

* docs(architecture): drop negative-restatement (doc-slop-reviewer)

Three spots stated the "guest holds no upstream credential" invariant two or
three times in one passage. Keep the single load-bearing negative, drop the
restatements: custody glossary ("custody issues no token to the guest"),
egress-edge fail-closed ("never bypassed"), token-table ("not anything the
guest holds —").

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

* docs(architecture): clarify revoke timeline ≤1/≤5/≤15 (CodeRabbit)

The rewrite left two revoke numbers reading as a conflict (≤5 min propagation
vs ≤15 min lease survival). State the three bounds smallest-first: custody
revokes the lease ≤1 min (NFR-SEC-29); denylist propagates ≤5 min
(NFR-SEC-04); a non-revoked lease self-expires at its ≤15 min TTL. An explicit
revoke cuts upstream access within ≤5 min — the 15-min TTL is only the ceiling
for a session never revoked. Resolves both CodeRabbit nits.

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

* docs(architecture): clarify token rotation, injection mode, revoke SLAs

- NFR-SEC-10: the ≤4h is a per-token TTL, not a session cap. While a session
  is active the Control plane rotates the token before expiry (a stolen token
  dies in ≤4h); session length is not bounded by the TTL. A long human session
  (multi-hour investigation) is not interrupted. Drop the undefined
  "session-max"; a hard session cap, if any, is a policy tracked at #178.
- NFR-SEC-29 (CodeRabbit): ≤1 min revocation is custody-internal — a tighter
  subset of the ≤5 min platform-wide target (NFR-SEC-04). State the relation.
- Layer 3 zone 4 (CodeRabbit): qualify that upstream-auth injection is
  MITM-inspecting mode only; transparent pass-through cannot inject (§7).

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

* docs(architecture): anchor session limits to regulators; demote token TTL

The ≤4h session-JWT TTL was an unsourced number that conflated three distinct
limits. Research (PCI-DSS 4.0, NIST SP 800-63B-4) separates them; we align to
AAL3 (privileged automation in a bank perimeter):

- NFR-SEC-10: session JWT TTL ≤4h → ≤60min, reframed as an anti-replay window
  (engineering bound, no regulatory citation), rotated while the session runs.
- NFR-SEC-40 (new): idle / inactivity timeout ≤15min — PCI-DSS 4.0 Req 8.2.8
  + NIST 800-63B-4 §2.3.3 (AAL3). This is the number a bank auditor looks for.
- NFR-SEC-41 (new): absolute session lifetime ≤12h — NIST 800-63B-4 §2.3.3
  (AAL3 SHALL). Default off on the minimal shelf, customer-tunable on the full
  shelf. Closes #178 with a sourced number instead of "if any".

Token TTL ≠ idle ≠ absolute: the token rotates under both session limits;
session length is bounded by SEC-40/41, not by the token TTL. Updated token
taxonomy table, Layer 3 §5/§8 + diagram, glossary Session JWT entry.

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

* docs(architecture): cut IdP-independence restatement (doc-slop-reviewer)

§7 revoke paragraph stated IdP-independence three times. Keep the opening
claim and the closing rationale ("that is why ≤5 min revoke holds even during
an IdP outage"); drop the bare middle restatement.

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

* docs(architecture): generalize MCP allow-list → egress allow-list

"MCP allow-list" mixed abstraction levels — it named a private case (one
destination type) where the general control (egress allow-list, deny-by-
default) was never stated. An MCP server, LLM API, object store, or internal
API is one allow-listed destination, not its own enforcement mechanism.

- NFR-SEC-08: "MCP allow-list enforcement" → "Egress allow-list" (customer
  declares reachable destinations; deny-by-default; destination type is not a
  separate control).
- Layer 3 zone 4 + .mmd: same generalization.

How a corporate environment authors these egress rules (rule format / standard)
is deferred to the egress-proxy component spec + a separate research pass.

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

* docs(architecture): finish MCP allow-list → egress allow-list (2 stragglers)

bounded-contexts §3 and the §02 long-form-scenario list still named the
private case. Generalize both.

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

* docs(architecture): fix two more abstraction-substitution defects + skill supply-chain NFR

Abstraction-substitution sweep (the MCP-allow-list defect class) found two
more embarrassing instances; both fixed:

- NFR-SEC-17 named "Egress posture (sandbox trust tier)" — but it is the
  enforcement mechanism of the NFR-SEC-08 allow-list, and "Egress posture"
  collides with the glossary/NFR-FLEX-15 term (transparent vs MITM). Renamed
  to "Egress allow-list enforcement mechanism" + cross-link SEC-08↔SEC-17.
- NFR-SEC-05 named "MITM-friendly egress" — but it anchors the whole Egress
  zone and MITM is the opt-in mode, not the default. Renamed "Single
  forward-proxy egress (customer-CA injectable; MITM-inspecting opt-in)".
- primitives-backlog: same two renames.

Also: NFR-SEC-42 (new, v2/tbd) — skill-registry supply chain (author-uploaded,
server-signed, provenance-tracked, signature-verified at attach), pairs with
SkillProvider NFR-SEC-24; tracks #179.

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

* docs(architecture): drop last 'MCP allow-list' straggler in PROCESS.md example

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/operator split (#189)

* docs(architecture): Layer 3 — Storage broker zone + Control-plane MCP/operator split

Verified against the rclone-filestore binary (Go, anthropic.filestore.v1alpha
Connect-RPC): the guest's mutable user-data mount is served by a host-side
storage broker that holds the storage-backend credential; the guest holds only
a session-scoped handle (filesystem_id). That credential class is distinct from
the LLM-egress credential injected at the Egress trust-edge from Credential
custody, and the inbound mount path is distinct from the outbound egress path.

- Storage broker drawn as a 6th trust zone (was 5): host-side, guest-facing
  mount interface, governs the inbound data path; distinct from Credential
  custody (no guest interface, outbound-only). NFR-SEC-25/23 reconciled — the
  storage-backend credential is held by the Storage broker, not custody.
- Control plane stated as one zone with two interfaces — agent-facing MCP and
  operator/lifecycle; the kill-switch is reachable only on the operator
  interface, never over MCP. The two-container split is a Layer 6 concern.
- New §7.1 separates the two guest-data paths (mount-in vs egress-out).
- Token taxonomy gains the storage-mount handle (four classes); synced between
  02-trust-boundaries §8 and manifesto/02-nfrs §Token TTL taxonomy.
- Zone count synced across 02-trust-boundaries, 03-c4-context, 04-bounded-
  contexts, glossary, 02-nfrs (six zones; five collapse into Agent Execution).

Skill-mount classes (public read-only / user mutable) are deferred to the
SkillProvider ADR (post-v1, NFR-SEC-24/42), not drawn here. Image signing /
digest-pin remains a forward NFR (#180), not asserted as observed.

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

* docs(architecture): Storage broker — file-RPC + broker-signs + allow-list-only egress

Advisor + binary/competitor research resolved how the broker's backend leg
reaches the object store without the SigV4-vs-proxy conflict the user flagged:

- Guest speaks a file-operation interface to the broker, NOT the object-store
  protocol. The broker is the object-store client and signs its own backend
  requests, so no middlebox ever rewrites a request signature. (Anthropic's
  rclone-filestore is a custom anthropic.filestore.v1alpha Connect-RPC fork,
  not a stock S3 mount; Daytona runs the same runner-is-S3-client shape.)
- The broker's backend leg traverses the Egress trust-edge in allow-list-only
  mode (no TLS termination), so the signature stays intact while all outbound
  still passes the single audited egress (NFR-SEC-16). A direct broker→backend
  dial bypassing the edge is forbidden.
- Content inspection / DLP runs at the broker on plaintext, before signing —
  not at the edge, which sees only ciphertext on this leg.
- Backend credential is STS-scoped per session to the prefix the filesystem_id
  names, held by the broker, never the guest.

Fixed the .mmd PROXY→OBJ edge (was mislabeled with the LLM custody-injection
semantics; the object-store leg is the broker's, broker-signed, allow-list-only).

Reconciled docs/future-architecture/architecture/06-storage.md: removed the
STS-token-in-guest / stock-rclone-as-target drift; stock rclone→S3 is now
labeled interim-PoC-only, broker model is the target.

The allow-list-only egress routing of the broker's backend leg is OUR design
choice, not observed Anthropic behavior (their broker→cloud controls were not
in the sources).

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Move research notes that belong in the local working buffer out of the
  tracked tree (gitignore + git rm); the canonical architecture under
  docs/architecture/ is the deliverable. Competitor, market, and audience
  research stays tracked. The research/ directory is not ignored wholesale —
  only the local-buffer files, by glob.
- Tidy the future-architecture design docs: drop stale source-citations and
  third-party internal identifiers, link only to surviving files, and state
  each design fact directly. Technical content is unchanged.
- Add two doc-discipline rules to CLAUDE.md: state the result rather than the
  process that produced it, and let each fact live in one place.
- Untrack two working-buffer files that predate the local-only ignore rules.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-43 (#190)

The control / exec channel stays off any network the guest can reach: the host
opens it and the guest listens, over vsock (microVM) or a host-side unix socket
(gVisor / runc); a TCP listener rejects loopback and own-interface sources so
guest code cannot dial the supervisor through its own stack. Every host-facing
call carries an identity the host derives itself — hypervisor context id, kernel
peer credentials of the per-session sandbox principal, or a per-session socket
path the guest cannot enumerate — never an identity the guest supplies. A
guest-out reverse dial to a host-side bridge is a fallback only where a
host-reachable guest listener is unavailable; that bridge holds no credential,
runs unprivileged and syscall-confined, and authenticates before any privileged
action.

Lands as the §4 invariant in 02-trust-boundaries and NFR-SEC-43. Honest
residual: shared-kernel tiers share the host kernel, so escape surface and DoS
containment are weaker than the microVM's hypervisor boundary — microVM stays
the isolation ceiling.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Yambr and others added 21 commits May 30, 2026 23:58
#196)

* docs(architecture): Layer 7 — STRIDE threat model on the container DFD

STRIDE-per-element over the Layer 6 DFD (7 containers, 2 data stores,
4 external actors, 11 boundary flows). The in-sandbox process is the
primary adversary, so the high-value rows weight the sandbox's outbound
and host-facing edges. Each threat resolves to a canon NFR or routes to
a tracked open hole; severity is qualitative Likelihood x Impact.

The twenty OPEN/residual rows map to existing security issues
(#149, #176, #181-188); the snapshot-at-rest and guest-self-audit gaps
surface as OPEN, not silently mitigated. The machine-checkable Threagile
layer is deferred to #194; a LINDDUN privacy pass to #195.

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

* docs(architecture): Layer 7 review fixes — CI slop, cross-layer NFR, open-question routing

- Rephrase two "is the only" superlatives the slop detector flagged (P6-T1, P6-E1).
- P5-D1: credit NFR-SEC-14 for the per-container PID ceiling it already mandates; narrow the #188 residual to disk quota + deterministic OOM scoping.
- Route four edge-integrity open questions to issues: edge binary/config attestation, no-credential-in-response, MITM-plaintext zeroization (#197), and transparent-mode SNI/Host consistency (#198).
- P3-I1: drop the out-of-scope NFR-SEC-32 citation (it disclaims guest-VM memory, not host custody-process memory).
- P2-S2: name the gateway's generic internal token class (NFR-SEC-23).

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(architecture): NFR-SEC-44..60 — close Layer 7 open holes

Adds 17 security NFRs adjudicated from the Layer 7 STRIDE residuals
(the OPEN holes and the PARTIAL residual tails). Each row is the
measurable control for a threat the model left uncovered.

Cheap fixes (close a tracked hole with a bounded control):
- SEC-44 snapshot-token-exclusion (#184), SEC-45 operator-action-audit (#186),
  SEC-46 resource-exhaustion-containment (#188), SEC-51 mcp-param-schema,
  SEC-52 cp-split-reachability, SEC-53 gateway-conn-ceiling.

New cross-component controls:
- SEC-47 guest-self-audit (#181), SEC-48 trusted-time (#185),
  SEC-49 per-action-authz (#187), SEC-50 mtls-upstreams (#176),
  SEC-54 mount-erase-order, SEC-55 killswitch-under-dos,
  SEC-56 audit-ingest-fairness.

Accept-with-tier (a measurable tier target plus a named, formally
accepted residual):
- SEC-57 egress-exfil-tripwire (#182), SEC-58 side-channel (#183),
  SEC-59 host-custody-memory (host-foothold only), SEC-60 minimal-storage-floor.

Threat-model anchor repointing (the live rows citing "none") lands with
the model restructure, not here.

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

* docs(architecture): NFR review fixes — SEC-44 timing, SEC-45/39 cross-ref, measurability

Applies the duplication / contradiction / slop review findings:
- SEC-44: the ≤1 min revoke bound applied to all token classes was wrong for
  the Session JWT (host-side JWT revoke is ≤5 min per SEC-04). Restate: the
  pre-snapshot JWT is invalid at resume by construction; only the custody/
  storage lease carries the ≤1 min (SEC-29) replay bound.
- SEC-45 ⊃ SEC-39: add a bidirectional cross-reference so the operator-action
  audit set and the tier-downgrade alarm do not read as duplicate controls.
- SEC-46: bind the "≤10% degradation" target to a named SLI (p99-latency
  regression vs the NFR-PERF baseline).
- SEC-58: drop the "documented in datasheet" clause from the Target (a
  deliverable, not a measurable bar).
- SEC-57: drop "zero reliance on payload inspection" from the Target (a design
  constraint already stated in the Scenario).
- SEC-56 / SEC-49: add one disambiguating clause each (vs SEC-46 data-plane
  limits; vs SEC-51 structural validation).

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

* docs(architecture): SEC-44 — clean-before-stop primary; fix SEC-38/39 source refs

A snapshot image is a verbatim copy of guest RAM + disk, so a secret live at
snapshot time is frozen into a file that can be copied and booted where the
resume hook never runs. Host-side revocation makes such a token useless but
not absent from the image — two different controls the prior text conflated.

Flip SEC-44's primacy: the load-bearing, testable control is that no
session-scoped secret is present at snapshot-create time (image taken at
minimal-init, session material hot-swapped at restore; verified by offline
extraction finding none). Resume-side re-auth + VMGenID reseed + N-fork
uniqueness become defence-in-depth. Public anchors: Firecracker snapshot
docs, VMGenID spec, arXiv 2102.12892, NIST SP 800-190 §4.5.

Also fix the SEC-38/SEC-39 Source cells: "this PR" was a placeholder that
broke once #163 merged; repoint to #163 (their origin).

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(architecture): NFR-SEC-61..72 — sandbox lifecycle hardening

Twelve NFRs closing the lifecycle gaps surfaced alongside the SEC-44
rewrite. They cluster on three transitions the model under-specified:
snapshot/resume, crash, and warm-pool reuse — and on the container tiers,
which get no VM-death scrub for free.

- SEC-61 snapshot-artifact-confidentiality (encrypt+authenticate the image)
- SEC-62 single-resume / N-fork uniqueness
- SEC-63 resume-clock-correction (correct CLOCK_REALTIME before TTL checks)
- SEC-64 no-cross-session-cache-residue
- SEC-65 container-tier-teardown-order (host-driven finalizer for runc/gVisor)
- SEC-66 pre-freeze-buffer-zeroize
- SEC-67 crash-state-sanitize
- SEC-68 single-use-guest (a guest that ran a session is destroyed, not pooled)
- SEC-69 claim-time-reauth (warm-pool claim re-derives identity)
- SEC-70 clean-before-pool
- SEC-71 guest-identity-regen (boot_id/machine-id on boot-from-snapshot)
- SEC-72 lifecycle-audit-events (audit per security-bearing transition)

Public anchors only (Firecracker snapshot/clone docs, VMGenID spec,
arXiv 2102.12892, NIST SP 800-190 §4.5); the rest labelled OUR-DESIGN.
Threat-model M-3 expansion lands separately, on the restructured model (#199).

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

* docs(architecture): apply quad-review fixes to the lifecycle NFRs

Blockers:
- SEC-65: replace phantom token names ("egress JWT", "broker lease") with
  canon — session JWT, custody credential lease, network-bound egress route
  (NFR-SEC-27); the §8 taxonomy has no egress JWT.
- SEC-62: cut the N-fork invariant it restated from SEC-44; carry only the
  single-restore guard and point to SEC-44 for fork uniqueness.
- SEC-47: replace the undefined "tier-2+" with the named gVisor/microVM tiers.

Cross-layer / dedup:
- SEC-50: state it is MITM-mode only (§7) — transparent pass-through cannot
  attach an upstream credential.
- SEC-64: name the boundary vs SEC-54 (persistent substrate) and SEC-13 (DEK).
- SEC-71: name the fork-uniqueness family (SEC-44 token/nonce/RNG, SEC-62
  single-restore, SEC-71 guest unique IDs).
- SEC-72/SEC-45: split the audit set by initiator (operator → SEC-45,
  system lifecycle → SEC-72) so the two fixtures neither overlap nor gap.
- SEC-70: re-point the warm-pool pairing (pre-claim vs claim-time vs single-use).

Slop:
- SEC-65: ordering property kept, the six-step arrow list demoted to a gloss.
- SEC-46: drop the per-breach audit clause (SEC-72 owns it).
- SEC-44: drop the "(defence-in-depth, not the proof)" hedge.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(architecture): Layer 7 — actor model + two-tier threat table

Restructure 06-threat-model.md so the STRIDE findings read as a curated
security artifact rather than one 79-row table, following the CNCF /
Microsoft-TMT / OWASP convention.

- §1 declares three threat actors (A1 in-sandbox guest = primary adversary,
  A2 external caller, A3 host-foothold) once; rows reference them by ID. An
  A3 threat is conditional on a prior escape (derives from NFR-SEC-02) or
  sits behind the trusted-host assumption, not a guest-isolation failure.
- §2 element×STRIDE applicability is the completeness proof.
- §3 Live threats carries only OPEN/PARTIAL, split by actor: §3.1 reachable
  by the guest (the primary adversary), §3.2 reachable only off a host
  foothold or external position.
- §4 Covered threats lists MITIGATED as a terse ID→controlling-NFR receipt.
- Drop the L and I columns; Rating already encodes Likelihood × Impact.

No threat content changed; rows are reclassified by actor and regrouped.

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

* docs(architecture): re-anchor threat-model rows to SEC-44..72; honest residual register

After the lifecycle NFRs landed (#200/#201), 20 live rows that read
"no canon NFR" now have a committed control. Re-anchor each from `none`
to its NFR and move it OPEN→PARTIAL — the control is specified, not yet
built, so it is not MITIGATED.

- §3: 20 rows re-anchored (snapshot→SEC-44/61/66, guest-audit→SEC-47,
  trusted-time→SEC-48/63, operator-audit→SEC-45, resource→SEC-46/53,
  exfil→SEC-57, per-action→SEC-49, mTLS→SEC-50, side-channel→SEC-58);
  residual restated as "committed, implementation pending". Only P1-I1
  (#149) stays genuinely OPEN — no NFR yet.
- §5: residual register reworked — one truly-uncovered row plus a
  theme→controlling-NFR table, so the picture reads "control specified,
  not built" rather than "uncovered".
- diagram: split the OPEN node into OPEN (P1-I1 only) and a
  committed-NFR-pending node.
- slop: drop the "PRIMARY ADVERSARY" caps tags (the §3.1 heading already
  establishes A1 as primary); collapse the §1 STRIDE-applicability stack
  that restated the §2 table to a pointer.

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

* docs(architecture): fix #199 review findings — mitigation/anchor consistency

Cross-layer + defect review of the re-anchored model found the bulk
re-anchor updated Anchor/Residual/Status but left the Mitigation cell
stale on 11 rows, and overlooked two more-precise NFRs.

- B1: reword the Mitigation cell on 11 rows from "no NFR / not yet canon"
  to "NFR-SEC-NN specifies the control; implementation pending" (P5-I1,
  P4-D1, P7-S1, P7-R2, P3-R1, P7-T2, P7-T3, P1-E2, P3-E1, P5-T1, P5-D1).
- B2: re-anchor P2-E1 → SEC-52 (CI IaC gateway↛operator assertion, the
  exact control its residual said was missing) and P2-D1 → SEC-55
  (kill-switch ≤30s under control-plane DoS).
- C1: §5 theme table — add the missing rows (P2-T2/P5-R1 to trusted-time,
  P2-R2 to operator-audit, the full #188/#150 DoS set to resource).
- C2: diagram PEND node — add #148/#150.
- C3: P1-D1 — add SEC-53 (per-caller connection ceiling).
- S1: §5 wording "by their controlling NFR" → "grouped by theme".

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(architecture): drop "commitment / lands-with" self-narration from NFRs and threat model

A control row states a property, not a roadmap. Remove the
"Commitment; implementation lands with <X> code" tails (NFR-SEC-38/39)
and reword the threat-model residual/mitigation cells from "is committed
by NFR-NN but not yet implemented" to "specified by NFR-NN". The PARTIAL
status already carries "not built"; the prose no longer restates it.

Kept: "events committed locally" in the durable-bus mitigation (a
transactional term, not narration).

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

* docs(architecture): collapse double-space left by the narration strip (SEC-38/39)

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…aps (#203)

* docs(architecture): NFR-SEC-73..77 + amendments — close the 8 reference-coverage gaps

Reference review (Anthropic-first) of the threat model surfaced eight
controls a mature sandbox runtime carries that ours did not name. This
adds them and re-anchors the affected threat rows; after it, no threat
row is genuinely open.

New NFRs:
- SEC-73 downloadable axis — storage authz gains a third axis {scope,
  intent, downloadable}; a non-downloadable object is readable in-session
  but yields no egress-eligible artifact, and the tag denies at the edge.
- SEC-74 exec-channel stdout/stderr bound (framed transport + byte
  counter + truncation marker).
- SEC-75 host-side env/argv secret-scrub at spawn (allowlist env, never
  secrets on argv).
- SEC-76 reject-non-host-source at the broker/control listener +
  broker-per-tenant for multi-tenant (accept-time enforcement of SEC-43).
- SEC-77 supervisor-death → VM-death (microVM tier; container tiers
  accept the routed-orphan residual closed by the SEC-65 finalizer).

Amendments: SEC-46 (broker max-object/message/mandatory-chunk, closes
P4-T1/P4-D1), SEC-12 (pin control/broker endpoints host-side, proxy
resolver sole egress authority), SEC-51 (bound + id-minimize MCP
error/discovery, closes P1-I1 / #149 — the last open threat row).

Layer 3 §2 zone 3 gains the downloadable-axis sentence. Threat model:
P1-I1 OPEN→PARTIAL, new P5-I4 (spawn-time secret leak), and SEC-73/74/76
anchors added to the affected P4/P6/P2/P5 rows. §5 residual register and
the overlay diagram updated — zero OPEN rows remain.

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

* docs(architecture): #203 review nits — tighten SEC-74 anchor, single-theme P6-I1

- P2-D1: drop the loose SEC-74 cite (its vector is session-create flood,
  not stdout amplification); SEC-74 stays on P5-D1, the exec-channel row.
- §5 register: P6-I1 lists once under Content-blind egress (its primary
  SEC-57 theme); the SEC-73 strengthening is already noted in its §3 row.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + embeddable UI (#213)

Measurable NFRs the file surface and its embeddable UI need, that the
per-sandbox rows did not cover:
- NFR-SEC-78: north-side inbound byte path (per-request body ceiling,
  pre-buffer reject, per-caller rate) — distinct from per-sandbox SEC-46.
- NFR-SEC-79: OCSF File System Activity audit on both broker faces,
  fail-closed.
- NFR-SEC-80: pre-extraction archive validation (decompression bomb,
  path traversal, symlink escape, entry-count).
- NFR-SEC-81: content classification on ingest (classify-and-record
  default + per-scope policy-deny).
- NFR-SEC-82: embeddable-UI authentication (signed short-TTL OIDC embed
  token, cross-origin without re-login).
- NFR-SEC-83: iframe-embedding headers (CSP frame-ancestors allowlist).
- NFR-SEC-84: first-party session cookie + CSRF for the embedded UI.

Extend NFR-SEC-49 with the three-value intent enum (read/write/preview);
align NFR-SEC-73 and trust-boundaries zone-3; reword NFR-IC-02 to scope
the UI non-goal to the operator/control-plane console (data-plane
preview/render UI is in scope per NFR-SEC-82).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… serves its own SPA) (#214)

Add a Data-plane client actor to the C4 Context: it reaches OCU's own
authenticated SPA — file preview and artifact render — plus the headless
upload/list/download API. Bytes flow client↔OCU directly, never relayed
through a peer and never reaching the object store. The SPA is embeddable
cross-origin in a calling peer (NFR-SEC-82/83). Rendering this data-plane
surface is OCU's, not the calling client's.

Scope the UI non-goal to the operator/control-plane console; the
data-plane preview/render SPA is in scope. Browser/terminal live-view
(CDP/ttyd) is v2, deferred under #210 and host-proxied per NFR-SEC-43
when taken up; the machine-facing PTY+CDP WebSocket (NFR-IC-03) stays v1
machine-to-machine.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-language (#216)

The file/artifact + SPA/preview surface stays inside the Agent Execution
bounded context: it shares the session aggregate root and introduces a
delivery sub-language, not a new context. Extend the Storage-broker
sub-language clause with the north-face terms (artifact, preview,
embed-token, downloadable) and mark SPA-render as a generic component the
broker gates, not a built capability.

No new context, no table row, no diagram change, no ADR — mirrors the C4
verdict (broker north face; SPA is a component, not a separate container).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lient) (#215)

The Storage broker is one object-store client with two faces: the guest
mount (south, filesystem_id-scoped file operations) and the data-plane
client face (north — OCU's authenticated SPA, the file/artifact API
upload/list/download, and preview-render). Both run as components inside
the one broker container, not a separate one. Both faces share the one
backend credential and the one egress backend leg; the broker is the only
object-store client (NFR-SEC-25), so one consistency view covers both and
the downloadable axis (NFR-SEC-73) resolves at read on either face. The
north face verifies the embed token and sets a first-party session
(NFR-SEC-82, NFR-SEC-83). Neither guest nor data-plane client holds a
backend credential.

The §4 boundary row carries the data-plane client → broker (north) edge,
inbound and caller-side: host-side caller to host-side broker, so the
host-dials-guest invariant (NFR-SEC-43) is unaffected. The container
diagram keeps the data-plane client as an external actor and the north
face as a face of the broker container — no eighth container.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#219)

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>
…-78/79 (#220)

The C4 layers (03/04/05), Layer 5 bounded contexts, and the Layer 7 threat
model all name the north-side file-artifact actor "Data-plane client".
NFR-SEC-78/79 still carried the older "File-client" term. Align the two
NFR rows to the one name; no semantic change.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…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>
…rs (#221)

* docs(architecture): Layer 10 — component specs for the seven containers

Add a spec per Layer 6 container under components/, plus the directory
index (00-overview) and a glossary entry for the Generic internal token.

Each spec follows the fixed section order (Purpose, Boundaries,
Invariants, Failure modes, Operational concerns, Open questions) and adds
only the intra-container design the layer docs do not already own:

- 01 MCP gateway, 02 Control/operator API, 03 Credential custody,
  04 Storage broker, 05 Session sandbox, 06 Egress trust-edge,
  07 Audit pipeline.

Failure-mode rows trace to the Layer 7 STRIDE rows and repeat each row's
controlling NFR; every container's PARTIAL rows have a home. Invariants
are stated test-enforceably against a real NFR anchor. Build-vs-buy
(storage engine) and runtime-tier remain future ADRs — the specs name
them by role and carry them as open decisions. The 04 two-face split is
an inline component diagram; the broker mount-remanence row anchors to
NFR-SEC-54/13/64 (erase-before-reuse, per-session DEK, cache residue).

00-overview records the spec slot, status, and bound contracts per
container; ADR cells stay empty until Layer 9 produces them.

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

* docs(architecture): fix L10 CI — trailing newlines + drop superlative

markdownlint MD047 on four specs (missing final newline) and the AI-slop
superlative rule on the sandbox egress-route line ("is the only path").

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

* docs(architecture): drop residual superlative in 05 sandbox flow table

CodeRabbit flagged 'the one outbound network leg' in the edge-flow row;
the direction is already carried by the 'guest -> edge' column. The
measurable referent (sole egress, invariant 4) stays in the rationale cell.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(architecture): manifesto §05 — licensing posture (FSL + dep gate + reject table)

Adds the licensing-posture manifesto doc that CLAUDE.md already references
for the Bill of Materials and the rejection table. States the FSL-1.1
terms and the per-release two-year Apache-2.0 conversion, the licence +
supply-chain gates a dependency passes, the bundled-vs-not-bundled rule,
and the seven rejected dependencies with the path taken instead.

The Bill of Materials table is not pre-populated: a row lands when the ADR
or component spec that adopts a dependency lands, so the bundled call is
made with the rationale that needs it. This unblocks the licence-evidence
the L9 ADR backlog cites.

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

* docs(architecture): drop process-narration in §05 reject-table preamble

doc-slop-reviewer flagged 'Licence facts were checked … on 2026-06-01' as
narrating the authoring step rather than stating a property. The forward
constraint (the adopting ADR re-verifies before citing a row) carries the
load; the snapshot date does not.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The data-plane UI discovers its views from a session-scoped descriptor
list rather than a hardcoded tab set, so the deferred live-session views
(browser, terminal, #210) are additive entries, not a breaking change.
v1 returns one descriptor (files); the endpoint returns three when #210
lands and an old shell renders the subset it recognizes.

Per-surface authentication (no panel-wide credential) and a host-side-only
descriptor entry.url (NFR-SEC-43) close the PoC's single-chat_id and
direct-guest-reachability weaknesses. Indexed in adr/README.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ratifies the workload-trust selection axis the §02 NFRs already encode and
closes the v1/post-v1 cut: tier is selected by workload_trust_profile
(runc for trusted_operator, gVisor for internal_workforce), microVM
deferred post-v1, never by data classification (AP-13). The ADR references
NFR-SEC-02/38/39 rather than restating them.

As the adopting ADR for runc and gVisor, both enter the §05 Bill of
Materials as bundled (Apache-2.0). Enterprise audit sinks/alarms stay
opt-in for the solo default; the local audit emit is mandatory in code.

Sets components/05 adr:[0003], links 00-overview and the ADR index.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eep (#230)

* docs(lint): gate 'bank' as audience framing + sweep canon to 'regulated enterprise'

The 'bank only as a named example, default term regulated enterprise' rule
lived only in prose, so every new doc re-introduced bank-as-framing and it
was caught by eye, not CI. Adds an ai-slop-detector check (#15) that flags
framing tells (the bank's, targets banks, bank-required/grade, bank InfoSec/
CISO/reviewer) while allowlisting the tier-1 US/EU bank named example, with
a self-test fixture pair.

Sweeps the existing framing hits to 'regulated enterprise': CLAUDE.md
(targets banks; bank auditor), PROCESS.md, ADR-0003, manifesto/05.

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

* docs(lint): OIDC-only identity surface — gate SAML + sweep canon

OCU's identity surface is OIDC; a SAML-only customer IdP federates in through
Dex or Keycloak, never an OCU SAML endpoint. The rule lived nowhere, so SAML
kept appearing as a co-equal platform surface. Adds ai-slop-detector check #16
(SAML asserted as a surface fails; a federation/fallback clause passes) with a
self-test fixture pair, and sweeps the 9 canon mentions: NFR-SEC-09, NFR-FLEX-03,
NFR-COMP-29, and the 02/03/04/08/primitives docs.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(architecture): ADR-0004 — operator authentication substrate

Closes the operator-auth needs-ADR in component 02: minimal shelf = host-rooted
local credential + signature-verified signed-webhook; full shelf = OCU as
relying-party to the customer IdP (OIDC+SCIM, PAM-JIT) + SPIFFE SVID for SOAR.
Drops dual-control and break-glass — the kill switch is already the single-operator
emergency path and NFR-SEC-45 already carries accountability; multi-party approval
is a post-v1 policy seam (#225). License: relying-party, nothing bundled
(Teleport AGPL / Boundary BUSL rejected).

Sets component 02 adr:[0004] and the ADR index row.

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

* docs(architecture): drop 'only' superlative in ADR-0004 context

AI-slop detector flags 'is the only / unique' without a measurable referent.
'the privileged plane' carries the same meaning; the reachability claims
(kill switch, denylist, tier admission) are the referent.

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

* docs(architecture): ADR-0004 — 'bank' framing → regulated enterprise

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

* docs(architecture): ADR-0004 — OIDC-only, SAML via federation only

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… SDS (#231)

Excise the Credential custody service from the canon. Egress credential
delivery rides Envoy SDS + credential_injector: a static file on the solo
shelf, a customer-provided SDS-compatible store on the enterprise shelf.
OCU stores, mints, and rotates nothing; the source owns the lifecycle.

The NFR-SEC-23 invariant holds — the upstream secret never enters the
guest; it is attached only at the egress boundary.

Trust zones 6 -> 5, containers 7 -> 6: the Egress trust-edge carries the
boundary-injection property. The bespoke F8 lease-pull protocol, the STS
delegator, and the OCU-enforced TTL/revoke bounds are removed.

- ADR-0005: egress credential delivery decision (replaces the F8/STS draft)
- delete components/03 credential-custody; rewrite components/06 egress edge
- 05 container, 02 trust-boundaries (+ .mmd), 06 threat-model: drop P3/D1/F8,
  renumber zones/containers/flows
- 08 contracts: drop lease-pull row (#205 re-scoped to session set-up)
- 02-nfrs: reframe SEC-23/25/27/29/31/59 to the SDS source; SEC-45/48/49/50/65/77
- BoM: add Envoy (Apache-2.0); diagrams c4-container/threat-model/contracts
- deferred: credential_injector/OAuth2 maturity caveat (components/06 open question)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(architecture): ADR-0006 — egress forward-proxy substrate

Closes the forward-proxy half of egress open-Q#2: one Apache-2.0/MIT/BSD forward
proxy (Envoy lead candidate) scoped to the v1 deny-by-default floor — allow-list
on resolved-IP+SNI, proxy-owned resolver with the mandatory deny-set,
x-deny-reason, one ext_authz seam (static allow-list default, OPA deferred), a
per-upstream-leg origination hook. No CA/SDS/ICAP/xDS on the transparent default
path. MITM-termination is a separate deferred ADR.

Adds Envoy to the §05 Bill of Materials (Apache-2.0, bundled); sets component 06
adr:[0006] and narrows its open-Q#2 to MITM-termination only.

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

* docs(architecture): ADR-0006 — 'bank' framing → regulated enterprise

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Comment retired during the initial-public-release history consolidation.

…rotocol-broker (#234)

Select the egress credential-attachment mechanism by upstream: edge-inject
(fixed-client bearer, e.g. an LLM API) or protocol-broker (high-value
scoped-by-rights credential, e.g. a PAT or object store). v1 ships edge-inject
only; the protocol-broker mechanism is the existing Storage-broker zone,
deferred for other upstreams.

Egress posture becomes a ladder by need (deny-all / transparent pass-through /
egress-wide bump / external SDS source) rather than a two-mode switch. Bump is
the default only when an upstream credential is configured, so the one-click
solo path holds at every rung; the per-deployment CA is auto-generated and its
public certificate auto-injected into the sandbox trust store at start. The
bump substrate is the Envoy data plane plus a self-hosted SDS minting service
for per-SNI leaves; a config-time-enumerable allow-list uses pre-minted leaves
over a file SDS source instead. mitmproxy/Squid are rejected-as-default for
dropping the Envoy data plane.

Injection is gated on a scoped credential the request presents, never on
network origin (tightens P6-E2). The guest holds no long-lived upstream secret
but may carry a short-lived session-scoped handle (corrects the prior
categorical "guest holds nothing").

Propagated across §7, NFRs (FLEX-15, SEC-05/17/23/27/37/50/57/73), the threat
model, component-06, glossary, c4 container/context, contracts, the trust-zone
and threat-model diagrams, ADR-0005/0006 forward-refs, and the BoM.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Yambr

Yambr commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator Author

Closing: this PR's diff is the entire next/v1 branch against main (274 files, +8769/-1959), not the single credential-broker doc the title describes — it was opened with the wrong base/head. The credential-broker design it intended to distill is now superseded by merged ADR-0005 (egress credential delivery) and ADR-0007 (egress auth mechanism), which fold custody into the egress-edge + storage-broker and remove the standalone custody process (threat-model: P3/D1 removed by ADR-0005). Not mergeable and not current.

@Yambr Yambr closed this Jun 2, 2026
@Yambr

Yambr commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator Author

Reopened — closed prematurely before the author reviewed it. No action taken; left for the author to decide.

@Yambr Yambr reopened this Jun 2, 2026
…se (#236)

Three-lens review fixes (the bounded, uncontested ones):

- components/00-overview.md — the index showed component-02 bound ADRs
  as "—", but its spec front-matter is `adr: [0004]`; the index now
  cites ADR-0004.
- components/04-storage-broker.md — the `contract:` front-matter listed
  only mount-config, but the spec binds three storage contracts
  (mount-config, file-ops, file-artifact-api), all present; the field
  now lists all three.
- 02-trust-boundaries.md — drop the "(consolidated to satisfy the
  CLAUDE.md ≤ 3-links rule)" parenthetical; the section shows the
  consolidation, the reason is not a fact about the content.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Yambr

Yambr commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator Author

Closing: the diff is the entire next/v1 branch against main (274 files, +8769/-1959), not the single credential-broker doc the title describes — wrong base/head. The credential-broker design it intended to distill is superseded by merged ADR-0005 and ADR-0007 (custody folds into the egress-edge + storage-broker; threat-model records P3/D1 as removed by ADR-0005). Not mergeable, not current.

@Yambr Yambr closed this Jun 2, 2026
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