Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,22 @@ sequenceDiagram
- Rust remains the psychometric arithmetic owner. Repair never substitutes
Python for scoring math.

## Object-storage governance (2026-08-16)

Central `.github` publishes a provider-neutral `object_storage` contract.
Naruon and other products keep their own adapters. The executable check is
`scripts/ci/validate_object_storage_contract.py`. HTTPS, exact-host
allowlists, tenant-purpose binding, server-side encryption, SHA-256-or-stronger
integrity, distinct lifecycle states, and non-destructive rollback are
required. Denied private-network trust also rejects special-use internal
and Kubernetes `.svc` suffixes. DNS pinning is mandatory; rebinding helper
suffixes and embedded or hyphenated IPv4 or 32-bit numeric aliases are never allowlist
members. CSAP and SOC 2 remain design
constraints, not certification claims.
Operational PII is not blanket-masked. Product adapters prove write/read/delete
timeout and partial-upload behavior with
`docs/object-storage/PRODUCT_ACCEPTANCE_TEMPLATE.md`.

## Quality gates

`scripts/ci/` ships with 100% statement/branch coverage and 100% docstrings.
Expand Down
12 changes: 9 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Semantic Versioning where the repository publishes a release.
- Added a dedicated Quarantine Sandbox Runtime hourly caller at minute 14 that targets protected `develop`, dispatches at most one exact-head repair, applies a two-hour same-head retry floor, preserves non-cancelling single-flight execution, and maps only the established scheduler credentials with job-scoped OIDC.
- Added a dedicated Quarantine Sandbox Runtime hourly caller at minute 14 that targets protected `develop`, dispatches at most one exact-head repair, applies a two-hour same-head retry floor, preserves non-cancelling single-flight execution, and maps only the established scheduler credentials with job-scoped OIDC.
- Added a dedicated OriginWeave hourly caller that invokes the product-neutral central scheduler with the exact repository, protected `main` branch, one-dispatch budget, two-hour same-head retry floor, non-cancelling single-flight heartbeat, job-scoped OIDC, and only the established scheduler credentials.
- Added a provider-neutral object-storage contract with an executable fail-closed validator, JSON Schema, naruon-shaped example, and APA 7 doctoring so AWS S3 and S3-compatible endpoints share one HTTPS, exact-host, integrity, retention, and rollback policy (ContextualWisdomLab/.github#1019, ContextualWisdomLab/naruon#1364).
- Added a trusted pull-request comment router for `@cwl-noema-review` and review-only `@opencode-agent` dispatches, with an organization sweep, exact-head receipts, repository allowlisting, fixed runners, immutable checkout pins, and a permanent 100% statement/branch/docstring quality gate.
- Added exact-base `uv.lock` materialization that reconstructs standalone nested projects with a checksum-pinned official `uv` exporter, isolated frozen/offline execution, strict exact-pin and SHA-256 output validation, and complete Python 3.10/3.14 quality evidence.
- Added a permanent exact-head contract workflow for the hourly review-repair scheduler, immutable reusable-workflow source, NVIDIA NIM model boundary, credential isolation, and fail-closed unattended-agent permissions.
Expand All @@ -28,9 +29,6 @@ Semantic Versioning where the repository publishes a release.
- Run the bounded fast-mlsirm repair heartbeat at minute 49 of every hour with one-dispatch scope and a two-hour same-head floor, without weakening true-parameter recovery, CPU/GPU parity, skipped-test, or Rust-ownership gates.
- Use NVIDIA NIM `mistralai/mistral-small-4-119b-2603` with explicit high reasoning for scheduled repair and `nvidia/nemotron-3-nano-30b-a3b` for bounded helper work instead of GitHub Models in the write-capable autofix worker.
- Apply one NUL-delimited exact-path and complete pre/post-worktree verification contract to both ordinary review repair and merge-conflict repair rather than relying on a visible post-model diff for the ordinary path.

### Changed

- Avoided the expensive R/testthat failure-summary regular expression on marker-absent bounded logs by checking the required terminal marker first, while preserving fail-closed handling for incomplete or malformed failure evidence.

### Fixed
Expand All @@ -39,6 +37,14 @@ Semantic Versioning where the repository publishes a release.
- Download the pinned `uv` 0.12.1 exporter from the official GitHub Releases URL instead of `releases.astral.sh`, which now returns HTTP 403 and blocks org-wide OpenCode `coverage-evidence`. The SHA-256 pin is unchanged. The opener may follow one hop onto `release-assets.githubusercontent.com` or `objects.githubusercontent.com` and still rejects every other host, userinfo, non-HTTPS scheme, and nondefault port (ContextualWisdomLab/.github#1109).
- Compared the trusted `uv` executable's post-install `--version` output against the real GitHub Releases build's full string, `uv 0.12.1 (x86_64-unknown-linux-gnu)`, instead of the bare `uv 0.12.1` the prior check required; the genuine release binary always prints the target triple, so every installation was failing the pin check immediately after the archive download itself was fixed (ContextualWisdomLab/.github#1109).
- Excluded relative `-r` and `--requirement` referrers from generated flat base-lock publication while retaining bounded include syntax diagnostics and discovering independently complete direct `.txt` children of `requirements` directories.
- Closed the object-storage contract around exact lowercase DNS hosts, TCP port range, finite RFC 8259 numbers, nested JSON Schema objects, and typed observability labels so hardening tests cannot pass a metadata, Unicode, or unhashable-label document.
- Removed the leftover one-shot object-storage repair workflow so Scorecard Token-Permissions no longer sees workflow-default `contents: write` after the hardening is already committed.
- Rejected decimal, hexadecimal, and dotted-numeric IPv4 aliases in object-storage host allowlists so Strix-proven metadata-service SSRF bypasses fail closed.
- Rejected multicast `.local`, metadata, and cluster-local names from object-storage allowlists, and rejected RFC 6761/6762 internal suffixes when private-network trust is denied, so a `denied` policy cannot admit `minio.internal`.
- Required tenant- and purpose-bound provider selection, aligned the portable JSON Schema with the executable value constraints, and added the product write/read/delete acceptance template so sibling adapters can prove compatibility without copying central code.
- Required DNS pinning, rejected DNS-rebinding helper suffixes and RFC 6761 `.test`/`.invalid` names from object-storage allowlists, rejected Kubernetes `.svc` and `.localdomain` hosts when private-network trust is denied, and encoded the consumed/zero-retention coupling in the portable schema so ContextualWisdomLab/.github#1019 SSRF/DNS-rebinding protection is executable.
- Rejected object-storage hosts that embed a dotted IPv4 address, a hyphenated `A-B-C-D` / `ip-A-B-C-D` label, or a 32-bit decimal/hexadecimal alias under an ordinary suffix, so `169.254.169.254.attacker.example` and `ip-169-254-169-254.ec2.internal` cannot bypass the helper-TLD denylist.
- Materialized base Python locks only when every package line is an exact SHA-256 pin or a bounded relative `-r`/`--requirement` include. A lone `--require-hashes` directive, a dotted include such as `./lock.txt`, or `-r other-hashes.txt` no longer enters the trusted build context.
- Refused a conflict-scope repository root whose immediate parent is a symbolic link, so a swapped parent cannot redirect the canonical worktree after the last-component check (CWE-367).
- Bounded the Strix quality self-test's deterministic timeout fixtures to 3-second process and 5-second fake-sleep budgets so exact-head policy evidence completes inside the existing job limit without changing production Strix scanner timeouts, providers, credentials, or review semantics.
- Allowed commas and ASCII parentheses in the bounded Strix changed-file path policy so legal tracked Packrat fixtures can receive exact-head security analysis, while rejecting raw `..` components before normalization and keeping controls, backslashes, whitespace ambiguity, and shell punctuation fail-closed.
Expand Down
122 changes: 122 additions & 0 deletions docs/doctoring/object-storage-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Object-storage contract: evidence and design record

검토 기준일: **2026-08-16**

## Incident / buyer-visible gap

Sibling products persist customer or evidence objects in AWS S3 or
S3-compatible stores. Without a central executable contract, each repository
can invent endpoint, credential, encryption, integrity, retention, and
rollback rules. That drift is a procurement and data-loss risk for buyers who
must keep documents, even when Naruon already implements a scoped adapter in
ContextualWisdomLab/naruon#1364.

This record does not implement Naruon storage. It records the organization
policy and the fail-closed check that leaf repositories can cite.

## Decision

1. Treat AWS S3 and S3-compatible HTTPS stores as one `object_storage`
capability.
2. Require HTTPS, exact-host allowlists, no wildcards, no automatic
redirects, and DNS pinning for the request lifetime (CWE-918; Jackson
et al., 2009). DNS-rebinding helper suffixes, RFC 6761 `.test` /
`.invalid` names, and hosts that embed a dotted IPv4 address or a 32-bit
numeric alias (for example `169.254.169.254.attacker.example` or
`169-254-169-254.attacker.example`) are never allowlist members. ASCII IDNA
`xn--` labels are also rejected because their decoded Unicode name can hide
the endpoint represented by the visible allowlist value.
3. Require explicit private-network trust. Implicit RFC1918, metadata-service,
multicast `.local` (Cheshire & Krochmal, 2013b), Kubernetes `.svc`
names (The Kubernetes Authors, n.d.), or special-use internal suffixes
(Cheshire & Krochmal, 2013a) are not authorized unless the exact host is
named after an explicit trust decision. Multicast `.local` names are
never unicast endpoints.
4. Require least-privilege object permissions and prohibit public ACLs, public
buckets, and browser-exposed long-lived credentials (CWE-798; CWE-200).
5. Require server-side encryption and fail-closed content-length plus SHA-256
or stronger read verification (Amazon Web Services, 2024a, 2024b).
6. Keep lifecycle states distinct. `consumed` does not imply immediate
deletion unless the product explicitly configures zero retention. Legal hold
and archive remain separate from transient reprocessing retention.
7. Refuse rollback that deletes customer data after a partial
migration or backfill.
8. Forbid bucket names, object keys, credentials, and raw PII as unbounded
telemetry labels. This is not a blanket operational PII mask.
9. Record CSAP and SOC 2 only as design constraints. The contract must not
claim certification.
10. Require tenant- and purpose-bound provider selection so one credential or
bucket cannot serve unrelated tenants or jobs.
11. Ship an executable validator and a product acceptance template. Prose
alone does not close ContextualWisdomLab/.github#1019.

## Trust boundary

- Central `.github` owns the schema, policy, doctoring record, and validator.
- Product repositories own adapters, buckets, keys, credentials, and evidence
objects.
- NVIDIA NIM / OpenCode credentials are untouched.
- The validator never prints bucket names, object keys, or secrets from a
failing document beyond the field that violated the closed policy.

## Verification contract

`tests/test_object_storage_contract.py` proves the checked-in example passes,
the schema keys match production constants, and each fail-closed control has a
unique rejection. `tests/test_object_storage_contract_hardening.py` proves
nested schema objects stay closed, NaN/Infinity are rejected, exact-host
allowlists exclude localhost, metadata, cluster-local names, IPv4 and IPv6
literals, decimal or hexadecimal IP aliases, Unicode and punycode IDNA labels,
case aliases, and
multicast `.local` names, a denied private-network policy rejects
single-label, Kubernetes `.svc`, and special-use internal hosts, custom
endpoints reject ports above 65535, schema value constraints match the
validator including the consumed/zero-retention coupling, tenant-purpose
binding and DNS pinning are mandatory, DNS-rebinding helper suffixes and
embedded or hyphenated IPv4 or 32-bit numeric aliases stay off the allowlist, and
malformed observability labels raise policy errors
instead of TypeError. Local quality remains 100% statement/branch coverage
and 100% docstrings.

## Rollback

Revert the validator, schema, example, policy, and this record together. Do
not keep a schema that the executable check no longer enforces.

## References (APA 7th)

Jackson, C., Barth, A., Bortz, A., Shao, W., & Boneh, D. (2009). Protecting
browsers from DNS rebinding attacks. *ACM Transactions on the Web, 3*(1),
Article 2, 1–26. https://doi.org/10.1145/1462148.1462150

The Kubernetes Authors. (n.d.). *DNS for Services and Pods*. Kubernetes
Documentation.
https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/

Cheshire, S., & Krochmal, M. (2013a). *Special-use domain names* (RFC 6761).
Internet Engineering Task Force. https://www.rfc-editor.org/rfc/rfc6761

Cheshire, S., & Krochmal, M. (2013b). *Multicast DNS* (RFC 6762). Internet
Engineering Task Force. https://www.rfc-editor.org/rfc/rfc6762

Amazon Web Services. (2024a). *Checking object integrity for data uploads in
Amazon S3*. Amazon Simple Storage Service User Guide.
https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity-upload.html

Amazon Web Services. (2024b). *Using server-side encryption with AWS KMS keys
(SSE-KMS)*. Amazon Simple Storage Service User Guide.
https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html

Amazon Web Services. (n.d.). *Authenticating requests (AWS Signature Version
4)*. Amazon Simple Storage Service API Reference.
https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html

MITRE. (n.d.-a). *CWE-200: Exposure of sensitive information to an
unauthorized actor*. CWE List.
https://cwe.mitre.org/data/definitions/200.html

MITRE. (n.d.-b). *CWE-798: Use of hard-coded credentials*. CWE List.
https://cwe.mitre.org/data/definitions/798.html

MITRE. (n.d.-c). *CWE-918: Server-side request forgery (SSRF)*. CWE List.
https://cwe.mitre.org/data/definitions/918.html
82 changes: 82 additions & 0 deletions docs/object-storage/CWL_OBJECT_STORAGE_CONTRACT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# CWL object-storage contract

This repository owns the reusable `object_storage` policy and the executable
check `scripts/ci/validate_object_storage_contract.py`. Product repositories
keep their own adapters, databases, and evidence objects. Naruon
[ContextualWisdomLab/naruon#1364](https://github.com/ContextualWisdomLab/naruon/pull/1364)
is the current concrete consumer and remains the owner of its document-storage
implementation.

AWS-managed S3 and S3-compatible HTTPS endpoints are one provider-neutral
capability. They are not an AWS-only product assumption.

## Closed controls

A contract JSON document is valid only when every control below is true.

| Control | Required value |
|---|---|
| Transport | `https` only |
| Hosts | exact-host allowlist; no wildcards; no automatic redirects; no multicast `.local`, metadata, RFC 6761 `.test`/`.invalid`, DNS-rebinding helper names, or embedded / hyphenated IPv4 / 32-bit numeric aliases |
| DNS pinning | resolve once and pin the address for the request lifetime; a later TTL flip must not retarget the socket |
| Private networks | `explicit_allowlist` or `denied`; never implicit RFC1918, RFC 6761/6762, Kubernetes `.svc`, or `.internal` access |
| Credentials | scoped secret registry or workload identity; never broadcast, browser-exposed, or ambient process-wide |
| Permissions | least privilege; public ACLs and public buckets prohibited |
| Encryption | server-side encryption `required` |
| Integrity | content length plus SHA-256 or stronger; fail-closed read verification |
| Lifecycle | `pending`, `available`, `consumed`, `archived`, and `held` are distinct; `consumed` does not delete unless zero retention is explicit |
| Rollback | a partial migration must not delete customer data |
| Names | persisted metadata uses multiword `snake_case` |
| Tenant binding | provider selection is tenant- and purpose-bound |
| Telemetry | bucket names, object keys, credentials, and raw PII are forbidden high-cardinality labels |
| Assurance | CSAP and SOC 2 are design constraints, not certifications |

Operational PII stays usable through the owning product. Forbidding
high-cardinality labels is not a blanket PII mask.

## Endpoint classes

1. **AWS-managed S3 HTTPS endpoints** — `provider_class` is `aws_s3` and every
host is an exact Amazon S3 regional or dual-stack name.
2. **Public S3-compatible HTTPS endpoints** — `provider_class` is
`s3_compatible` and every host is an exact public DNS name.
3. **Authorized private-network endpoints** — `private_network_trust` is
`explicit_allowlist` and each private host is named. Implicit RFC1918,
link-local, metadata-service, multicast `.local`, or special-use
`.internal` / `.corp` / `.lan` / `.svc` access is rejected unless that exact
host is named after an explicit trust decision. Multicast `.local` names,
DNS-rebinding helpers such as `.nip.io`, and hosts that embed a dotted or
hyphenated IPv4 address or 32-bit numeric alias (for example
`169.254.169.254.attacker.example` or `ip-169-254-169-254.ec2.internal`)
are never valid unicast endpoints.

Workload-identity and instance-metadata access require their own SSRF review.
This contract does not grant that access.

## Product next actions

On success the consumer persists or reads the object under its own schema and
records a purpose-bound audit event without bucket, key, credential, or raw PII
labels. On rejection, timeout, duplicate, or partial upload the consumer leaves
the previous durable object in place. Rollback never deletes customer data
because a backfill step only partly succeeded. Product repositories then run
the write/read/delete failure-injection lane in
`docs/object-storage/PRODUCT_ACCEPTANCE_TEMPLATE.md`.

## Verification

```bash
python3 scripts/ci/validate_object_storage_contract.py \
--path schemas/examples/cwl-object-storage-v1.example.json
```

The checked-in example is the naruon-shaped fixture. A product repository may
commit its own contract file and run the same command in its quality job. The
issue is not closed by prose alone: the executable check is the acceptance
evidence.

## Schema

`schemas/cwl-object-storage-v1.schema.json` lists the closed top-level keys.
The Python validator is authoritative when a JSON Schema library is not
installed.
Loading
Loading