diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index fedca83d5..b13278cc6 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -28,7 +28,9 @@ Routes have different meanings: `/health` is liveness, `/ready` is offline confi This revision exposes both `ALLOWED_WORKFLOW_REF_PREFIX` and `ALLOWED_WORKFLOW_SHA`. Despite the legacy ref-binding name, `src/worker.ts` parses `ALLOWED_WORKFLOW_REF_PREFIX` as one **exact full workflow ref** and compares decoded `job_workflow_ref` or `workflow_ref` for exact equality. Wildcard, comma, whitespace, and prefix-sharing configuration forms are rejected. `src/runtime-entrypoint.ts` performs readiness dispatch and delegates `/exchange`; `src/entrypoint.ts` applies the distributed rate limiter before `src/worker.ts` performs its denial-only exact workflow-ref precheck. `src/index.ts` independently enforces the exact workflow ref/repository plus immutable `job_workflow_sha` or fallback `workflow_sha` after cryptographic verification. Missing, malformed, mismatched, or non-canonical configured source identity fails closed. -`wrangler.toml` pins `ALLOWED_WORKFLOW_SHA` to protected central `.github` commit `1cbb6aaf0a24c3628d24c3dd6d9dcaa8a7eec0c5`. The exact trusted `.github/workflows/noema-review.yml` blob at that protected commit remains Git blob `064c4e5aeedcbb188196bd4800a0b918abd6da27`. Movement from `c8cc68a34bd19a91e2544acf08f2ead142ba702b` to current `1cbb6a...` leaves the workflow bytes unchanged but modifies the materialized trusted source tree in `scripts/ci/noema_review_gate.py` and its focused tests, adding one bounded correction attempt when the trusted verdict validator rejects an invalid changed-line/adversarial verdict. Because `noema-review.yml` materializes the complete trusted central source tree at its immutable workflow SHA and then executes `python3 -m scripts.ci.noema_review_gate`, this source-tree change is trust-relevant even though the workflow blob is unchanged; Noema re-audited the exact protected one-commit delta before accepting the new repository commit identity. More generally, Noema re-audits both the exact workflow blob and every relevant protected central delta before each repository-commit movement; ancestry alone is insufficient because GitHub OIDC `job_workflow_sha` binds the caller to that exact repository commit identity. The central repository remains a read-only dependency from Noema. The protected central OIDC consumer still reads a top-level `.token` from the exchange response while Noema's stable success envelope exposes the credential under `data.token`; that consumer defect remains central-owned and is not repaired by weakening or reshaping Noema's stable envelope. +`wrangler.toml` is the canonical repository copy of the currently audited `ALLOWED_WORKFLOW_SHA`; this architecture document deliberately does not duplicate that mutable 40-character value. GitHub OIDC `job_workflow_sha` binds the caller to the exact protected central `.github` repository commit selected by `refs/heads/main`, not merely to the bytes of one workflow file. Therefore every protected central ref movement requires a fresh comparison before Noema may move the runtime trust pin, even when the intervening commit changes only unrelated files. The audit must compare the new protected source tip, the trusted `noema-review.yml` workflow, the review-gate implementation it invokes, central Security Scan authority, and the intervening source-tree delta. Audit-specific SHAs and file deltas belong in the active PR/review evidence rather than this canonical architecture document so later unrelated central commits cannot silently make architecture prose stale. The current central Noema workflow must continue to resolve its trusted source to an immutable workflow commit before materialization, reject stale pull-request heads before credential/model setup, and keep model evaluation separate from publication. Noema advances or rolls back its repository-commit trust pin only after those checks succeed. + +The central repository remains a read-only dependency from Noema. A central OIDC consumer mismatch, reviewer-token lifecycle defect, or scanner-control defect remains central-owned: Noema does not weaken or reshape its producer envelope, reviewer boundary, or source-authentication semantics to compensate for a foreign consumer/control-plane defect. The configured workflow ref and source SHA are operator authority bytes, not normalization input. The protected workflow-ref parser and authoritative verifier do not trim whitespace from these trust values before validation/comparison. A whitespace-bearing value therefore fails as unusable configuration rather than being normalized into a different trusted identity. On an active PR head this statement is candidate truth if the corresponding source delta is not yet on the live protected base. diff --git a/test/architecture-documentation.test.ts b/test/architecture-documentation.test.ts index d92b9b370..55477b583 100644 --- a/test/architecture-documentation.test.ts +++ b/test/architecture-documentation.test.ts @@ -29,7 +29,7 @@ describe("authoritative architecture documentation", () => { } }); - it("binds the code-current architecture to Wrangler state classes and immutable workflow-source configuration", () => { + it("binds the code-current architecture to Wrangler state classes without duplicating the volatile workflow-source pin", () => { const wrangler = readFileSync("wrangler.toml", "utf8"); const architecture = readFileSync("ARCHITECTURE.md", "utf8"); @@ -43,7 +43,9 @@ describe("authoritative architecture documentation", () => { expect(workflowSha).toBeDefined(); expect(architecture).toContain("Code-current canonical architecture"); expect(architecture).toContain("`ALLOWED_WORKFLOW_SHA`"); - expect(architecture).toContain(workflowSha!); + expect(architecture).toContain("`wrangler.toml` is the canonical repository copy"); + expect(architecture).toContain("deliberately does not duplicate that mutable 40-character value"); + expect(architecture).not.toContain(workflowSha!); expect(architecture).not.toContain("Active PR #426"); expect(architecture).not.toContain("not deployed truth until the PR integrates"); }); diff --git a/test/trusted-workflow-runtime-state-preservation.test.ts b/test/trusted-workflow-runtime-state-preservation.test.ts new file mode 100644 index 000000000..d8fd14310 --- /dev/null +++ b/test/trusted-workflow-runtime-state-preservation.test.ts @@ -0,0 +1,12 @@ +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; + +describe("trusted workflow source roll-forward preserves deployed runtime state declarations", () => { + it("keeps the existing Durable Object exports instead of redeclaring deployed classes as new migrations", () => { + const wrangler = readFileSync(new URL("../wrangler.toml", import.meta.url), "utf8"); + + expect(wrangler).toContain('[exports.NoemaRateLimiter]\ntype = "durable-object"\nstorage = "sqlite"'); + expect(wrangler).toContain('[exports.NoemaOidcReplayGuard]\ntype = "durable-object"\nstorage = "sqlite"'); + expect(wrangler).not.toContain("new_sqlite_classes"); + }); +}); diff --git a/test/trusted-workflow-source-rollforward.test.ts b/test/trusted-workflow-source-rollforward.test.ts index 6d5738506..0ea1b83ae 100644 --- a/test/trusted-workflow-source-rollforward.test.ts +++ b/test/trusted-workflow-source-rollforward.test.ts @@ -1,15 +1,24 @@ import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; -const auditedCentralWorkflowSourceSha = - "1cbb6aaf0a24c3628d24c3dd6d9dcaa8a7eec0c5"; +const auditedCentralWorkflowSha = + "43024633eba9d96b0456970391360da5a171fbda"; describe("trusted central workflow source revision", () => { it("binds the deployed OIDC trust configuration to the audited central source commit", () => { const wrangler = readFileSync(new URL("../wrangler.toml", import.meta.url), "utf8"); expect(wrangler).toContain( - `ALLOWED_WORKFLOW_SHA = "${auditedCentralWorkflowSourceSha}"`, + `ALLOWED_WORKFLOW_SHA = "${auditedCentralWorkflowSha}"`, ); }); + + it("keeps the mutable exact source pin single-sourced in wrangler configuration", () => { + const architecture = readFileSync(new URL("../ARCHITECTURE.md", import.meta.url), "utf8"); + + expect(architecture).toContain( + "`wrangler.toml` is the canonical repository copy of the currently audited `ALLOWED_WORKFLOW_SHA`", + ); + expect(architecture).not.toContain(auditedCentralWorkflowSha); + }); }); \ No newline at end of file diff --git a/wrangler.toml b/wrangler.toml index 17a038e3f..53cc4d1cd 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -24,7 +24,7 @@ ALLOWED_AUDIENCE = "cwl-noema-review" ALLOWED_REPOSITORY_OWNER = "ContextualWisdomLab" ALLOWED_WORKFLOW_REPOSITORY = "ContextualWisdomLab/.github" ALLOWED_WORKFLOW_REF_PREFIX = "ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main" -ALLOWED_WORKFLOW_SHA = "1cbb6aaf0a24c3628d24c3dd6d9dcaa8a7eec0c5" +ALLOWED_WORKFLOW_SHA = "43024633eba9d96b0456970391360da5a171fbda" GITHUB_API_BASE = "https://api.github.com" NOEMA_RATE_LIMIT_PER_MINUTE = "60" NOEMA_OIDC_JWKS_CACHE_TTL_SECONDS = "300" @@ -34,4 +34,4 @@ NOEMA_INSTALLATION_CACHE_TTL_SECONDS = "600" # wrangler secret put GITHUB_APP_ID # wrangler secret put GITHUB_APP_PRIVATE_KEY_PEM # Optional: restrict to a specific installation id instead of discovering by repo. -# wrangler secret put GITHUB_APP_INSTALLATION_ID +# wrangler secret put GITHUB_APP_INSTALLATION_ID \ No newline at end of file