diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d595ef46..f2eddff2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,15 +59,8 @@ jobs: # fact — never as live vocabulary a reader would adopt. # - docs/VERIFICATION-GAPS.md: narrates the terminology # sweep itself. - # - docs/architecture/repository-certification.md, - # docs/architecture/phase2b-technical-debt-assessment.md, - # docs/architecture/phase2c-terminology-guard.md: each - # independently found and/or fixed this exact guard gap, - # and necessarily quotes the retired phrase to document - # what was found. Expect this category to keep gaining - # entries over time — that is normal, not a regression; - # see phase2c-terminology-guard.md for why a per-file list - # is the deliberate choice here, not a stopgap. + # A per file list is the deliberate choice here, not a + # stopgap; expect it to gain entries over time. # # (2) Third-party citation: docs/site's two "Related work" notes # cite an unrelated third-party academic framework that is @@ -99,9 +92,6 @@ jobs: --exclude='VERIFICATION-GAPS.md' \ --exclude='how-parmana-thinks.mdx' \ --exclude='execution-authorization.mdx' \ - --exclude='repository-certification.md' \ - --exclude='phase2b-technical-debt-assessment.md' \ - --exclude='phase2c-terminology-guard.md' \ --exclude='changelog.mdx' \ --exclude='ci.yml' \ --exclude='terminology-guard.test.ts' \ diff --git a/.gitleaks.toml b/.gitleaks.toml index d111bf43..9ca145f6 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -34,7 +34,7 @@ regexes = [ # SHA-256 hash of the publicly documented local-dev demo caller key # (raw key "my-secret-api-key", see docs/site/api-reference/authentication.mdx - # and docs/guides/e2e/04-execute-transaction.md). A hash is not reversible + # and docs/site/deployment/local.mdx). A hash is not reversible # and the raw value it hashes is intentionally public. '''325ededd6c3b9988f623c7f964abb9b016b76b0f8b3474df0f7d7c23b941381f''', diff --git a/docs/CLAIMS.md b/docs/CLAIMS.md index d3cc52c9..f4c83d4d 100644 --- a/docs/CLAIMS.md +++ b/docs/CLAIMS.md @@ -210,13 +210,13 @@ Evidence Parmana supports replay of recorded execution decisions for verification and analysis, via the standalone `@parmana/replay` package (`ReplayEngine`), which re-evaluates a recorded policy decision against its recorded signals and reports whether the outcome matches. -**Scope note (independently verified, Phase 2G):** this is a package-level capability, not yet wired into the HTTP API. `POST /replay` exists and returns 200, but its actual implementation (`ExecutionTrustApplication.replay()`) performs a signature/hash recheck of the stored Trust Record — the same category of check as `/verify` — not a call into `@parmana/replay`. Both behaviors are real and tested; they are simply two different things sharing the word "replay." See `docs/site/replay/overview.mdx` for the full disambiguation and `docs/architecture/phase2g-replay-semantics.md` for the independent verification, call graphs, and regression tests establishing this as the current, intentional, and separately production-contracted (both SDKs are typed against `POST /replay`'s current response shape) behavior — not an unwired bug awaiting a fix. +**Scope note (independently verified, Phase 2G):** this is a package-level capability, not yet wired into the HTTP API. `POST /replay` exists and returns 200, but its actual implementation (`ExecutionTrustApplication.replay()`) performs a signature/hash recheck of the stored Trust Record — the same category of check as `/verify` — not a call into `@parmana/replay`. Both behaviors are real and tested; they are simply two different things sharing the word "replay." See `docs/site/replay/overview.mdx` for the full disambiguation and the Phase 2G replay semantics record (in git history) for the independent verification, call graphs, and regression tests establishing this as the current, intentional, and separately production-contracted (both SDKs are typed against `POST /replay`'s current response shape) behavior — not an unwired bug awaiting a fix. Evidence - Replay package (packages/replay/src/ReplayEngine.ts) -- docs/architecture/phase2g-replay-semantics.md +- the Phase 2G replay semantics record (in git history) --- @@ -504,7 +504,7 @@ Evidence - packages/policy/tests/unit/CapabilityPolicyBinder.test.ts (proves the exact live-shaped exploit — `hubspot:deal-update` paired with the unrelated, unprotected `vendor-payment/2.0.0` policy, and the same substitution shape for `hubspot:deal-fetch` paired with `customer-refund/1.0.0` — is rejected; also proves every production-registered capability has a binding, and, per that file's own comment, that Razorpay capabilities were removed from the bound set entirely when the connector was deleted 2026-08-12, not merely left unbound) -- docs/architecture/phase3d-independent-authorization-certification.md §5.1 (independent re-verification, Phase 3D) +- §5.1 of the Phase 3D independent authorization certification, in git history (independent re-verification, Phase 3D) **Update (code-only ground-truth capture pass, follow-up closure):** this section's "covering every capability the production connector registry actually registers" was, until this pass, not quite true — `CANONICAL_CAPABILITY_POLICY_BINDINGS` also carried a stale `payments:execute` entry left behind by G-27's vendor-payment removal (`docs/VERIFICATION-GAPS.md` G-27's own update). Removed; the table now matches the claim exactly. See that G-27 update for the full trace. @@ -518,7 +518,7 @@ Evidence ## 2.23 Independently Certified Authorization (Phase 3D) -_"Even if AI has valid credentials, it still cannot execute anything your business hasn't authorized. No exceptions"_ — the specific claim tracked and re-verified across `docs/architecture/phase2k-capability-policy-binding.md`, `phase2l-authorization-exceptions.md` (which found it **not fully supported**, naming two exceptions: Razorpay's caller-declared daily cumulative total, and HubSpot's caller-declared `preAuthorizedForAmountChange`) — was independently re-certified from current repository state in `docs/architecture/phase3d-independent-authorization-certification.md`, treating every prior phase's conclusion as a claim to re-verify, not inherit. +_"Even if AI has valid credentials, it still cannot execute anything your business hasn't authorized. No exceptions"_ — the specific claim tracked and re-verified across the Phase 2K capability policy binding record (in git history), the Phase 2L authorization exceptions record (in git history) (which found it **not fully supported**, naming two exceptions: Razorpay's caller-declared daily cumulative total, and HubSpot's caller-declared `preAuthorizedForAmountChange`) — was independently re-certified from current repository state in the Phase 3D independent authorization certification (in git history), treating every prior phase's conclusion as a claim to re-verify, not inherit. **Result: CLAIM FULLY CERTIFIED** for both capabilities actually reachable in production (`razorpay:refund-create`, `hubspot:deal-update`). Both of Phase 2L's named exceptions are independently confirmed closed at the mechanism level (2.4/3.4/3.10's own TD-23 updates), credential isolation, structural capability-to-policy binding (2.22, TD-22), replay resistance, concurrency safety, and auditability were each independently re-traced from source, and no repository evidence was found that contradicts the claim for either in-scope capability — no counter-example, exploit, or bypass path was located during the certification's adversarial review (§10 of that document). @@ -526,7 +526,7 @@ Two narrow, genuinely fixable gaps the certification disclosed were closed in th Evidence -- docs/architecture/phase3d-independent-authorization-certification.md (full methodology, per-property verification, adversarial assessment, evidence summary) +- the Phase 3D independent authorization certification, in git history (full methodology, per-property verification, adversarial assessment, evidence summary) - packages/connector-hubspot/src/HubSpotTypes.ts (`redactHubSpotToken`, fingerprint-based) @@ -566,7 +566,7 @@ Evidence ## 2.25 Strategic Positioning — Independently Validated, YES (Pass 4) -_"Only what you authorize should become real"_ — the specific invariant underlying the "We Are Not in the AI Race" positioning — was independently, repeatedly source-code-validated across four passes (`docs/architecture/strategic-positioning-validation.md`, the living record). The first three passes reached **PARTIALLY SUPPORTED**: the authorization mechanism itself was fully validated for every capability actually reachable in production, but `payments:execute` (vendor-payment) existed in committed code with unverified caller-declared signals, gated only by `NODE_ENV`, not by any authorization-strength property — a capability that would have violated the claim had it ever been enabled as it then existed. +_"Only what you authorize should become real"_ — the specific invariant underlying the "We Are Not in the AI Race" positioning — was independently, repeatedly source-code-validated across four passes (the strategic positioning validation record, in git history). The first three passes reached **PARTIALLY SUPPORTED**: the authorization mechanism itself was fully validated for every capability actually reachable in production, but `payments:execute` (vendor-payment) existed in committed code with unverified caller-declared signals, gated only by `NODE_ENV`, not by any authorization-strength property — a capability that would have violated the claim had it ever been enabled as it then existed. **The fourth pass, run fresh with no reliance on the first three passes' conclusions, upgraded the verdict to SUPPORTED BY IMPLEMENTATION — YES.** The structural change: `payments:execute` was removed from the repository entirely (`docs/VERIFICATION-GAPS.md` G-27), not gated more tightly. `createConnectorRegistry.ts`, at the time of this pass, registered exactly three connectors in production wiring — `test-fixture` (a `NODE_ENV=test`-only, unbound, no-production-implication connector introduced solely to keep shared test infrastructure executable), `razorpay`, `hubspot` — and `payments:execute` had no connector to resolve to in any environment, independently confirmed by a dedicated regression test asserting this across `test`, `production`, and `development` `NODE_ENV` values. The fourth pass separately, freshly re-scrutinized the replacement test-only connector itself for new bypass risk (rather than assuming it safe by association with the removal) and found none: it is fail-closed by the identical mechanism every other connector in this registry uses, and unbound from `CapabilityPolicyBinding.ts`'s governance entirely. @@ -574,11 +574,11 @@ _"Only what you authorize should become real"_ — the specific invariant underl **Update (2026-08-25 audit-fix pass):** the "exactly two" count directly above was itself already wrong the day it was written. `createConnectorRegistry.ts` conditionally registers a third connector, `github` (`createGitHubConnector`, `github:pr-fetch`/`github:pr-merge`, §3.17), wired in on 2026-08-19 — five days before the 2026-08-24 pass re-counted the file and still reported two. Confirmed directly by re-reading `createConnectorRegistry.ts` itself, which conditionally registers `test-fixture`, `hubspot`, and `github`. This does not change the YES verdict — GitHub's authorization mechanism is the same caller-agnostic pipeline, and `payments:execute` still has no connector — but it is the second time in this section's history a connector count was asserted without actually re-deriving it from the file, and the miscount stood for a full day of otherwise-careful documentation-currency work. `docs/VERIFICATION-GAPS.md` G-30 has the fuller account of what else this same miscount caused to be missed (§2.22's binding-coverage claim). -**Honesty constraint, carried from the fourth pass's own report, not rounded away here:** that pass re-executed only 2 of the 10 negative tests cited across this validation's history fresh (`authority-type-agnostic-execution.integration.test.ts`; `create-connector-registry.test.ts`'s new absence case) — the other 8 were cited from code paths confirmed structurally unchanged by the removal, not individually re-run in that session. Multi-tenant/cross-institution authority isolation and the direct-database-write bypass finding were explicitly left as "unchanged, not re-traced," not re-asserted clean. The YES verdict means the specific invariant is now supported without a known exception for every capability this repository currently exposes — it is not a claim that every adjacent property was re-proven from scratch in the same session. Full precision on this distinction: `docs/architecture/strategic-positioning-validation.md` §6 ("Final Answer"). +**Honesty constraint, carried from the fourth pass's own report, not rounded away here:** that pass re-executed only 2 of the 10 negative tests cited across this validation's history fresh (`authority-type-agnostic-execution.integration.test.ts`; `create-connector-registry.test.ts`'s new absence case) — §6 of the other 8 were cited from code paths confirmed structurally unchanged by the removal, not individually re-run in that session. Multi-tenant/cross-institution authority isolation and the direct-database-write bypass finding were explicitly left as "unchanged, not re-traced," not re-asserted clean. The YES verdict means the specific invariant is now supported without a known exception for every capability this repository currently exposes — it is not a claim that every adjacent property was re-proven from scratch in the same session. Full precision on this distinction: the strategic positioning validation record (in git history) ("Final Answer"). Evidence -- docs/architecture/strategic-positioning-validation.md (full four-pass history, claim-by-claim matrices, execution control path, bypass analysis, negative-test evidence for each pass) +- the strategic positioning validation record, in git history (full four-pass history, claim-by-claim matrices, execution control path, bypass analysis, negative-test evidence for each pass) - docs/VERIFICATION-GAPS.md G-27 (the removal this upgrade rests on) @@ -671,7 +671,7 @@ its same-day addendum. **Preventive Git-layer enforcement: CI check exists and is fail-closed; enabling it as a required check is blocked by GitHub plan/visibility, not by anything in this codebase.** The maker-checker API above governs every write that goes through `pending-policy-changes.ts`, but it cannot by itself prevent a direct edit to `policies/{name}/{version}/policy.json` committed straight to the repository, bypassing the approval workflow entirely. `scripts/verify-policy-changes-approved.ts` closes the detection half of that gap: given a list of changed policy files (or `--full-scan`), it hashes each file's live content (`PolicyChangeCrypto.hashPolicyContent`) and checks it against `policy_change_approval_records`' `content_hash_after` for that `(policyName, policyVersion)`, via a read-only Supabase `anon` credential scoped by RLS to that one table (`supabase/migrations/20260818150000_add_ci_read_only_policy_for_approval_records.sql`) — deliberately not `DATABASE_URL`/`SUPABASE_SERVICE_ROLE_KEY`, which would grant read+write on every table. It is fail-**closed** by design, the opposite discipline from `verifyPolicyGovernanceIntegrityAtStartup`'s fail-open startup check: a missing approval record, a content-hash mismatch, _and_ any failure to complete the check at all (Supabase unreachable, a malformed response) are all treated identically as a failure — there is no "couldn't check, let it pass" path. `.github/workflows/ci.yml`'s `verify-policy-approvals` job runs this on every push and pull request against whichever `policies/**/policy.json` files changed, and already fails the job (non-zero exit) on a bypass — this is not advisory logging. What is genuinely missing is the last step: telling GitHub to treat that job's pass/fail as a required condition for merging into `main` (via branch protection). Attempting to enable it this session (`PUT /repos/{owner}/{repo}/branches/main/protection`, admin-authenticated) failed with a real, externally-imposed constraint: `403 Upgrade to GitHub Pro or make this repository public to enable this feature` — branch protection rules are not available on a private repository under this account's current GitHub plan. This is exactly the condition the CI job's own header comment already anticipated ("if/when this repo's plan or visibility ever allows branch protection to be enabled"), not a gap introduced or overlooked by this pass. **Precise current state:** a direct `git push` to `main` containing an unapproved `policy.json` edit will be caught and reported by CI (the job fails, visibly, on that commit) but is not currently _prevented_ from landing on `main` — detection is real and automatic; prevention requires either a paid GitHub plan or making this proprietary, evaluation-only repository public, and no decision has been made to do either. -**Legacy-policy backfill: resolved 2026-09-16.** The pause point described below (originally: awaiting a second, genuinely distinct human checker) is closed. A distinct reviewer identity, `policy-reviewer-1` (`credentialHolderType: USER`, provisioned via `scripts/generate-api-key.ts --generate-step-up-key`, genuinely separate bearer key and step-up keypair from the proposer `charak1987`), approved all 10 of the original 2026-08-19 proposals plus 4 additional pre-existing policies that had never been proposed at all (`agent-vendor-payment`, `api-key-issuance`, `expense-reimbursement`, `slack-post-message`), for 14 total, following `docs/operations/policy-approval-runbook.md`. Every approval went through the real endpoints (`POST /:name/:version/pending-changes`, `POST /pending-changes/:id/approve`), step-up signed and verified, `SameActorCannotApproveOwnChangeError` never triggered since `proposedBy` (`charak1987`) and `approvedBy` (`policy-reviewer-1`) were always genuinely distinct credentials. **Correction (2026-09-28):** distinct credentials, not proven distinct people. The `policy-reviewer-1` step up private key was kept on the proposer's machine (recorded 2026-09-27), so these approvals do not show a second person. Its key was revoked on 2026-09-27. +**Legacy-policy backfill: resolved 2026-09-16.** The pause point described below (originally: awaiting a second, genuinely distinct human checker) is closed. A distinct reviewer identity, `policy-reviewer-1` (`credentialHolderType: USER`, provisioned via `scripts/generate-api-key.ts --generate-step-up-key`, genuinely separate bearer key and step-up keypair from the proposer `charak1987`), approved all 10 of the original 2026-08-19 proposals plus 4 additional pre-existing policies that had never been proposed at all (`agent-vendor-payment`, `api-key-issuance`, `expense-reimbursement`, `slack-post-message`), for 14 total, following the policy approval runbook (in git history). Every approval went through the real endpoints (`POST /:name/:version/pending-changes`, `POST /pending-changes/:id/approve`), step-up signed and verified, `SameActorCannotApproveOwnChangeError` never triggered since `proposedBy` (`charak1987`) and `approvedBy` (`policy-reviewer-1`) were always genuinely distinct credentials. **Correction (2026-09-28):** distinct credentials, not proven distinct people. The `policy-reviewer-1` step up private key was kept on the proposer's machine (recorded 2026-09-27), so these approvals do not show a second person. Its key was revoked on 2026-09-27. One real finding surfaced during this pass: the original 2026-08-19 proposals for all 10 policies predated this codebase's `unboundSignalReasons` documentation field (harmless, non-functional) and, for `connector-capability` and `customer-refund` specifically, predated `boundSignals` being added to those two files entirely (functional — without it, `SignalIntentBinder` cannot derive `paymentAmount`/`refundAmount` from the request's own parameters). Approving the stale 2026-08-19 content as-is would have persisted that gap. Instead, all 14 were re-proposed with their current, correct file content and re-approved, so the live `policies` table and each policy's `PolicyChangeApprovalRecord` agree exactly — confirmed via `scripts/verify-policy-changes-approved.ts --full-scan`: "All 14 checked policy file(s) have a matching approval record." @@ -957,7 +957,7 @@ Evidence **Feature-flagged, default OFF — this was a decision, not an oversight.** `createPolicyExecutionVerifier()` (`packages/api/src/bootstrap/createPolicyExecutionVerifier.ts`) returns `undefined` unless `POLICY_EXECUTION_VERIFICATION_ENFORCED=true` is set. This was deliberate when written: at that time, every real production policy in this system was still `PENDING_APPROVAL` with zero rows in `policy_change_approval_records` (2.26's "Legacy-policy backfill" entry, ten policies proposed 2026-08-19). Enabling this gate unconditionally would have refused every execution in the system, not merely a genuine bypass. Before writing any code, this exact tradeoff was put to the user directly (an always-on gate vs. a warn-only stage vs. a feature flag vs. not building it yet); the user chose the feature-flagged default-off option, specifically to avoid a choice between bricking production and fabricating synthetic approvals for the ten real pending policies to work around it — the latter being exactly what 2.34's backfill-script fix (`4e1a8e3`) already refused to do for a different reason. -**Update (2026-09-16): the precondition above no longer holds, the flag itself is unchanged.** 2.26's "Legacy-policy backfill" entry now shows all 14 real production policies with a genuine, distinct-checker `PolicyChangeApprovalRecord`. The reason the flag was off (zero approval records, an unconditional gate would refuse everything) is gone. `POLICY_EXECUTION_VERIFICATION_ENFORCED` has not been changed and remains `false` in `.env` — turning it on is still a separate, deliberate decision the runbook explicitly reserves for a later, dedicated step (`docs/operations/policy-approval-runbook.md` Part 5: "Do this only once every policy this deployment actually executes against has been resolved"), not something this pass did as a side effect of closing the backfill. +**Update (2026-09-16): the precondition above no longer holds, the flag itself is unchanged.** 2.26's "Legacy-policy backfill" entry now shows all 14 real production policies with a genuine, distinct-checker `PolicyChangeApprovalRecord`. The reason the flag was off (zero approval records, an unconditional gate would refuse everything) is gone. `POLICY_EXECUTION_VERIFICATION_ENFORCED` has not been changed and remains `false` in `.env` — turning it on is still a separate, deliberate decision the runbook explicitly reserves for a later, dedicated step (Part 5 of the policy approval runbook, in git history: "Do this only once every policy this deployment actually executes against has been resolved"), not something this pass did as a side effect of closing the backfill. **Update (2026-09-20): the flag no longer decides production behavior.** `createPolicyExecutionVerifier()` now returns a real verifier everywhere except when `NODE_ENV` is exactly `test` or `development`. In production, or with `NODE_ENV` unset or set to any other value, `POLICY_EXECUTION_VERIFICATION_ENFORCED` is ignored and enforcement is on. The earlier gap, where a production deployment could run with the verifier unconfigured (`policyExecutionVerifierConfigured: false` was observed in the live startup log on 2026-09-20), is closed by removing the opt in. The tradeoff recorded above still applies in one direction: any deployment must have a genuine, signed approval record for every policy it executes against before it goes live, or executions under that policy are refused. @@ -1363,9 +1363,9 @@ Learning directly from this codebase's own Razorpay incidents, both fixes are st A third, narrower point this milestone originally left unresolved: `preAuthorizedForAmountChange` was, at the time, a caller-declared boolean signal with no independent verification. Absent, it defaults to `false` — the safe default: an over-threshold amount change is denied unless a caller declares it pre-authorized. -**Update (TD-23, Phase 3C, now closed):** `preAuthorizedForAmountChange` is no longer trusted verbatim. `HubSpotSignalStateVerifier` (`packages/connector-hubspot/src/HubSpotSignalStateVerifier.ts`), constructed with a real `ApprovalVerifier` (`@parmana/approval`) unconditionally in `packages/api/src/bootstrap/createHubSpotSignalStateVerifier.ts` (that file's own comment: "approvalVerifier is always supplied here (never omitted) -- the production wiring path is where independent verification... becomes a structural invariant rather than an optional, caller-declared signal"), verifies a caller's `preAuthorizedForAmountChange: true` claim against a real, independently-issued, Ed25519-signed Approval Artifact (`SignedApproval`, `docs/architecture/phase3a-authorization-artifact-design.md`) carried in `signals.approvalArtifact` -- checking issuer identity against a registry, signature validity, expiry, capability/resource scope match, and single-use nonce consumption (`ApprovalVerifier.verify`, `packages/approval/src/ApprovalVerifier.ts`), all before the declared value is trusted. A missing, expired, wrong-scope, replayed, or unsigned artifact is treated as `preAuthorizedForAmountChange: false` regardless of what the caller declared. This closes the gap `docs/architecture/phase2l-authorization-exceptions.md` independently re-confirmed still open as of that phase. +**Update (TD-23, Phase 3C, now closed):** `preAuthorizedForAmountChange` is no longer trusted verbatim. `HubSpotSignalStateVerifier` (`packages/connector-hubspot/src/HubSpotSignalStateVerifier.ts`), constructed with a real `ApprovalVerifier` (`@parmana/approval`) unconditionally in `packages/api/src/bootstrap/createHubSpotSignalStateVerifier.ts` (that file's own comment: "approvalVerifier is always supplied here (never omitted) -- the production wiring path is where independent verification... becomes a structural invariant rather than an optional, caller-declared signal"), verifies a caller's `preAuthorizedForAmountChange: true` claim against a real, independently-issued, Ed25519-signed Approval Artifact (`SignedApproval`, the Phase 3A approval artifact design, in git history) carried in `signals.approvalArtifact` -- checking issuer identity against a registry, signature validity, expiry, capability/resource scope match, and single-use nonce consumption (`ApprovalVerifier.verify`, `packages/approval/src/ApprovalVerifier.ts`), all before the declared value is trusted. A missing, expired, wrong-scope, replayed, or unsigned artifact is treated as `preAuthorizedForAmountChange: false` regardless of what the caller declared. This closes the gap the Phase 2L authorization exceptions record (in git history) independently re-confirmed still open as of that phase. -**Operational scope, independently confirmed by Phase 3D:** `createApprovalIssuerRegistry.ts`'s `TRUSTED_APPROVAL_ISSUERS` list ships **empty by default** — no real business-approver key is provisioned in this deployment. This is the correct fail-closed starting state (every `preAuthorizedForAmountChange` claim is rejected, genuine artifact or not, until an operator adds a real entry and deploys), not a gap in the mechanism above, but it means no over-threshold `hubspot:deal-update` amount change can be legitimately approved in the current deployment as configured — only ever denied. The verification mechanism itself is proven correct by unit and integration test against synthetic issuers (below); it has not yet been exercised against a real, operator-provisioned issuer key in production. See `docs/architecture/phase3d-independent-authorization-certification.md` §4.2, §12.3. +**Operational scope, independently confirmed by Phase 3D:** `createApprovalIssuerRegistry.ts`'s `TRUSTED_APPROVAL_ISSUERS` list ships **empty by default** — no real business-approver key is provisioned in this deployment. This is the correct fail-closed starting state (every `preAuthorizedForAmountChange` claim is rejected, genuine artifact or not, until an operator adds a real entry and deploys), not a gap in the mechanism above, but it means no over-threshold `hubspot:deal-update` amount change can be legitimately approved in the current deployment as configured — only ever denied. The verification mechanism itself is proven correct by unit and integration test against synthetic issuers (below); it has not yet been exercised against a real, operator-provisioned issuer key in production. See §4.2 and §12.3 of the Phase 3D independent authorization certification (in git history). **Test posture, in the order specified for this milestone:** @@ -1827,7 +1827,7 @@ The following claims are planned but are intentionally withheld until supported - Every production Runtime enforces the canonical trust pipeline. -- Every production API request executes through the canonical runtime. **Reconciliation note (not a promotion — remains withheld pending whatever broader evidence standard this section applies):** Phase 3D's Property C bypass search (`docs/architecture/phase3d-independent-authorization-certification.md` §5.2) traced every route `packages/api/src/app.ts` mounts and confirmed only `POST /execute` reaches `application.execute()`/`RuntimeEngine`; every other route is read-only, verification-only, or a passive audit sink that never calls `executionSystem.execute()` or any connector. This is direct, current evidence bearing on this specific claim, scoped to the routes and connectors that exist today — flagged here so it isn't lost, without this reconciliation pass itself deciding whether it now meets this section's bar for promotion to §2. +- Every production API request executes through the canonical runtime. **Reconciliation note (not a promotion — remains withheld pending whatever broader evidence standard this section applies):** Phase 3D's Property C bypass search (§5.2 of the Phase 3D independent authorization certification, in git history) traced every route `packages/api/src/app.ts` mounts and confirmed only `POST /execute` reaches `application.execute()`/`RuntimeEngine`; every other route is read-only, verification-only, or a passive audit sink that never calls `executionSystem.execute()` or any connector. This is direct, current evidence bearing on this specific claim, scoped to the routes and connectors that exist today — flagged here so it isn't lost, without this reconciliation pass itself deciding whether it now meets this section's bar for promotion to §2. - Replay semantically verifies every trust artifact. @@ -1835,7 +1835,7 @@ The following claims are planned but are intentionally withheld until supported - Every guarantee includes complete independent verification evidence. -- A general, named credential-brokering _mechanism_ — a formal product capability letting an arbitrary future connector class (e.g. third-party cloud credentials via AWS STS, per the July roadmap, removed 2026-09-28) prove "AI never possesses execution credentials" without bespoke per-connector work — does not exist; only the `CredentialProvider`/session-credential-vault pattern each connector individually implements does. **Narrowed by Phase 3D (2.22/2.23):** for the connectors covered by that certification (Razorpay, HubSpot, at the time it was performed — Razorpay was removed from this codebase 2026-08-12, HubSpot remains the one production-reachable connector today), the underlying property this future item describes — the AI-facing `/execute` request path never comes into possession of the raw connector credential, which is resolved only after authorization is fully decided, confined to the connector-execution layer — was independently traced end-to-end and verified true (`docs/architecture/phase3d-independent-authorization-certification.md` §3). What remains genuinely future is only the generalized, connector-class-agnostic mechanism, not the property itself for these two connectors. (Certification's own disclosed caveat, 2.23: a short, non-functional fingerprint of the credential, not the credential itself, reaches the caller-visible response — see 3.4/3.10.) +- A general, named credential-brokering _mechanism_ — a formal product capability letting an arbitrary future connector class (e.g. third-party cloud credentials via AWS STS, per the July roadmap, removed 2026-09-28) prove "AI never possesses execution credentials" without bespoke per-connector work — does not exist; only the `CredentialProvider`/session-credential-vault pattern each connector individually implements does. **Narrowed by Phase 3D (2.22/2.23):** for the connectors covered by that certification (Razorpay, HubSpot, at the time it was performed — Razorpay was removed from this codebase 2026-08-12, HubSpot remains the one production-reachable connector today), the underlying property this future item describes — the AI-facing `/execute` request path never comes into possession of the raw connector credential, which is resolved only after authorization is fully decided, confined to the connector-execution layer — was independently traced end-to-end and verified true (§3 of the Phase 3D independent authorization certification, in git history). What remains genuinely future is only the generalized, connector-class-agnostic mechanism, not the property itself for these two connectors. (Certification's own disclosed caveat, 2.23: a short, non-functional fingerprint of the credential, not the credential itself, reaches the caller-visible response — see 3.4/3.10.) - Enterprise-grade key custody: current key storage is local PEM files read by FileKeyProvider; no KMS, HSM, or cloud key vault integration exists. **Narrowed (2026-09-15):** AWS KMS integration now exists and is verified end-to-end for the gateway signing key (see the 2026-09-15 update on the G-40 entry in this document, §3.22, and `docs/adr/ADR-0009-KMS-Secrets-And-Connector-Signature-Hardening.md`) — but it is not the default (`KEY_PROVIDER=local` remains the default; a deployment must explicitly opt into `KEY_PROVIDER=aws-kms`), production Vercel→AWS OIDC federation is not yet provisioned, and HSM/Azure Key Vault/GCP KMS remain entirely unimplemented. The "no KMS integration exists" framing above is accurate only for a default, unconfigured deployment. diff --git a/docs/adr/ADR-0009-KMS-Secrets-And-Connector-Signature-Hardening.md b/docs/adr/ADR-0009-KMS-Secrets-And-Connector-Signature-Hardening.md index 3eb8bb96..6c1cd25d 100644 --- a/docs/adr/ADR-0009-KMS-Secrets-And-Connector-Signature-Hardening.md +++ b/docs/adr/ADR-0009-KMS-Secrets-And-Connector-Signature-Hardening.md @@ -7,8 +7,8 @@ **Update (2026-09-20):** AWS KMS refuses a raw Ed25519 message over 4096 bytes, which broke signing of a full Execution Trust Record in production. A message over that size is now signed as a fixed size commitment, see `ADR-0010-Large-Message-Signing-Under-KMS.md`. **Update (2026-09-15):** Architecture §1 (gateway signing key → AWS KMS) and §4 (Paytm connector signature verification) are now built — see `ROADMAP.md`'s "Secrets, Signing-Key Custody & Connector Signature Hardening" section and `docs/CLAIMS.md`'s 2026-09-15 update on the G-40 entry for exact status and verification evidence. Architecture §2 (opaque connector secrets → AWS Secrets Manager) and §3 (GitHub App credential elimination) remain not built. **Update (2026-09-16):** Full setup guide and troubleshooting record for everything in this -ADR: `docs/operations/aws-kms-vercel-oidc-setup-guide.md` and -`docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md`. Five additional real +ADR: `docs/site/deployment/aws-kms-signing.mdx` and +the 2026-09-15 KMS migration troubleshooting guide (in git history). Five additional real production bugs were found and fixed getting this live — none visible from code review, only from testing against real Vercel/AWS/traffic — full detail in those documents and in `docs/VERIFICATION-GAPS.md` G-48/G-49. diff --git a/docs/architecture/DATABASE_SCHEMA_REFERENCE.md b/docs/architecture/DATABASE_SCHEMA_REFERENCE.md deleted file mode 100644 index 00152a80..00000000 --- a/docs/architecture/DATABASE_SCHEMA_REFERENCE.md +++ /dev/null @@ -1,580 +0,0 @@ -# Database Schema Reference - -**Status:** Verified against the live production database (2026-09-16) and all 28 -`supabase/migrations/*.sql` files, read in chronological order — several tables were altered -by 4-5 later migrations each (most notably `caller_audit_events` and `execution_audit_events`, -whose `type` CHECK constraints were each widened repeatedly), so the shape below is the -**final, cumulative** schema, not just what the original `CREATE TABLE` said. - -**Companion documents:** `docs/02-architecture/STORAGE.md` (the abstract Storage Layer -architecture spec — read that first for the conceptual model); `docs/operations/aws-kms-vercel-oidc-setup-guide.md` -and `docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md` (operational -history that touches several of these tables). This document is the concrete "what table, -what column, why, and how do I query it" reference neither of those is. - -## How to use this document - -Each table's section states: what it's for, its final column list, which migrations built -it (in order), what application code owns it, and RLS status. Part 2 is a library of -real, copy-pasteable SQL for troubleshooting, auditing, and verifying schema state — the -kind of queries actually used tonight to diagnose a real production incident (see the -troubleshooting guide referenced above). - -**Connecting:** every query below assumes a direct Postgres connection via `DATABASE_URL` -(the same one the application itself uses — Parmana connects directly via `pg`, not through -PostgREST, which is why RLS being enabled on most tables doesn't block the application: a -direct Postgres connection with the database's own credentials bypasses RLS entirely, the -same way a Postgres superuser or the table owner does. RLS matters here for what a -different credential — e.g. Supabase's `anon`/`authenticated` roles via PostgREST — could -see, not what this codebase's own `DATABASE_URL` connection can). - -```bash -psql "$DATABASE_URL" -# or, from Node, using this repo's own convention: -node -e 'require("dotenv").config(); const {Pool}=require("pg"); const pool=new Pool({connectionString:process.env.DATABASE_URL,min:1}); /* ... */' -``` - ---- - -## Part 1 — Table-by-table reference - -### Quick index - -| Table | One-line purpose | Owning code | -| --------------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------- | -| `business_transactions` | The initial received request, as submitted | `SupabaseBusinessTransactionRepository` | -| `executions` | Append-only log of execution attempts for a transaction | `SupabaseExecutionTrustRecordRepository` | -| `execution_trust_records` | The signed, final trust record for a transaction (one per transaction that reached one) | `SupabaseExecutionTrustRecordRepository` | -| `verifications` | Log of independent verification calls | `SupabaseExecutionTrustRecordRepository` | -| `receipts` | Signed receipts issued for a transaction | `SupabaseExecutionTrustRecordRepository` | -| `settlement_confirmations` | Signed settlement confirmations | `SupabaseExecutionTrustRecordRepository` | -| `overrides` | Backing table for `OverrideService` — **deliberately unreachable**, expect empty | `SupabaseExecutionTrustRecordRepository` | -| `refusal_records` | Signed record of a policy/binding REJECT | `SupabaseRefusalRecordRepository` | -| `caller_audit_events` | Every caller-auth decision (auth success/failure, capability/principal checks) | `SupabaseCallerAuditSink` | -| `execution_audit_events` | Execution-control lifecycle events — **shared cross-repo with `parmana-paytm-agent`** | `SupabaseExecutionAuditSink` | -| `pending_policy_changes` | Maker-checker: a proposed policy change awaiting approval | `SupabasePendingPolicyChangeRepository` | -| `policy_change_approval_records` | Maker-checker: the signed approval/rejection of a pending change | `SupabasePolicyChangeApprovalRecordRepository` | -| `consumed_nonces` | Replay protection for Gateway Authorization envelopes | `SupabaseNonceStore` | -| `consumed_approval_nonces` | Replay protection for Approval Artifacts (separate namespace) | `SupabaseApprovalNonceStore` | -| `consumed_policy_change_step_up_nonces` | Replay protection for a human checker's step-up key (separate namespace) | `SupabasePolicyChangeStepUpNonceStore` | -| `challenge_records` | Investigation records for disputed claims — the only mutable table here | `PostgresChallengeRecordRepository` | -| `rate_limit_counters` | Fleet-wide rate-limit counters — the only table without RLS | `PostgresRateLimitStore` | - -**Not covered here:** `supabase_migrations.schema_migrations` (Supabase CLI's own tracking -table, a different schema — see Part 2's migration-sync queries). Three Razorpay tables -(`razorpay_webhook_events`, `razorpay_webhook_audit_events`, `razorpay_daily_refund_reservations`) -were dropped 2026-09-16 (`20260916120000_drop_razorpay_tables.sql`) — orphaned schema from -the Razorpay connector, removed from this codebase entirely on 2026-08-12. - ---- - -### `business_transactions` - -**Why it exists:** the durable record of a request exactly as submitted to `POST /execute` -— authority, authorization, intent, policy reference, and signals, all as originally -received, before any decision is made. - -**Created:** `20260629013035_initial_schema.sql`. Never altered since. - -**Columns:** - -| Column | Type | Notes | -| ------------------------- | ---------------------- | ----------- | -| `business_transaction_id` | `TEXT` | Primary key | -| `status` | `TEXT NOT NULL` | | -| `authority_json` | `JSONB NOT NULL` | | -| `authorization_json` | `JSONB NOT NULL` | | -| `intent_json` | `JSONB NOT NULL` | | -| `metadata_json` | `JSONB NOT NULL` | | -| `policy_json` | `JSONB NOT NULL` | | -| `signals_json` | `JSONB NOT NULL` | | -| `created_at` | `TIMESTAMPTZ NOT NULL` | Indexed | - -RLS: enabled, no policies (service-role/direct-connection only). - ---- - -### `executions` - -**Why it exists:** append-only log of execution attempts for a transaction — the full -`execution_json` blob (decision, evidence, chain hash/signature — this is where the -_actual current outcome_ of a transaction lives, not `business_transactions.status`, which -is never updated after initial receipt). - -**Created:** `20260629013035_initial_schema.sql` (`execution_id` PK, `business_transaction_id` -FK `ON DELETE RESTRICT`, `execution_json JSONB`, `created_at`). Altered by `20260711120000`: -added `seq BIGSERIAL` — an exact insertion-order tiebreak, since millisecond timestamps can -tie under fast/concurrent appends and UUIDs carry no ordering. - -Indexed on `business_transaction_id` and `created_at`. RLS enabled. - -**A transaction can have multiple `executions` rows** — this is the append-only sequence, -not a single mutable status field. - ---- - -### `execution_trust_records` - -**Why it exists:** the final, signed Execution Trust Record — the artifact an independent -verifier checks. **Not every `business_transactions` row gets one** — a policy rejection -never reaches this table (it gets a `refusal_records` row instead). - -**Created:** `20260629013035_initial_schema.sql` (`trust_record_id` PK, -`business_transaction_id TEXT NOT NULL UNIQUE` + FK RESTRICT, `transaction_json`, -`trust_record_hash`, `created_at`, `updated_at`). Altered twice: - -- `20260702183000` — added `signature_json JSONB` (nullable, GIN-indexed) — persisting the - Trust Record's own signature, previously computed but not stored. -- `20260907120000` — added `authorization_json JSONB` (GIN-indexed, the - `SignedExecutionAuthorization` itself), `schema_version INTEGER`, `signatures_json JSONB` - (hybrid/post-quantum signature support — without this, a pre-hybrid record round-tripping - through this table would silently lose its second signature). - -RLS enabled. - ---- - -### `verifications` - -**Why it exists:** log of independent verification calls made against a transaction. - -**Created:** `20260629013035_initial_schema.sql` (`verification_id` PK, FK RESTRICT, -`verification_json`, `verified_at`). `seq BIGSERIAL` added `20260711120000` (same ordering -reason as `executions`). Indexed on `business_transaction_id` and `verified_at`. RLS enabled. - ---- - -### `receipts` - -**Why it exists:** signed receipts issued for a transaction. - -**Created:** `20260629013035_initial_schema.sql` (`receipt_id` PK, FK RESTRICT, -`receipt_json`, `issued_at`). `seq BIGSERIAL` added `20260711120000`. Indexed on -`business_transaction_id` and `issued_at`. RLS enabled. - ---- - -### `settlement_confirmations` - -**Why it exists:** signed confirmations that a transaction actually settled. - -**Created:** `20260718190412_add_settlement_confirmations_and_audit_severity.sql` (M4b), -"mirrors `receipts`' shape exactly." Columns: `confirmation_id TEXT` PK, -`business_transaction_id TEXT NOT NULL`, `confirmation_json JSONB NOT NULL`, -`issued_at TIMESTAMPTZ NOT NULL`, `seq BIGSERIAL`, `created_at TIMESTAMPTZ NOT NULL DEFAULT now()`. -Indexed on `business_transaction_id`. RLS enabled. Append-only, never mutated/deleted. - -**Worth knowing:** unlike every other `business_transaction_id`-bearing table created in -the initial schema, this one has **no foreign key constraint** back to `business_transactions` -— added later (M4b) rather than in the original schema. Not confirmed whether this is -deliberate or an inconsistency; worth asking before relying on referential integrity here -the way you safely can for `executions`/`verifications`/`receipts`/`execution_trust_records`. - ---- - -### `overrides` - -**Why it exists:** backing table for `OverrideService`. **Expect this table to stay empty -by design** — `OverrideService` is deliberately unreachable in this codebase (a standing -security guard documented in `02-REMAINING.md` says not to wire it up). An empty table here -is not a gap or a bug; it's the intended state. - -**Created:** `20260629013035_initial_schema.sql`, identical shape to `executions` -(`override_id` PK, FK RESTRICT, `override_json`, `created_at`; `seq BIGSERIAL` added -`20260711120000`). RLS enabled. Never altered further. - ---- - -### `refusal_records` - -**Why it exists:** the signed record of a policy or signal-binding REJECT. **Scope is -narrow**: only `PolicyEngine.evaluate` rejections and `SignalIntentBinder` binding-violation -rejections land here — caller-auth failures go to `caller_audit_events` instead, and -webhook failures go elsewhere again. Don't assume this table is "every rejection in the -system." - -**Created:** `20260802120000_add_refusal_records.sql` (RFC-0021). Columns: -`refusal_record_id` PK, `business_transaction_id TEXT NOT NULL UNIQUE` + FK RESTRICT, -`decision_json NOT NULL`, `evaluated_intent_json NOT NULL`, `binding_violations_json` -(nullable), `submitted_by` (nullable), `refusal_record_hash TEXT NOT NULL`, -`signature_json JSONB NOT NULL` — **signed from creation**, unlike `execution_trust_records` -which had an unsigned period before `20260702183000` retrofitted it. Indexed on -`created_at`. RLS enabled. - ---- - -### `caller_audit_events` - -**Why it exists:** every caller-authentication and authorization-scoping decision — the -highest-write-volume table in the system (fires on every authenticated request). This is -what tonight's whole Pfinite-onboarding debugging session was ultimately traced through. - -**Created:** `20260718090000_add_nonce_and_caller_audit_tables.sql` (G-13). **The most -altered table in the schema — 8 migrations total.** Final cumulative shape: - -| Column | Type | Added by | Notes | -| ----------------------------------------------------- | ------------------------------------ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `id` | `BIGSERIAL PRIMARY KEY` | initial | | -| `type` | `TEXT NOT NULL CHECK (...)` | initial, widened 5x | see below | -| `occurred_at` | `TIMESTAMPTZ NOT NULL` | initial | Indexed | -| `route` | `TEXT NOT NULL` | initial | | -| `caller_id` | `TEXT` | initial | nullable — present only for `caller.authenticated`. Indexed | -| `reason` | `TEXT` | initial | nullable — present only for `caller.rejected`, never a credential value | -| `inserted_at` | `TIMESTAMPTZ NOT NULL DEFAULT now()` | initial | | -| `signature_json` | `JSONB` | `20260802130000` | nullable — signed from this point forward; earlier rows stay honestly unsigned | -| `capability` | `TEXT` | `20260812120000` | nullable, indexed WHERE NOT NULL | -| `principal_id` | `TEXT` | `20260816120000` | nullable, indexed WHERE NOT NULL | -| `severity` | `TEXT` | `20260818130000` | nullable | -| `business_transaction_id` | `TEXT` | `20260824090000` | nullable, indexed WHERE NOT NULL | -| `chain_hash`, `previous_chain_hash`, `chain_position` | `TEXT`, `TEXT`, `BIGINT` | `20260906120000` | all nullable — chained **per `caller_id`** via a Postgres advisory lock on `hashtext(caller_id)`, not one global lock (avoids serializing the whole API through a single lock). Rows with no `caller_id` (malformed-body rejections) stay unchained by necessity | - -**`type`'s final allowed values** (widened 5 times — `20260812120000`, `20260816120000`, -`20260818130000`, `20260824090000`, `20260911090000`): -`'caller.authenticated'`, `'caller.rejected'`, `'caller.capability_denied'`, -`'caller.principal_denied'`, `'caller.non_human_denied'`, `'caller.structural_rejected'`, -`'caller.capability_granted'`. - -**A real bug lives in this history, worth knowing about if you ever add a new event type -here:** `20260911090000` was a _bug-fix_ migration — application code had already been -writing `'caller.capability_granted'` for a while before any migration allowed that value -in the CHECK constraint, which meant **every successful, authenticated request was failing -closed with `503 AUDIT_UNAVAILABLE`** until the constraint was widened. Adding a new -`CallerAuditEvent` type in application code and forgetting the matching migration -reproduces this exact outage. - -RLS enabled. - ---- - -### `execution_audit_events` - -**Why it exists:** durable execution-control lifecycle events (`session.created`, -`execution.completed`, `execution.rejected`). **This table is shared cross-repo**: -`parmana-paytm-agent` (a separate repository) writes to it directly via its own -`src/parmana/audit.ts`, correlated by `business_transaction_id` — this is the _only_ table -in this schema written to by code outside this repo. See `docs/connectors/PAYTM_CONNECTOR.md`. - -**Created:** `20260914120000_add_execution_audit_events.sql` (GAP-1). Altered twice: - -- `20260914130000` — added `business_transaction_id TEXT` (nullable, indexed) as **the - cross-service correlation key** (`authorization_id` is Parmana-internal and never - forwarded to `parmana-paytm-agent`); also relaxed `signature_json`/`chain_hash`/ - `chain_position` from `NOT NULL` to nullable, specifically so `parmana-paytm-agent`'s rows - — it holds no Parmana private key, only the public key, so it cannot sign or chain — can - coexist with this repo's own signed, chained rows. -- `20260914140000` — widened the `type` CHECK to add `'authorization.verified'`, the event - `parmana-paytm-agent` records right after verifying Parmana's signature. - -**Final `type` values:** `'session.created'`, `'execution.completed'`, -`'execution.rejected'`, `'authorization.verified'`. - -Full column list: `id BIGSERIAL PK`, `type`, `occurred_at TIMESTAMPTZ NOT NULL`, -`connector_id TEXT NOT NULL`, `authorization_id TEXT NOT NULL`, `session_id TEXT NOT NULL`, -`action TEXT`, `reason TEXT` (rejected-only, the connector's own thrown error message, -never a credential), `credential_id TEXT`, `gateway_id TEXT`, `signature_json JSONB`, -`chain_hash TEXT`, `previous_chain_hash TEXT`, `chain_position INTEGER`, -`inserted_at TIMESTAMPTZ DEFAULT now()`, `business_transaction_id TEXT`. - -**Chained per `authorization_id`**, not per-caller (unlike `caller_audit_events`) — "one -authorization's full execution lifecycle is the unit a regulator asks about." Indexed on -`occurred_at`, `authorization_id`, `connector_id`, `business_transaction_id`. RLS enabled. - -This is exactly the table queried tonight (by `business_transaction_id`) to find the real -reason a cross-service Paytm refund failed when Parmana's own logs only showed an opaque -`500` — see Part 2's "trace one transaction across every table" query. - ---- - -### `pending_policy_changes` - -**Why it exists:** maker-checker — a proposed policy content change awaiting a distinct -human's approval or rejection. - -**Created:** `20260818120000_add_policy_governance_tables.sql`. Columns: -`pending_policy_change_id` PK, `policy_name`/`policy_version TEXT NOT NULL`, -`proposed_content_json JSONB NOT NULL`, `proposed_by TEXT NOT NULL`, -`proposed_at TIMESTAMPTZ NOT NULL`, `status TEXT NOT NULL CHECK (status IN -('PENDING_APPROVAL','APPROVED','REJECTED'))`, `reason TEXT NOT NULL`, `resolved_by TEXT`, -`resolved_at TIMESTAMPTZ`, `rejection_reason TEXT`. - -**A real database-level invariant, not just an application check:** a partial unique index, -`ux_pending_policy_changes_open` on `(policy_name, policy_version) WHERE status='PENDING_APPROVAL'` -— enforces "at most one open change per policy" at the database level, not just in -application code. Indexed on `status`. RLS enabled. Never altered further. - ---- - -### `policy_change_approval_records` - -**Why it exists:** the signed record of a human checker's approval or rejection of a -pending policy change. - -**Created:** `20260818120000_add_policy_governance_tables.sql` alongside -`pending_policy_changes`. Columns: `policy_change_approval_record_id` PK, -`pending_policy_change_id TEXT NOT NULL REFERENCES pending_policy_changes(...) ON DELETE RESTRICT`, -`policy_name`/`policy_version`/`proposed_by`/`approved_by TEXT NOT NULL`, -`proposed_at`/`approved_at TIMESTAMPTZ NOT NULL`, `content_hash_before TEXT` (nullable), -`content_hash_after TEXT NOT NULL`, `signature_json JSONB NOT NULL`. `20260907130000` added -`previous_record_hash TEXT` (nullable) — chains the approval-record history itself, -distinct from `content_hash_after` (which only proves the live policy file matches what was -approved, not that the approval history itself is tamper-evident). - -Indexed on `(policy_name, policy_version, approved_at DESC)`. - -**RLS is not just "enabled, no policies" here** — `20260818150000` grants the `anon` role -**read-only** `SELECT` on this table specifically, for CI (`SUPABASE_ANON_KEY`), deliberately -scoped to this one table rather than reusing the application's own RLS-bypassing -`DATABASE_URL`. This is the only table in the schema with a real RLS policy attached. - -As of 2026-09-15 (`docs/operations/policy-approval-runbook.md`), this table has **zero -rows** — none of the 10 real production policies have actually been approved by a distinct -human checker yet. An empty table here is an accurate, expected state, not a bug. - ---- - -### `consumed_nonces`, `consumed_approval_nonces`, `consumed_policy_change_step_up_nonces` - -**Why three separate tables, not one:** each backs a distinct trust domain's own nonce -namespace — a Gateway Authorization envelope's nonce, an Approval Artifact's nonce, and a -human checker's step-up-key nonce, respectively — issued by different parties for different -purposes. Sharing one table would let a coincidental collision between two unrelated -namespaces falsely report "already consumed." This is a deliberate design choice, not -duplication to clean up. - -**All three share the identical shape:** - -| Column | Type | Notes | -| -------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| (nonce column) | `TEXT PRIMARY KEY` | **The primary key IS the atomicity mechanism** — concurrent inserts for the same nonce race at the database level; one wins, the loser gets Postgres error `23505 unique_violation`, mapped by application code to "already consumed." No separate locking needed. | -| `expires_at` | `TIMESTAMPTZ NOT NULL` | Indexed; not currently read back by application code — reserved for a future cleanup job that doesn't exist yet | -| `consumed_at` | `TIMESTAMPTZ DEFAULT now()` | | - -**Created:** `consumed_nonces` by `20260718090000_add_nonce_and_caller_audit_tables.sql`; -`consumed_approval_nonces` by `20260805180000_add_consumed_approval_nonces.sql`; -`consumed_policy_change_step_up_nonces` by `20260818140000_add_consumed_policy_change_step_up_nonces.sql`. -None altered since creation. RLS enabled on all three. - ---- - -### `challenge_records` - -**Why it exists:** investigation records for a disputed claim (RFC-0022) — the **only -mutable table in this entire schema**. Every other table here is append-only by convention -(some enforced at the application layer, none by a DB trigger); this one is explicitly -updated as an investigation proceeds (`status`, `investigation_steps_json`, `finding_json`, -`outcome_json`, `disclosure_json` all get updated in place). - -**Created:** `20260803150000_add_challenge_records.sql`. Columns: `challenge_record_id` PK, -`status TEXT NOT NULL CHECK (status IN ('open','investigating','resolved'))`, -`claim_challenged TEXT NOT NULL`, `source_json JSONB NOT NULL`, -`investigation_steps_json JSONB NOT NULL DEFAULT '[]'`, `finding_json`/`outcome_json`/ -`disclosure_json` (nullable JSONB), `supersedes TEXT` (deliberately **no** foreign key — -tolerant of independent pruning of the record it supersedes), `created_at`/`updated_at -TIMESTAMPTZ NOT NULL`. Indexed on `status` and `created_at`. RLS enabled. - -**Deliberately unsigned**, unlike `refusal_records`/`execution_trust_records`: "the party -who could misrepresent it is the same party who writes it; a signature would prove -tamper-evidence of bytes while leaving the actual trust question unaddressed." Append-only -history within a record is enforced at the application layer -(`PostgresChallengeRecordRepository.append`), not a database constraint. - ---- - -### `rate_limit_counters` - -**Why it exists:** fleet-wide rate-limit counters, shared across every process pointed at -the same database — the durable counterpart to `express-rate-limit`'s default in-process -`MemoryStore`. - -**Created:** `20260910120000_add_rate_limit_counters.sql`. Columns: `key TEXT PRIMARY KEY` -(a caller ID for `/execute`'s limiter, a client IP for `/health`,`/ready`'s limiter — as of -tonight's fix, **prefixed per limiter**, `"execute:"`/`"health:"`, so the two limiters' -keys can never collide even if the same raw value appeared in both), `count INTEGER NOT NULL`, -`reset_time TIMESTAMPTZ NOT NULL`. Indexed on `reset_time` (for a housekeeping job that -doesn't exist yet — the table self-corrects without one, since the upsert resets an expired -row's count on its next hit). - -**The only table in this schema without RLS enabled** — deliberately: "rows here carry no -business or trust-record data, only ephemeral counters... no end-user or dashboard access -path exists," so a restrictive policy would add operational surface without a -corresponding threat. - -See `docs/VERIFICATION-GAPS.md` G-49 for why this table's key-prefixing matters — sharing -one `Store` instance across two limiters (both ultimately backed by this same table) -violates `express-rate-limit`'s own documented contract. - ---- - -## Part 2 — Common SQL for troubleshooting, audit, and verification - -All examples assume a `pool`/`psql` session against `DATABASE_URL`. Replace -`$BUSINESS_TRANSACTION_ID` / `$CALLER_ID` / etc. with real values. - -### Trace one transaction across every table (the most useful cross-table query) - -The single most useful troubleshooting query — everything that ever happened for one -`business_transaction_id`, across every table that could reference it: - -```sql -SELECT 'business_transactions' AS source, created_at AS at, status::text AS detail - FROM business_transactions WHERE business_transaction_id = $1 -UNION ALL -SELECT 'executions', created_at, (execution_json->>'status') - FROM executions WHERE business_transaction_id = $1 -UNION ALL -SELECT 'execution_trust_records', created_at, trust_record_hash - FROM execution_trust_records WHERE business_transaction_id = $1 -UNION ALL -SELECT 'refusal_records', created_at, refusal_record_hash - FROM refusal_records WHERE business_transaction_id = $1 -UNION ALL -SELECT 'execution_audit_events', occurred_at, type || COALESCE(': ' || reason, '') - FROM execution_audit_events WHERE business_transaction_id = $1 -UNION ALL -SELECT 'caller_audit_events', occurred_at, type - FROM caller_audit_events WHERE business_transaction_id = $1 -ORDER BY at; -``` - -This is exactly the pattern used tonight to find that `parmana-paytm-agent` had recorded -`authorization.verified` for a transaction Parmana's own logs only showed a generic `500` -for — the `execution_audit_events` rows (written by _both_ repos) told the real story. - -### Trace one caller's full audit history - -```sql -SELECT type, route, occurred_at, capability, principal_id, reason -FROM caller_audit_events -WHERE caller_id = $1 -ORDER BY occurred_at DESC -LIMIT 50; -``` - -### Verify a caller's audit chain hasn't been tampered with - -```sql --- Walk chain_hash -> previous_chain_hash per caller_id; a break means either --- a gap in the chain or a mismatch between consecutive rows. -SELECT id, occurred_at, chain_hash, previous_chain_hash -FROM caller_audit_events -WHERE caller_id = $1 -ORDER BY chain_position; -``` - -### Check whether a specific capability grant/denial pattern is happening - -```sql -SELECT capability, type, COUNT(*) -FROM caller_audit_events -WHERE occurred_at > now() - interval '1 hour' -GROUP BY capability, type -ORDER BY COUNT(*) DESC; -``` - -### Find every rejection reason for a policy, recently - -```sql -SELECT decision_json->>'reason' AS reason, COUNT(*) -FROM refusal_records -WHERE created_at > now() - interval '24 hours' -GROUP BY reason -ORDER BY COUNT(*) DESC; -``` - -### Check a rate limiter's current state for a caller - -```sql -SELECT key, count, reset_time, - (reset_time > now()) AS window_still_active -FROM rate_limit_counters -WHERE key = 'execute:' || $1; -- or 'health:' || for the health/ready limiter -``` - -### Confirm a nonce was actually consumed (replay-protection check) - -```sql -SELECT nonce, consumed_at, expires_at FROM consumed_nonces WHERE nonce = $1; --- No row = never consumed (or already expired and cleaned up, if a cleanup job exists). -``` - -### Audit the maker-checker approval queue - -```sql --- Everything still awaiting a human checker -SELECT policy_name, policy_version, proposed_by, proposed_at, reason -FROM pending_policy_changes -WHERE status = 'PENDING_APPROVAL' -ORDER BY proposed_at; - --- Full approval history for one policy -SELECT approved_by, approved_at, content_hash_before, content_hash_after -FROM policy_change_approval_records -WHERE policy_name = $1 AND policy_version = $2 -ORDER BY approved_at; -``` - -### Check table row counts (quick health snapshot) - -```sql -SELECT 'business_transactions', COUNT(*) FROM business_transactions -UNION ALL SELECT 'executions', COUNT(*) FROM executions -UNION ALL SELECT 'execution_trust_records', COUNT(*) FROM execution_trust_records -UNION ALL SELECT 'caller_audit_events', COUNT(*) FROM caller_audit_events -UNION ALL SELECT 'execution_audit_events', COUNT(*) FROM execution_audit_events -UNION ALL SELECT 'refusal_records', COUNT(*) FROM refusal_records -UNION ALL SELECT 'pending_policy_changes', COUNT(*) FROM pending_policy_changes -UNION ALL SELECT 'policy_change_approval_records', COUNT(*) FROM policy_change_approval_records; -``` - -### Migration-sync verification (files vs. tracking table) - -Run this whenever migrations feel out of sync — see tonight's own real finding of 3 -untracked-but-applied migrations for why this matters: - -```bash -node -e ' -require("dotenv").config(); -const fs = require("fs"); -const { Pool } = require("pg"); -const pool = new Pool({ connectionString: process.env.DATABASE_URL, min: 1 }); -const fileVersions = fs.readdirSync("supabase/migrations") - .filter(f => f.endsWith(".sql")) - .map(f => f.split("_")[0]).sort(); -(async () => { - const { rows } = await pool.query("SELECT version FROM supabase_migrations.schema_migrations ORDER BY version"); - const dbVersions = rows.map(r => r.version).sort(); - console.log("Files:", fileVersions.length, "Tracked:", dbVersions.length); - console.log("In files, not tracked:", fileVersions.filter(v => !dbVersions.includes(v))); - console.log("Tracked, no file:", dbVersions.filter(v => !fileVersions.includes(v))); - await pool.end(); -})(); -' -``` - -### List all live tables and their RLS status - -```sql -SELECT c.relname AS table_name, c.relrowsecurity AS rls_enabled -FROM pg_class c -JOIN pg_namespace n ON n.oid = c.relnamespace -WHERE n.nspname = 'public' AND c.relkind = 'r' -ORDER BY c.relname; -``` - -### Check a table's actual CHECK constraints (don't trust the migration file alone — several have been widened repeatedly) - -```sql -SELECT conname, pg_get_constraintdef(oid) AS definition -FROM pg_constraint -WHERE conrelid = 'caller_audit_events'::regclass AND contype = 'c'; -``` - -### Find foreign keys pointing at a table (before ever dropping one) - -```sql -SELECT tc.table_name AS referencing_table, tc.constraint_name -FROM information_schema.table_constraints tc -JOIN information_schema.constraint_column_usage ccu - ON tc.constraint_name = ccu.constraint_name -WHERE tc.constraint_type = 'FOREIGN KEY' AND ccu.table_name = $1; -``` diff --git a/docs/architecture/EXECUTION-FLOW-AUDIT.md b/docs/architecture/EXECUTION-FLOW-AUDIT.md deleted file mode 100644 index f52eed38..00000000 --- a/docs/architecture/EXECUTION-FLOW-AUDIT.md +++ /dev/null @@ -1,15773 +0,0 @@ -\# Execution Flow Audit - -> Status: Complete (Implementation Verified) - -> - -> Purpose: - -> - -> This document is the canonical implementation audit of Parmana's execution - -> pipeline. It traces the execution flow through the repository using the - -> actual implementation. - -> - -> Every statement in this document must be backed by source code. - -> No assumptions. No future architecture. No marketing language. - -\--- - -\# Audit Rules - -\- Document only the current implementation. - -\- Every stage must reference its source file. - -\- Do not speculate. - -\- Record audit observations separately from implementation. - -\- Every stage should eventually have associated tests. - -\--- - -\# High-Level Flow - -``` - -Client - - │ - - ▼ - -HTTP Request - - │ - - ▼ - -Express Route - - │ - - ▼ - -BusinessTransactionMapper - - │ - - ▼ - -ExecutionTrustApplication - - │ - - ▼ - -Runtime - - │ - - ▼ - -RuntimeEngine - - │ - - ▼ - -Runtime Pipeline - - │ - - ▼ - -ExecutionComponent - - │ - - ▼ - -ExecutionSystem - - │ - - ▼ - -Execution Gateway - - │ - - ▼ - -Connector - - │ - - ▼ - -Enterprise System - - │ - - ▼ - -Execution Receipt - - │ - - ▼ - -Verification - - │ - - ▼ - -Execution Trust Record - -``` - -\--- - -\# Stage 1: HTTP Entry - -\## File - -``` - -packages/api/src/routes/execute.ts - -``` - -\## Responsibility - -Accept HTTP execution requests. - -\## Input - -HTTP POST request. - -\## Output - -BusinessTransaction. - -\## Verified Behaviour - -\- Validates `businessTransactionId`. - -\- Rejects invalid UUID with HTTP 400. - -\- Delegates object creation to `BusinessTransactionMapper`. - -\- Calls `ExecutionTrustApplication.execute()`. - -\- Returns the resulting Execution Trust Record. - -\## Calls - -``` - -BusinessTransactionMapper.fromRequest() - - - -↓ - - - -ExecutionTrustApplication.execute() - -``` - -\## Audit Notes - -Current route validates only the transaction identifier. - -Further validation points will be identified during the runtime audit. - -Status: - -✅ Verified - -\--- - -\# Stage 2: Business Transaction Mapper - -\## File - -``` - -packages/api/src/mappers/BusinessTransactionMapper.ts - -``` - -\## Responsibility - -Convert HTTP request payload into an immutable - -BusinessTransaction. - -\## Input - -Request payload. - -\## Output - -BusinessTransaction. - -\## Verified Behaviour - -Copies: - -\- metadata - -\- authority - -\- authorization - -\- intent - -\- policy - -\- signals - -Creates server-owned fields: - -\- status = RECEIVED - -\- createdAt = current server time - -\## Does NOT - -\- Execute policy - -\- Validate policy - -\- Execute business logic - -\- Persist data - -\- Generate receipts - -Status: - -✅ Verified - -\--- - -\# Stage 3: Application Composition - -\## File - -``` - -packages/api/src/application.ts - -``` - -\## Responsibility - -Compose the application. - -\## Creates - -\- PolicyRepository - -\- BusinessTransactionRepository - -\- ExecutionTrustRecordRepository - -\- ExecutionTrustApplication - -Uses - -``` - -RuntimeFactory.create(...) - -``` - -\## Audit Notes - -Contains dependency composition only. - -No execution logic. - -Status: - -✅ Verified - -\--- - -\# Stage 4: Runtime Factory - -\## File - -``` - -packages/runtime/src/RuntimeFactory.ts - -``` - -\## Responsibility - -Construct the complete runtime. - -\## Creates - -Application Services - -\- BusinessTransactionService - -\- ExecutionService - -\- VerificationService - -\- ReceiptService - -Builders - -\- ExecutionRequestBuilder - -\- ExecutionEvidenceBuilder - -Runtime Pipeline - -\- TrustChainValidationComponent - -\- ExecutionComponent - -Application - -\- ExecutionTrustApplication - -\## Audit Notes - -Factory performs dependency composition only. - -No runtime execution occurs here. - -Status: - -✅ Verified - -\--- - -\# Stage 5: ExecutionTrustApplication - -\## File - -``` - -packages/runtime/src/ExecutionTrustApplication.ts - -``` - -\## Responsibility - -Orchestrate the complete execution lifecycle. - -\## Execution Sequence - -``` - -Accept Transaction - - - -↓ - - - -Runtime.execute() - - - -↓ - - - -Verification.verify() - - - -↓ - - - -Receipt.generate() - - - -↓ - - - -Load Execution Trust Record - - - -↓ - - - -Return Execution Trust Record - -``` - -\## Public Operations - -\- execute() - -\- verify() - -\- replay() - -\- generateReceipt() - -\- getTrustRecord() - -\- getTransaction() - -\- listTransactions() - -\## Audit Notes - -Acts as an orchestration layer. - -Delegates all domain behaviour to dedicated services. - -Status: - -✅ Verified - -\--- - -\# Stage 6: Runtime - -\## File - -``` - -packages/runtime/src/Runtime.ts - -``` - -\## Responsibility - -Execute RuntimeEngine and persist the resulting - -Execution Trust Record. - -\## Execution Sequence - -``` - -RuntimeEngine.execute() - - - -↓ - - - -Persist Trust Record - - - -↓ - - - -Return RuntimeResult - -``` - -\## Audit Notes - -Runtime is a thin façade. - -Execution occurs inside RuntimeEngine. - -Status: - -✅ Verified - -\--- - -\# Stage 7: Runtime Engine - -\## File - -``` - -packages/runtime/src/RuntimeEngine.ts - -``` - -\## Responsibility - -The Runtime Engine is the core orchestration engine of Parmana. - -It is responsible for: - -\- Loading policies. - -\- Evaluating policies. - -\- Building execution artifacts. - -\- Creating execution authorization. - -\- Executing the Runtime Pipeline. - -\- Producing the final Execution Trust Record. - -It contains the complete deterministic execution flow. - -Status: - -✅ Verified - -\--- - -\## Execution Sequence - -``` - -BusinessTransaction - - │ - - ▼ - -Extract Signals - - │ - - ▼ - -Load Policy - - │ - - ▼ - -Evaluate Policy - - │ - - ▼ - -Build Decision - - │ - - ▼ - -Execution Gate - - │ - - ▼ - -Build Executable Content - - │ - - ▼ - -Sign Authorization - - │ - - ▼ - -Build Execution Artifact - - │ - - ▼ - -Create Runtime Context - - │ - - ▼ - -Runtime Pipeline - - │ - - ▼ - -Business Trust Pipeline - - │ - - ▼ - -Execution Trust Record - -``` - -\--- - -\## Step 1: Extract Runtime Signals - -Input - -``` - -BusinessTransaction.signals - -``` - -Output - -``` - -Record - -``` - -Purpose - -Provide deterministic input to policy evaluation. - -Status: - -✅ Verified - -\--- - -\## Step 2: Load Policy - -Component - -``` - -PolicyRouter - -``` - -Input - -``` - -policy.name - -policy.version - -``` - -Output - -``` - -Runtime Policy - -``` - -Purpose - -Resolve the exact policy requested by the Business Transaction. - -Hooks - -\- beforePolicyLoad() - -\- afterPolicyLoad() - -Status: - -✅ Verified - -\--- - -\## Step 3: Evaluate Policy - -Component - -``` - -PolicyEngine - -``` - -Input - -\- Policy - -\- Runtime Signals - -Output - -``` - -PolicyDecision - -``` - -Hooks - -\- beforePolicyEvaluation() - -\- afterPolicyEvaluation() - -Status: - -✅ Verified - -\--- - -\## Step 4: Build Decision - -Component - -``` - -DecisionBuilder - -``` - -Input - -\- Business Transaction - -\- Policy Decision - -Output - -``` - -Decision - -``` - -Status: - -✅ Verified - -\--- - -\## Step 5: Execution Gate - -Component - -``` - -ExecutionGate - -``` - -Input - -``` - -Decision - -``` - -Purpose - -Enforce execution before authorization is created. - -Status: - -✅ Verified - -\--- - -\## Step 6: Executable Content - -Component - -``` - -toExecutableContent(...) - -``` - -Produces the canonical executable payload. - -Contains - -\- businessTransactionId - -\- action - -\- target - -\- parameters - -Purpose - -Create the immutable content that will be authorized. - -Status: - -✅ Verified - -\--- - -\## Step 7: Runtime Authorization - -Component - -``` - -RuntimeAuthorizationSigner - -``` - -Input - -\- Decision ID - -\- Transaction ID - -\- Policy - -\- Executable Content - -Output - -``` - -Execution Authorization - -``` - -Hooks - -\- beforeAuthorization() - -\- afterAuthorization() - -Status: - -✅ Verified - -\--- - -\## Step 8: Execution Artifact - -Component - -``` - -ExecutionBuilder - -``` - -Output - -``` - -Execution - -``` - -Status: - -✅ Verified - -\--- - -\## Step 9: Runtime Context - -Creates - -``` - -RuntimeContext - -``` - -Contains - -\- Transaction - -\- Decision - -\- Authorization - -\- Execution - -Purpose - -Shared context passed between all Runtime Pipeline stages. - -Status: - -✅ Verified - -\--- - -\## Step 10: Runtime Pipeline - -Component - -``` - -RuntimePipeline - -``` - -Input - -``` - -RuntimeContext - -``` - -Output - -``` - -Processed RuntimeContext - -``` - -Purpose - -Execute every configured Runtime Component. - -Status: - -✅ Verified - -\--- - -\## Step 11: Business Trust Pipeline - -Component - -``` - -BusinessTrustPipeline - -``` - -Input - -Processed Runtime Context - -Output - -``` - -ExecutionTrustRecord - -``` - -Purpose - -Generate the final immutable Execution Trust Record. - -Status: - -✅ Verified - -\--- - -\## Runtime Hooks - -The Runtime Engine exposes lifecycle hooks before and after every major stage. - -Verified hook points - -\- Policy Load - -\- Policy Evaluation - -\- Decision - -\- Authorization - -\- Execution - -\- Trust Record - -\- Runtime Error - -This provides extension points without changing the Runtime Engine itself. - -Status: - -✅ Verified - -\--- - -\## Audit Notes - -The Runtime Engine is deterministic orchestration. - -It does not perform transport handling. - -It does not perform persistence. - -It does not expose HTTP concerns. - -It coordinates policy evaluation, authorization, execution, and trust record creation through explicit stages. - -\--- - -\--- - -\# Stage 8: Runtime Pipeline - -\## File - -``` - -packages/runtime/src/RuntimePipeline.ts - -``` - -\## Responsibility - -Execute Runtime Components in deterministic order. - -The Runtime Pipeline contains no business logic. - -Its only responsibility is to orchestrate Runtime Components sequentially. - -Status: - -✅ Verified - -\--- - -\## Execution Flow - -``` - -RuntimeContext - - │ - - ▼ - -Component 1 - - │ - - ▼ - -Component 2 - - │ - - ▼ - -Component N - - │ - - ▼ - -Processed RuntimeContext - -``` - -\--- - -\## Pipeline Guarantees - -Verified from implementation. - -\- Deterministic execution order. - -\- Sequential execution. - -\- No component reordering. - -\- Fail-fast execution. - -\- Shared RuntimeContext passed between every stage. - -Status: - -✅ Verified - -\--- - -\## Execution Algorithm - -Implementation - -``` - -current = context - - - -for each component - - - - current = component.execute(current) - - - -return current - -``` - -Status: - -✅ Verified - -\--- - -\## RuntimeContext - -Input - -``` - -RuntimeContext - -``` - -Output - -``` - -RuntimeContext - -``` - -Every Runtime Component receives the complete Runtime Context and returns the updated Runtime Context. - -No component communicates directly with another component. - -Communication occurs only through RuntimeContext. - -Status: - -✅ Verified - -\--- - -\## Component Registration - -Components are injected through the constructor. - -The pipeline never creates Runtime Components. - -This responsibility belongs to RuntimeFactory. - -Status: - -✅ Verified - -\--- - -\## Audit Notes - -The Runtime Pipeline is intentionally minimal. - -It provides deterministic orchestration only. - -It does not: - -\- evaluate policy - -\- authorize execution - -\- execute connectors - -\- generate receipts - -\- verify trust - -Those responsibilities belong to Runtime Components. - -\--- - -\# Stage 9: Trust Chain Validation Component - -\## File - -``` - -packages/runtime/src/components/TrustChainValidationComponent.ts - -``` - -\## Responsibility - -Validate the minimum trust requirements before execution may continue. - -This component does \*\*not\*\* evaluate business policy. - -It validates that all mandatory trust artifacts already exist. - -Status: - -✅ Verified - -\--- - -\## Position in Pipeline - -``` - -RuntimeContext - - │ - - ▼ - -TrustChainValidationComponent - - │ - - ▼ - -ExecutionComponent - -``` - -\--- - -\## Input - -``` - -RuntimeContext - -``` - -\--- - -\## Output - -``` - -RuntimeContext - -``` - -No new objects are created. - -The component either: - -\- returns the RuntimeContext unchanged - -\- throws an exception - -\--- - -\## Validation Sequence - -``` - -Authority - - │ - - ▼ - -Authorization - - │ - - ▼ - -Intent - - │ - - ▼ - -Policy - - │ - - ▼ - -Decision - - │ - - ▼ - -Decision Approved - - │ - - ▼ - -Execution Allowed - -``` - -Status: - -✅ Verified - -\--- - -\## Validation Rules - -\### Authority - -Required. - -Failure - -``` - -AuthorityRequiredError - -``` - -\--- - -\### Authorization - -Required. - -Failure - -``` - -AuthorizationRequiredError - -``` - -\--- - -\### Intent - -Required. - -Failure - -``` - -IntentRequiredError - -``` - -\--- - -\### Policy - -Required. - -Failure - -``` - -ValidationError - - - -POLICY\_REQUIRED - -``` - -\--- - -\### Decision - -Required. - -Failure - -``` - -ValidationError - - - -DECISION\_REQUIRED - -``` - -\--- - -\### Decision Outcome - -Only - -``` - -APPROVED - -``` - -may continue. - -Any other outcome throws - -``` - -DecisionNotApprovedError - -``` - -\--- - -\## Audit Notes - -This component validates trust prerequisites. - -It does \*\*not\*\* - -\- load policies - -\- evaluate policy - -\- execute systems - -\- generate receipts - -\- verify trust records - -Its only responsibility is deciding whether the Runtime Context is complete enough to continue. - -Status: - -✅ Verified - -\--- - -\# Stage 10: Execution Component - -\## File - -``` - -packages/runtime/src/components/ExecutionComponent.ts - -``` - -\## Responsibility - -Execute an approved Business Transaction through the configured Execution System. - -This component is responsible for: - -\- Creating the Execution artifact. - -\- Building the approved Execution Request. - -\- Invoking the configured Execution System. - -\- Building immutable Execution Evidence. - -\- Attaching execution evidence. - -\- Completing or failing execution. - -Status: - -✅ Verified - -\--- - -\## Position in Pipeline - -``` - -TrustChainValidationComponent - - │ - - ▼ - -ExecutionComponent - -``` - -\--- - -\## Execution Sequence - -``` - -RuntimeContext - - │ - - ▼ - -Validate Decision - - │ - - ▼ - -Validate Authorization - - │ - - ▼ - -Create Execution Artifact - - │ - - ▼ - -Build Execution Request - - │ - - ▼ - -ExecutionSystem.execute() - - │ - - ▼ - -Execution Response - - │ - - ▼ - -Execution Evidence - - │ - - ▼ - -Attach Evidence - - │ - - ▼ - -Complete Execution - - │ - - ▼ - -Updated RuntimeContext - -``` - -\--- - -\## Preconditions - -Execution requires - -\- Decision - -\- Signed Runtime Authorization - -Failure - -``` - -Decision missing - - - -↓ - - - -Error - -``` - -or - -``` - -Authorization missing - - - -↓ - - - -Error - -``` - -\--- - -\## Step 1 - -Create Execution Artifact - -Component - -``` - -ExecutionService.create() - -``` - -Purpose - -Create an Execution object before execution begins. - -Execution Mode - -``` - -SYNC - -``` - -Status: - -✅ Verified - -\--- - -\## Step 2 - -Build Execution Request - -Component - -``` - -ExecutionRequestBuilder - -``` - -Input - -\- BusinessTransaction - -\- Runtime Authorization - -Output - -``` - -ExecutionRequest - -``` - -Status: - -✅ Verified - -\--- - -\## Step 3 - -Invoke Execution System - -Component - -``` - -ExecutionSystem.execute() - -``` - -Input - -ExecutionRequest - -Output - -ExecutionResponse - -Status: - -✅ Verified - -\--- - -\## Step 4 - -Build Execution Evidence - -Component - -``` - -ExecutionEvidenceBuilder - -``` - -Input - -ExecutionResponse - -Output - -ExecutionEvidence - -Status: - -✅ Verified - -\--- - -\## Step 5 - -Attach Evidence - -Component - -``` - -ExecutionService.attachEvidence() - -``` - -Purpose - -Bind immutable evidence to the Execution artifact. - -Status: - -✅ Verified - -\--- - -\## Step 6 - -Complete Execution - -Component - -``` - -ExecutionService.complete() - -``` - -Marks execution as completed. - -Status: - -✅ Verified - -\--- - -\## Failure Path - -If any exception occurs - -``` - -ExecutionService.fail() - - - -↓ - - - -rethrow exception - -``` - -Execution failures are recorded before the error propagates. - -Status: - -✅ Verified - -\--- - -\## Output - -Returns - -``` - -RuntimeContext - -``` - -with updated - -``` - -execution - -``` - -artifact. - -\--- - -\# Stage 11: Execution System - -\## File - -``` - -packages/execution-system/src/ExecutionSystem.ts - -``` - -\## Responsibility - -Define the execution boundary between Parmana and external execution systems. - -The Execution System is an abstraction. - -It defines the contract that every execution implementation must satisfy. - -Parmana never executes enterprise operations directly. - -Instead, it delegates approved execution requests to an implementation of this interface. - -Status: - -✅ Verified - -\--- - -\## Interface - -```typescript - -interface ExecutionSystem { - - execute( - - request: ExecutionRequest - - ): Promise; - -} - -``` - -\--- - -\## Input - -``` - -ExecutionRequest - -``` - -\--- - -\## Output - -``` - -ExecutionResult - -``` - -\--- - -\## Responsibilities - -The Execution System is responsible for: - -\- Accepting an approved Execution Request. - -\- Executing the request against the configured execution target. - -\- Returning an Execution Result. - -It is \*\*not\*\* responsible for: - -\- Policy evaluation. - -\- Decision creation. - -\- Authorization generation. - -\- Trust record generation. - -\- Receipt generation. - -\- Verification. - -Those responsibilities remain within the Parmana Runtime. - -Status: - -✅ Verified - -\--- - -\## Architectural Boundary - -``` - -Parmana Runtime - - │ - - ▼ - -ExecutionRequest - - │ - -══════════════════════════════════ - -Execution System Boundary - -══════════════════════════════════ - - │ - - ▼ - -ExecutionSystem.execute() - - │ - - ▼ - -ExecutionResult - - │ - -══════════════════════════════════ - -Back into Parmana Runtime - -══════════════════════════════════ - - │ - - ▼ - -Execution Evidence - - │ - - ▼ - -Execution Trust Record - -``` - -\--- - -\## Audit Notes - -ExecutionSystem is an interface. - -It contains no business logic. - -Concrete implementations determine how execution reaches enterprise systems. - -This abstraction decouples Parmana's governance runtime from specific execution technologies. - -Status: - -✅ Verified - -\--- - -\# Stage 12: Execution System Implementations - -\## Interface - -``` - -packages/execution-system/src/ExecutionSystem.ts - -``` - -\## Verified Implementations - -\### Production - -``` - -packages/execution-gateway/src/ExecutionGateway.ts - -``` - -Implements - -``` - -ExecutionSystem - -``` - -Status - -✅ Verified - -\--- - -\### Default Implementation - -``` - -packages/execution-system/src/DefaultExecutionSystem.ts - -``` - -Implements - -``` - -ExecutionSystem - -``` - -Status - -✅ Verified - -\--- - -\### HTTP Implementation - -``` - -packages/execution-system/src/HttpExecutionSystem.ts - -``` - -Implements - -``` - -ExecutionSystem - -``` - -Status - -✅ Verified - -\--- - -\### Test Implementations - -``` - -packages/api/tests/helpers/FailingExecutionSystem.ts - - - -packages/runtime/tests/unit/execution-authorization-wiring.test.ts - -``` - -Used only for testing. - -Status - -✅ Verified - -\--- - -\## Audit Conclusion - -The Runtime depends only on the `ExecutionSystem` interface. - -Concrete execution behavior is supplied through dependency injection. - -This allows Parmana to support multiple execution strategies without changing the Runtime. - -\--- - -\# Stage 13: Execution Gateway - -\## File - -``` - -packages/execution-gateway/src/ExecutionGateway.ts - -``` - -\## Responsibility - -The Execution Gateway is the production implementation of the - -`ExecutionSystem` interface. - -It is the final verification boundary before any request is released - -to an external execution target. - -Status: - -✅ Verified - -\--- - -\## Position in Architecture - -``` - -Runtime - - │ - - ▼ - -ExecutionComponent - - │ - - ▼ - -ExecutionGateway - - │ - - ▼ - -Connector / ExecutionControl - - │ - - ▼ - -Enterprise System - -``` - -\--- - -\## Responsibilities - -The Execution Gateway: - -\- verifies the signed authorization envelope - -\- verifies executable content integrity - -\- prevents replay attacks - -\- freezes the executable payload - -\- forwards only verified requests - -\- rejects every failed verification - -It does \*\*not\*\*: - -\- evaluate policy - -\- create decisions - -\- create authorizations - -\- generate trust records - -Status: - -✅ Verified - -\--- - -\## Verification Pipeline - -``` - -ExecutionRequest - - │ - - ▼ - -Envelope Verification - - │ - - ▼ - -Executable Content Hash Verification - - │ - - ▼ - -Replay Protection (Nonce) - - │ - - ▼ - -Deep Freeze Transaction - - │ - - ▼ - -Connector / ExecutionControl - -``` - -Status: - -✅ Verified - -\--- - -\## Verification Order - -The implementation performs verification in the following order. - -1\. Envelope verification - - - signature - - - version - - - expiry - - - TTL - -2\. Executable content hash verification - -3\. Nonce consumption - -4\. Release to execution - -The nonce is intentionally consumed last. - -Status: - -✅ Verified - -\--- - -\## Executable Content Verification - -The gateway reconstructs the canonical executable content from the - -Execution Request. - -It independently hashes the reconstructed content and compares it to - -the hash embedded in the signed Runtime Authorization. - -If they differ: - -``` - -Execution rejected - -``` - -Status: - -✅ Verified - -\--- - -\## Replay Protection - -Replay protection is performed only after every deterministic - -verification succeeds. - -A failed request never consumes a nonce. - -Status: - -✅ Verified - -\--- - -\## Immutable Release - -Before forwarding execution: - -``` - -deepFreeze(executableContent) - -``` - -is applied. - -The forwarded transaction becomes immutable. - -Status: - -✅ Verified - -\--- - -\## Release Targets - -Exactly one execution strategy is configured. - -\### Connector Mode - -``` - -Connector.execute(...) - -``` - -\### Controlled Execution Mode - -``` - -ExecutionControl.execute(...) - -``` - -or - -``` - -ExecutionChannel.release(...) - -``` - -The gateway rejects configurations that attempt to use both. - -Status: - -✅ Verified - -\--- - -\## Failure Behaviour - -Every verification failure results in rejection. - -The gateway returns detailed failure information including: - -\- failed verification stages - -\- hash mismatches - -\- replay failures - -No execution is forwarded. - -Status: - -✅ Verified - -\--- - -\## Audit Notes - -The Execution Gateway is the final mechanical enforcement point before - -enterprise execution. - -It converts Runtime Authorization into controlled execution. - -It is the primary security boundary of the Parmana execution model. - -\--- - -\# Stage 14: Business Trust Pipeline - -\## File - -``` - -packages/runtime/src/BusinessTrustPipeline.ts - -``` - -\## Responsibility - -⏳ Pending Audit - -Expected responsibilities: - -\- Assemble the final Execution Trust Record. - -\- Execute trust-building stages. - -\- Produce the immutable trust artifact. - -Questions to verify - -\- How is the Execution Trust Record built? - -\- What builders are used? - -\- What evidence is included? - -\- Is hashing performed here? - -\- Is signing performed here? - -\- Is persistence performed here? - -Status - -⏳ Pending - -\--- - -\# Stage 14: Business Trust Pipeline - -\## File - -``` - -packages/runtime/src/BusinessTrustPipeline.ts - -``` - -\## Responsibility - -Assemble the canonical immutable Execution Trust Record. - -The Business Trust Pipeline does \*\*not\*\* perform business logic. - -It validates that the Runtime has already produced the required artifacts - -and delegates trust record construction to the BusinessTrustRecordBuilder. - -Status - -✅ Verified - -\--- - -\## Position in Execution Flow - -``` - -RuntimeContext - - │ - - ▼ - -BusinessTrustPipeline - - │ - - ▼ - -BusinessTrustRecordBuilder - - │ - - ▼ - -ExecutionTrustRecord - -``` - -\--- - -\## Input - -``` - -RuntimeContext - -``` - -Expected artifacts - -\- Business Transaction - -\- Execution - -Optional artifacts - -\- Override - -\- Verification - -\- Receipt - -Status - -✅ Verified - -\--- - -\## Validation - -The pipeline verifies the minimum required runtime artifacts. - -Required - -``` - -BusinessTransaction - -Execution - -``` - -Failure - -``` - -Business Transaction is required. - - - -Execution artifact is required. - -``` - -Status - -✅ Verified - -\--- - -\## Execution - -After validation - -``` - -BusinessTrustRecordBuilder.build(context) - -``` - -is invoked. - -The pipeline itself contains no assembly logic. - -Status - -✅ Verified - -\--- - -\## Responsibilities - -The pipeline - -✓ validates runtime completeness - -✓ delegates trust record construction - -✓ returns the completed Execution Trust Record - -The pipeline does NOT - -✗ hash - -✗ sign - -✗ evaluate policy - -✗ execute connectors - -✗ persist data - -Status - -✅ Verified - -\--- - -\## Audit Notes - -BusinessTrustPipeline is intentionally minimal. - -Its responsibility is orchestration, not construction. - -All trust record creation is delegated to BusinessTrustRecordBuilder. - -\--- - -\# Stage 15: Business Trust Record Builder - -\## File - -``` - -packages/runtime/src/BusinessTrustRecordBuilder.ts - -``` - -\## Responsibility - -Construct the canonical immutable `ExecutionTrustRecord`. - -This builder assembles all runtime artifacts, computes the trust record hash, digitally signs the completed record, and returns the final immutable trust artifact. - -Status - -✅ Verified - -\--- - -\## Position in Execution Flow - -``` - -RuntimeContext - - │ - - ▼ - -BusinessTrustRecordBuilder - - │ - - ▼ - -Draft Trust Record - - │ - - ▼ - -Hash - - │ - - ▼ - -Digital Signature - - │ - - ▼ - -ExecutionTrustRecord - -``` - -\--- - -\## Input - -``` - -RuntimeContext - -``` - -Required - -\- Business Transaction - -\- Execution - -Optional - -\- Override - -\- Verification - -\- Receipt - -Status - -✅ Verified - -\--- - -\## Assembly - -The builder assembles: - -``` - -trustRecordId - - - -businessTransactionId - - - -transaction - - - -overrides\[] - - - -executions\[] - - - -verifications\[] - - - -receipts\[] - - - -createdAt - - - -updatedAt - -``` - -Status - -✅ Verified - -\--- - -\## Phase 1: Draft Record - -A draft trust record is created. - -At this point - -``` - -trustRecordHash = "" - - - -signature.value = "" - -``` - -This temporary structure exists only to create a deterministic hash. - -Status - -✅ Verified - -\--- - -\## Phase 2: Trust Record Hash - -Component - -``` - -VerificationCrypto.hash(...) - -``` - -Output - -``` - -trustRecordHash - -``` - -The hash is calculated before the signature is created. - -Status - -✅ Verified - -\--- - -\## Phase 3: Digital Signature - -Component - -``` - -VerificationCrypto.sign(...) - -``` - -Input - -Record including - -``` - -trustRecordHash - -``` - -Output - -``` - -signature - -``` - -Status - -✅ Verified - -\--- - -\## Phase 4: Final Record - -The builder returns - -``` - -ExecutionTrustRecord - -``` - -containing - -\- all runtime artifacts - -\- trustRecordHash - -\- digital signature - -Status - -✅ Verified - -\--- - -\## Responsibilities - -The builder - -✓ assembles runtime artifacts - -✓ computes trust record hash - -✓ digitally signs the record - -✓ returns immutable trust artifact - -The builder does NOT - -✗ evaluate policy - -✗ execute systems - -✗ persist records - -✗ verify signatures - -Status - -✅ Verified - -\--- - -\## Audit Notes - -This is the final construction step of the execution lifecycle. - -Every preceding stage exists to produce artifacts consumed by this builder. - -\--- - -\# Stage 16: Execution Request Builder - -\## File - -``` - -packages/runtime/src/ExecutionRequestBuilder.ts - -``` - -\## Responsibility - -⏳ Pending Audit - -Construct the canonical ExecutionRequest passed from the Runtime to the Execution Gateway. - -Questions to verify - -\- What fields are included? - -\- Is the Runtime Authorization embedded? - -\- Is executable content copied or reconstructed? - -\- Is any mutable state included? - -\- What guarantees does the builder provide? - -Status - -⏳ Pending - -\--- - -\# Stage 16: Execution Request Builder - -\## File - -``` - -packages/runtime/src/ExecutionRequestBuilder.ts - -``` - -\## Responsibility - -Construct the canonical `ExecutionRequest` passed from the Runtime to the configured `ExecutionSystem`. - -The builder performs structural mapping only. - -It does not: - -\- evaluate policy - -\- create authorization - -\- execute requests - -\- verify signatures - -\- mutate the transaction - -Status - -✅ Verified - -\--- - -\## Position in Execution Flow - -``` - -BusinessTransaction - - │ - - ▼ - -Signed Runtime Authorization - - │ - - ▼ - -ExecutionRequestBuilder - - │ - - ▼ - -ExecutionRequest - - │ - -══════════════════════════════════════ - -Execution Boundary - -══════════════════════════════════════ - - │ - - ▼ - -ExecutionGateway - -``` - -\--- - -\## Input - -``` - -BusinessTransaction - - - -SignedExecutionAuthorization - -``` - -Status - -✅ Verified - -\--- - -\## Output - -``` - -ExecutionRequest - -``` - -Status - -✅ Verified - -\--- - -\## ExecutionRequest Structure - -The builder constructs: - -``` - -businessTransactionId - - - -action - - - -target - - - -parameters - - - -authorization - -``` - -Status - -✅ Verified - -\--- - -\## Source Mapping - -| ExecutionRequest | Source | - -|------------------|--------| - -| businessTransactionId | transaction.businessTransactionId | - -| action | transaction.intent.action | - -| target | transaction.intent.target | - -| parameters | transaction.intent.parameters | - -| authorization | SignedExecutionAuthorization | - -Status - -✅ Verified - -\--- - -\## Security Properties - -The builder does not create executable content. - -Instead it copies the already-approved intent together with the Runtime Authorization. - -This ensures the Execution Gateway independently reconstructs the canonical executable content before verification. - -Status - -✅ Verified - -\--- - -\## Audit Notes - -The ExecutionRequest is intentionally minimal. - -\## Security Properties - -The builder does not create executable content. - -Instead it copies the already-approved intent together with the Runtime Authorization. - -This ensures the Execution Gateway independently reconstructs the canonical executable content before verification. - -Status - -✅ Verified - -\--- - -\## Architectural Notes - -`ExecutionRequest` is intentionally a \*\*transport object\*\*, not a domain object. - -It is the canonical message exchanged between the Runtime and the Execution System. - -The Execution Gateway independently reconstructs the canonical `ExecutableContent` from the `ExecutionRequest` before verifying its integrity and authorization. - -This design ensures the Gateway verifies the actual execution payload rather than trusting serialized Runtime state. - -Status - -✅ Verified - -\--- - -\## Audit Notes - -The ExecutionRequest is intentionally minimal. - -It contains only: - -\- businessTransactionId - -\- action - -\- target - -\- parameters - -\- authorization - -It does not contain: - -\- Policy - -\- Decision - -\- RuntimeContext - -\- ExecutionTrustRecord - -This minimizes the data crossing the Runtime → Execution System boundary while preserving deterministic verification. - -Status - -✅ Verified - -\--- - -\# Stage 17: Execution Evidence Builder - -\## File - -``` - -packages/runtime/src/ExecutionEvidenceBuilder.ts - -``` - -\## Responsibility - -⏳ Pending Audit - -Construct immutable Execution Evidence from the Execution Result returned by the configured Execution System. - -Questions to verify - -\- What evidence is preserved? - -\- Is the original response copied or normalized? - -\- Are timestamps generated here? - -\- Is hashing performed? - -\- Is evidence immutable? - -Status - -⏳ Pending - -\--- - -\# Stage 17: Execution Evidence Builder - -\## File - -``` - -packages/runtime/src/ExecutionEvidenceBuilder.ts - -``` - -\## Responsibility - -Construct the canonical `ExecutionEvidence` from the `ExecutionResult` returned by the configured `ExecutionSystem`. - -The builder performs structural mapping only. - -It does not: - -\- execute business operations - -\- verify signatures - -\- hash evidence - -\- persist evidence - -\- mutate execution state - -Status - -✅ Verified - -\--- - -\## Position in Execution Flow - -``` - -ExecutionResult - - │ - - ▼ - -ExecutionEvidenceBuilder - - │ - - ▼ - -ExecutionEvidence - - │ - - ▼ - -ExecutionService.attachEvidence() - - │ - - ▼ - -Execution Artifact - - │ - - ▼ - -BusinessTrustRecordBuilder - -``` - -\--- - -\## Input - -``` - -ExecutionResult - -``` - -Status - -✅ Verified - -\--- - -\## Output - -``` - -ExecutionEvidence - -``` - -Status - -✅ Verified - -\--- - -\## Evidence Structure - -The builder constructs: - -``` - -businessTransactionId - - - -action - - - -target - - - -parameters - - - -success - - - -executedAt - - - -attributes (optional) - -``` - -Status - -✅ Verified - -\--- - -\## Source Mapping - -| ExecutionEvidence | Source | - -|-------------------|--------| - -| businessTransactionId | result.businessTransactionId | - -| action | result.action | - -| target | result.target | - -| parameters | result.parameters | - -| success | result.success | - -| executedAt | result.executedAt | - -| attributes | result.metadata | - -Status - -✅ Verified - -\--- - -\## Security Properties - -The builder preserves the execution outcome exactly as returned by the configured `ExecutionSystem`. - -It performs no enrichment, filtering, or transformation beyond structural mapping. - -This minimizes the risk of altering execution evidence before it becomes part of the Execution Trust Record. - -Status - -✅ Verified - -\--- - -\## Architectural Notes - -`ExecutionEvidence` is an immutable evidence artifact. - -It is \*\*not\*\* the raw connector response. - -Instead, it is the canonical evidence representation used throughout the Runtime and ultimately embedded within the `ExecutionTrustRecord`. - -This decouples the internal trust model from connector-specific response formats. - -Status - -✅ Verified - -\--- - -\## Audit Notes - -The builder intentionally contains no business logic. - -It converts an `ExecutionResult` into Parmana's canonical evidence model. - -This provides a stable evidence contract regardless of which `ExecutionSystem` implementation produced the original result. - -Status - -✅ Verified - -\--- - -\# Stage 18: Runtime Authorization Signer - -\## File - -``` - -packages/runtime/src/RuntimeAuthorizationSigner.ts - -``` - -\## Responsibility - -Construct and sign the Runtime Execution Authorization. - -The Runtime Authorization Signer is a thin orchestration layer over the - -shared cryptographic infrastructure. - -It is responsible for: - -\- loading the Runtime signing key - -\- invoking the AuthorizationSigner - -\- returning the signed Runtime Authorization - -It does not implement cryptographic algorithms. - -Status - -✅ Verified - -\--- - -\## Position in Execution Flow - -``` - -Decision - - │ - - ▼ - -ExecutableContent - - │ - - ▼ - -RuntimeAuthorizationSigner - - │ - - ▼ - -AuthorizationSigner - - │ - - ▼ - -SignedExecutionAuthorization - - │ - - ▼ - -ExecutionRequestBuilder - -``` - -\--- - -\## Input - -``` - -decisionId - - - -businessTransactionId - - - -policyName - - - -policyVersion - - - -ExecutableContent - - - -TTL - -``` - -Status - -✅ Verified - -\--- - -\## Dependencies - -``` - -CryptoBootstrap - - - -AuthorizationSigner - - - -FileKeyProvider - -``` - -Status - -✅ Verified - -\--- - -\## Signing Flow - -``` - -Load Private Key - - │ - - ▼ - -AuthorizationSigner.sign(...) - - │ - - ▼ - -SignedExecutionAuthorization - -``` - -Status - -✅ Verified - -\--- - -\## Key Management - -Private keys are loaded through - -``` - -FileKeyProvider - -``` - -using - -``` - -DEFAULT\_KEY\_ID - -``` - -The Runtime Authorization Signer never embeds key material. - -Status - -✅ Verified - -\--- - -\## Security Properties - -The Runtime Authorization Signer does not implement signing logic. - -Instead it delegates all cryptographic operations to the shared - -AuthorizationSigner. - -This centralizes cryptographic behavior across: - -\- Runtime Authorization - -\- Execution Trust Records - -\- Receipts - -Status - -✅ Verified - -\--- - -\## Architectural Notes - -The Runtime Authorization Signer is an orchestration layer. - -Cryptography is implemented inside the crypto package. - -This separation keeps Runtime independent from cryptographic algorithms, - -allowing providers to evolve without changing Runtime orchestration. - -Status - -✅ Verified - -\--- - -\## Audit Notes - -This class has a single responsibility: - -create a SignedExecutionAuthorization. - -It does not: - -\- evaluate policy - -\- hash executable content - -\- verify signatures - -\- manage certificates - -\- execute business logic - -Status - -✅ Verified - -\--- - -\# Stage 19: Execution Service - -\## File - -``` - -packages/runtime/src/services/execution-service.ts - -``` - -\## Responsibility - -⏳ Pending Audit - -Expected responsibilities - -\- Create Execution artifact - -\- Attach ExecutionEvidence - -\- Complete Execution - -\- Fail Execution - -\- Persist execution state - -Questions - -\- Is Execution immutable? - -\- What status transitions exist? - -\- When is persistence performed? - -\- Can evidence be modified after attachment? - -\- How are failures recorded? - -Status - -⏳ Pending - -\--- - -\# Stage 19: Execution Service - -\## File - -``` - -packages/runtime/src/services/execution-service.ts - -``` - -\## Responsibility - -Manage the lifecycle of immutable `Execution` artifacts. - -The Execution Service is responsible for: - -\- creating Execution artifacts - -\- attaching immutable Execution Evidence - -\- transitioning execution lifecycle state - -\- persisting execution updates - -It does not: - -\- evaluate policy - -\- execute enterprise systems - -\- verify execution - -\- generate trust records - -Status - -✅ Verified - -\--- - -\## Position in Execution Flow - -``` - -ExecutionComponent - - │ - - ▼ - -ExecutionService.create() - - │ - - ▼ - -Execution - - │ - - ▼ - -ExecutionService.attachEvidence() - - │ - - ▼ - -ExecutionService.complete() - - │ - - ▼ - -BusinessTrustPipeline - -``` - -Failure path - -``` - -ExecutionService.fail() - -``` - -Status - -✅ Verified - -\--- - -\## Dependencies - -``` - -BusinessTransactionRepository - - - -ExecutionTrustRecordRepository - -``` - -Status - -✅ Verified - -\--- - -\## Lifecycle - -``` - -Create - - │ - - ▼ - -PROCESSING - - │ - - ▼ - -Attach Evidence - - │ - - ▼ - -COMPLETED - -``` - -Failure - -``` - -Create - - │ - - ▼ - -PROCESSING - - │ - - ▼ - -FAILED - -``` - -Status - -✅ Verified - -\--- - -\## Phase 1: Create Execution - -Method - -``` - -create(...) - -``` - -Responsibilities - -\- verify Business Transaction exists - -\- create Execution artifact - -\- assign Execution ID - -\- set status to PROCESSING - -\- record startedAt timestamp - -\- append Execution to Trust Record repository - -Initial status - -``` - -PROCESSING - -``` - -Status - -✅ Verified - -\--- - -\## Phase 2: Attach Evidence - -Method - -``` - -attachEvidence(...) - -``` - -Responsibilities - -\- create a new Execution object - -\- attach immutable ExecutionEvidence - -\- replace stored Execution - -No mutation occurs on the original object. - -Status - -✅ Verified - -\--- - -\## Phase 3: Complete Execution - -Method - -``` - -complete(...) - -``` - -Responsibilities - -\- transition status to COMPLETED - -\- set completedAt timestamp - -\- persist updated Execution - -Status - -``` - -COMPLETED - -``` - -Status - -✅ Verified - -\--- - -\## Phase 4: Fail Execution - -Method - -``` - -fail(...) - -``` - -Responsibilities - -\- transition status to FAILED - -\- set completedAt timestamp - -\- persist updated Execution - -Status - -``` - -FAILED - -``` - -Status - -✅ Verified - -\--- - -\## Security Properties - -Execution artifacts are treated as immutable. - -Every lifecycle transition creates a new Execution object rather than modifying the existing instance. - -Persistence occurs after each transition. - -Status - -✅ Verified - -\--- - -\## Architectural Notes - -The Execution Service manages execution state only. - -It does not communicate with enterprise systems. - -Enterprise execution occurs in `ExecutionComponent` through the configured `ExecutionSystem`. - -The Execution Service records and persists the resulting execution lifecycle. - -Status - -✅ Verified - -\--- - -\## Audit Notes - -Execution state transitions are explicit. - -``` - -PROCESSING - - - -↓ - - - -COMPLETED - -``` - -or - -``` - -PROCESSING - - - -↓ - - - -FAILED - -``` - -The service separates execution state management from execution itself, keeping orchestration and persistence independent. - -Status - -✅ Verified - -\--- - -\# Stage 20: Business Transaction Service - -\## File - -``` - -packages/runtime/src/services/business-transaction-service.ts - -``` - -\## Responsibility - -Accept, validate, persist, and retrieve immutable `BusinessTransaction` objects. - -The Business Transaction Service is responsible for: - -\- validating Business Transactions - -\- preventing duplicate transactions - -\- persisting immutable Business Transactions - -\- retrieving Business Transactions - -It does not: - -\- evaluate policy - -\- execute enterprise systems - -\- create decisions - -\- create trust records - -\- verify execution - -Status - -✅ Verified - -\--- - -\## Position in Execution Flow - -``` - -BusinessTransaction - - │ - - ▼ - -BusinessTransactionService.accept() - - │ - - ▼ - -BusinessTransactionValidator - - │ - - ▼ - -Duplicate Check - - │ - - ▼ - -Repository.create() - - │ - - ▼ - -Immutable BusinessTransaction - -``` - -Status - -✅ Verified - -\--- - -\## Dependencies - -``` - -BusinessTransactionRepository - - - -BusinessTransactionValidator - -``` - -Status - -✅ Verified - -\--- - -\## Phase 1: Validate - -Method - -``` - -BusinessTransactionValidator.validate(...) - -``` - -Responsibilities - -\- validate trust-chain invariants - -\- reject structurally invalid transactions - -Status - -✅ Verified - -\--- - -\## Phase 2: Duplicate Detection - -Method - -``` - -repository.exists(...) - -``` - -Responsibilities - -\- detect duplicate BusinessTransactionId - -\- reject duplicate requests - -Failure - -``` - -DuplicateBusinessTransactionError - -``` - -Status - -✅ Verified - -\--- - -\## Phase 3: Persist - -Method - -``` - -repository.create(...) - -``` - -Responsibilities - -\- persist immutable BusinessTransaction - -\- return stored transaction - -Status - -✅ Verified - -\--- - -\## Retrieval Operations - -Supported methods - -``` - -get(...) - - - -list(...) - -``` - -These methods provide read-only access to previously accepted Business Transactions. - -Status - -✅ Verified - -\--- - -\## Security Properties - -Every Business Transaction is validated before persistence. - -Duplicate transaction identifiers are rejected. - -Once accepted, the Business Transaction is treated as immutable. - -Status - -✅ Verified - -\--- - -\## Architectural Notes - -The Business Transaction Service is the Runtime's acceptance boundary. - -No Runtime processing occurs until a Business Transaction has been: - -\- validated - -\- checked for uniqueness - -\- persisted - -This establishes a canonical transaction before policy evaluation begins. - -Status - -✅ Verified - -\--- - -\## Audit Notes - -The service performs three responsibilities only: - -\- validate - -\- reject duplicates - -\- persist - -Policy evaluation, authorization, execution, and trust record generation are delegated to later Runtime stages. - -Status - -✅ Verified - -\--- - -\# Stage 21: Verification Service - -\## File - -``` - -packages/runtime/src/services/verification-service.ts - -``` - -\## Responsibility - -⏳ Pending Audit - -Expected responsibilities - -\- Verify Execution Trust Records - -\- Validate cryptographic integrity - -\- Produce Verification artifacts - -Questions - -\- What is actually verified? - -\- Are signatures verified? - -\- Are hashes recomputed? - -\- Are authorizations verified? - -\- Does verification mutate anything? - -\- Is verification deterministic? - -Status - -⏳ Pending - -\--- - -\# Stage 21: Verification Service - -\## File - -``` - -packages/runtime/src/services/verification-service.ts - -``` - -\## Responsibility - -Verify the integrity and authenticity of an `ExecutionTrustRecord`. - -The Verification Service performs deterministic verification of the - -complete trust record and produces an immutable `Verification` artifact. - -It is responsible for: - -\- validating trust record integrity - -\- verifying cryptographic signatures - -\- validating authorization binding - -\- recording verification results - -It does not: - -\- execute business operations - -\- evaluate policy - -\- modify trust records - -\- generate receipts - -Status - -✅ Verified - -\--- - -\## Position in Execution Flow - -``` - -ExecutionTrustRecord - - │ - - ▼ - -VerificationService - - │ - - ▼ - -Verification - - │ - - ▼ - -ReceiptService - -``` - -Status - -✅ Verified - -\--- - -\## Dependencies - -``` - -ExecutionTrustRecordRepository - - - -VerificationCrypto - -``` - -Status - -✅ Verified - -\--- - -\## Verification Flow - -``` - -Load Trust Record - - │ - - ▼ - -Run Verification Checks - - │ - - ▼ - -Build Verification Artifact - - │ - - ▼ - -Persist Verification - - │ - - ▼ - -Return Verification - -``` - -Status - -✅ Verified - -\--- - -\## Verification Checks - -The service performs every verification independently. - -A failure in one check does not prevent the remaining checks from running. - -Status - -✅ Verified - -\--- - -\### Check 1: Integrity - -``` - -VerificationCrypto.hash(...) - -``` - -The trust record hash is recomputed. - -Verification succeeds only when - -``` - -recomputedHash == storedHash - -``` - -Failure - -``` - -Integrity check failed - -``` - -Status - -✅ Verified - -\--- - -\### Check 2: Signature - -``` - -VerificationCrypto.verifySignature(...) - -``` - -The digital signature must verify using the stored public key. - -Failure - -``` - -Signature check failed - -``` - -Status - -✅ Verified - -\--- - -\### Check 3: Authorization Binding - -Every APPROVED execution must contain - -``` - -authorizationId - -``` - -inside - -``` - -execution.metadata - -``` - -Rejected executions are exempt. - -Failure - -``` - -Authorization binding check failed - -``` - -Status - -✅ Verified - -\--- - -\## Verification Artifact - -The service constructs - -``` - -verificationId - - - -businessTransactionId - - - -status - - - -message - - - -verifiedAt - - - -trustRecordHash - -``` - -Status - -✅ Verified - -\--- - -\## Persistence - -Verification artifacts are appended using - -``` - -appendVerification(...) - -``` - -Previous verification history is preserved. - -Status - -✅ Verified - -\--- - -\## Security Properties - -Verification is deterministic. - -All checks execute regardless of earlier failures. - -The resulting Verification artifact records every detected failure. - -Status - -✅ Verified - -\--- - -\## Architectural Notes - -Verification is modeled as its own immutable domain artifact. - -Rather than returning only a boolean, Parmana records a complete - -Verification object containing: - -\- verification identity - -\- verification timestamp - -\- verification status - -\- verification message - -\- verified trust record hash - -This provides durable evidence that verification occurred and what it - -verified. - -Status - -✅ Verified - -\--- - -\## Audit Notes - -Verification consists of three independent security guarantees: - -\- Integrity - -\- Authenticity - -\- Authorization Binding - -Only after these checks complete is the Runtime ready to generate a - -Receipt. - -Status - -✅ Verified - -\--- - -\# Stage 22: Receipt Service - -\## File - -``` - -packages/runtime/src/services/receipt-service.ts - -``` - -\## Responsibility - -Generate immutable cryptographic Receipts for successfully verified - -Execution Trust Records. - -The Receipt Service is responsible for: - -\- validating verification status - -\- computing receipt hashes - -\- generating signed receipts - -\- persisting receipt artifacts - -It does not: - -\- execute business operations - -\- evaluate policy - -\- verify trust records - -\- modify Execution Trust Records - -Status - -✅ Verified - -\--- - -\## Position in Execution Flow - -``` - -ExecutionTrustRecord - - │ - - ▼ - -ReceiptService - - │ - - ▼ - -Receipt - -``` - -Status - -✅ Verified - -\--- - -\## Dependencies - -``` - -ExecutionTrustRecordRepository - - - -ReceiptCrypto - -``` - -Status - -✅ Verified - -\--- - -\## Receipt Generation Flow - -``` - -Load Trust Record - - │ - - ▼ - -Verify Latest Verification - - │ - - ▼ - -Compute Receipt Hash - - │ - - ▼ - -Create Signed Receipt - - │ - - ▼ - -Persist Receipt - - │ - - ▼ - -Return Receipt - -``` - -Status - -✅ Verified - -\--- - -\## Phase 1: Load Trust Record - -Method - -``` - -findByTransactionId(...) - -``` - -Failure - -``` - -VerificationFailedError - -``` - -Status - -✅ Verified - -\--- - -\## Phase 2: Verification Prerequisite - -The latest Verification must exist. - -Required status - -``` - -VERIFIED - -``` - -Otherwise - -``` - -ReceiptGenerationError - -``` - -is thrown. - -Status - -✅ Verified - -\--- - -\## Phase 3: Receipt Hash - -Component - -``` - -ReceiptCrypto.hash(...) - -``` - -Input - -``` - -ExecutionTrustRecord - -``` - -Output - -``` - -receiptHash - -``` - -Status - -✅ Verified - -\--- - -\## Phase 4: Create Receipt - -Component - -``` - -ReceiptCrypto.createReceipt(...) - -``` - -Receipt contains - -``` - -receiptId - - - -businessTransactionId - - - -trustRecordHash - - - -receiptHash - - - -issuedAt - -``` - -Status - -✅ Verified - -\--- - -\## Phase 5: Persistence - -Receipt is appended using - -``` - -appendReceipt(...) - -``` - -Receipt history is preserved. - -Status - -✅ Verified - -\--- - -\## Security Properties - -Receipts cannot exist without successful verification. - -Receipt generation depends on: - -\- existing Execution Trust Record - -\- successful Verification - -\- cryptographic Receipt generation - -Status - -✅ Verified - -\--- - -\## Architectural Notes - -A Receipt is not generated directly from execution. - -It is generated only after the Execution Trust Record has been successfully verified. - -The Receipt therefore attests to the integrity and authenticity of the verified trust record rather than the execution event alone. - -Status - -✅ Verified - -\--- - -\## Audit Notes - -Receipt generation is the final Runtime operation. - -The Runtime execution lifecycle is therefore: - -Business Transaction - -↓ - -Execution - -↓ - -Execution Trust Record - -↓ - -Verification - -↓ - -Receipt - -Status - -✅ Verified - -\--- - -\# Stage 23: Policy Router - -\## File - -``` - -packages/policy/src/PolicyRouter.ts - -``` - -\## Responsibility - -⏳ Pending Audit - -Expected responsibilities - -\- Resolve requested policy - -\- Load correct policy version - -\- Delegate to repository - -Questions - -\- How are versions selected? - -\- Is latest supported? - -\- How are missing policies handled? - -\- Is caching performed? - -Status - -⏳ Pending - -\--- - -\# Stage 23: Policy Router - -\## File - -``` - -packages/policy/src/PolicyRouter.ts - -``` - -\## Responsibility - -Load and validate exactly one Policy. - -The Policy Router is responsible for: - -\- loading the requested policy from the configured repository - -\- validating the loaded policy - -\- returning the validated policy - -It does not: - -\- evaluate policy - -\- execute rules - -\- cache policies - -\- select outcomes - -\- persist policies - -Status - -✅ Verified - -\--- - -\## Position in Decision Flow - -``` - -BusinessTransaction - - │ - - ▼ - -PolicyRouter - - │ - - ▼ - -PolicyRepository - - │ - - ▼ - -PolicyValidator - - │ - - ▼ - -Validated Policy - - │ - - ▼ - -PolicyEngine - -``` - -Status - -✅ Verified - -\--- - -\## Dependencies - -``` - -PolicyRepository - - - -PolicyValidator - -``` - -Status - -✅ Verified - -\--- - -\## Decision Flow - -``` - -Load Policy - - │ - - ▼ - -Validate Policy - - │ - - ▼ - -Return Policy - -``` - -Status - -✅ Verified - -\--- - -\## Phase 1: Load Policy - -Method - -``` - -repository.load(name, version) - -``` - -Responsibilities - -\- resolve policy name - -\- resolve policy version - -\- retrieve policy - -Status - -✅ Verified - -\--- - -\## Phase 2: Validate Policy - -Method - -``` - -PolicyValidator.validate(...) - -``` - -Responsibilities - -\- validate loaded policy - -\- reject invalid policy definitions - -Status - -✅ Verified - -\--- - -\## Security Properties - -Every policy is validated before entering the Policy Engine. - -The Runtime never evaluates an unvalidated policy. - -Status - -✅ Verified - -\--- - -\## Architectural Notes - -PolicyRouter is an orchestration layer. - -It delegates: - -\- storage to PolicyRepository - -\- validation to PolicyValidator - -It performs no business decision logic. - -Status - -✅ Verified - -\--- - -\## Audit Notes - -Policy loading and policy evaluation are intentionally separated. - -PolicyRouter is responsible for obtaining a valid Policy. - -PolicyEngine is responsible for evaluating that Policy. - -Status - -✅ Verified - -\--- - -\# Stage 24: File Policy Repository - -\## File - -``` - -packages/policy/src/FilePolicyRepository.ts - -``` - -\## Responsibility - -Load Policy definitions from the filesystem. - -The File Policy Repository is responsible for: - -\- locating policy files - -\- loading policy JSON - -\- deserializing policy definitions - -\- reporting missing policies - -It does not: - -\- validate policies - -\- evaluate policies - -\- cache policies - -\- modify policies - -Status - -✅ Verified - -\--- - -\## Position in Decision Flow - -``` - -PolicyRouter - - │ - - ▼ - -FilePolicyRepository - - │ - - ▼ - -policy.json - - │ - - ▼ - -Policy - - │ - - ▼ - -PolicyValidator - -``` - -Status - -✅ Verified - -\--- - -\## Repository Layout - -Policies are organized by: - -``` - -policies/ - - / - - / - - policy.json - -``` - -Example - -``` - -policies/ - - vendor-payment/ - - 1.0.0/ - - policy.json - -``` - -Status - -✅ Verified - -\--- - -\## Resolution Strategy - -Policy path - -``` - -basePath/ - - name/ - - version/ - - policy.json - -``` - -Example - -``` - -policies/vendor-payment/1.0.0/policy.json - -``` - -Status - -✅ Verified - -\--- - -\## Loading Flow - -``` - -Construct File Path - - │ - - ▼ - -Read JSON - - │ - - ▼ - -Parse JSON - - │ - - ▼ - -Return Policy - -``` - -Status - -✅ Verified - -\--- - -\## Error Handling - -If the file cannot be loaded - -``` - -PolicyNotFoundError - -``` - -is thrown. - -The repository does not expose filesystem exceptions directly. - -Status - -✅ Verified - -\--- - -\## Security Properties - -The repository only loads policy definitions. - -Validation occurs later in `PolicyValidator`. - -Evaluation occurs later in `PolicyEngine`. - -Status - -✅ Verified - -\--- - -\## Architectural Notes - -`FilePolicyRepository` is a storage adapter implementing the - -`PolicyRepository` interface. - -The Runtime depends on the interface rather than filesystem-specific - -behavior, allowing alternate implementations (database, object storage, - -Git-backed repositories, etc.) without changing Runtime orchestration. - -Status - -✅ Verified - -\--- - -\## Audit Notes - -Policy lookup is deterministic. - -The repository resolves exactly one policy using: - -\- name - -\- version - -No implicit "latest" resolution, version negotiation, or fallback behavior - -exists in this implementation. - -Status - -✅ Verified - -\--- - -\# Stage 25: Policy Engine - -\## File - -``` - -packages/policy/src/PolicyEngine.ts - -``` - -\## Responsibility - -Evaluate exactly one validated Policy against a set of input signals and - -produce a deterministic `PolicyDecision`. - -The Policy Engine is responsible for: - -\- evaluating policy rules - -\- evaluating policy conditions - -\- selecting the first matching rule - -\- producing a deterministic PolicyDecision - -It does not: - -\- authorize execution - -\- execute business operations - -\- access external systems - -\- persist data - -\- create trust records - -\- generate timestamps - -Status - -✅ Verified - -\--- - -\## Position in Decision Flow - -``` - -Validated Policy - - │ - - ▼ - -PolicyEngine - - │ - - ▼ - -PolicyDecision - - │ - - ▼ - -DecisionBuilder - -``` - -Status - -✅ Verified - -\--- - -\## Inputs - -``` - -Policy - - - -PolicySignals - -``` - -Status - -✅ Verified - -\--- - -\## Output - -``` - -PolicyDecision - -``` - -containing - -\- policyId - -\- policyVersion - -\- outcome - -\- reason - -\- matchedRuleId - -\- evaluatedRules - -\- matchedPath - -Status - -✅ Verified - -\--- - -\## Evaluation Flow - -``` - -Policy - - │ - - ▼ - -Rule 1 - - │ - - ├── Match? - - │ - - ├── Yes → STOP - - │ - - ▼ - -Rule 2 - - │ - - ├── Match? - - │ - - ├── Yes → STOP - - │ - - ▼ - -Rule 3 - - │ - - ▼ - -... - - │ - - ▼ - -No Match - -``` - -Status - -✅ Verified - -\--- - -\## Rule Selection Strategy - -Evaluation uses - -``` - -First Match Wins - -``` - -Rules are evaluated in declaration order. - -The first matching rule terminates evaluation. - -No remaining rules are evaluated. - -Status - -✅ Verified - -\--- - -\## Condition Types - -Supported conditions - -\### Leaf - -``` - -fact - -operator - -value - -``` - -Status - -✅ Verified - -\--- - -\### Always - -``` - -always - -``` - -Always evaluates to true. - -Status - -✅ Verified - -\--- - -\### Logical AND - -``` - -all\[] - -``` - -Every child condition must evaluate to true. - -Status - -✅ Verified - -\--- - -\### Logical OR - -``` - -any\[] - -``` - -At least one child condition must evaluate to true. - -Status - -✅ Verified - -\--- - -\## Missing Signal Behavior - -If a required signal is missing - -``` - -signal === undefined - -``` - -the condition evaluates to - -``` - -false - -``` - -No exception is thrown. - -Status - -✅ Verified - -\--- - -\## Operator Evaluation - -Leaf comparisons are delegated to - -``` - -OperatorEvaluator - -``` - -The Policy Engine does not implement comparison operators directly. - -Status - -✅ Verified - -\--- - -\## Default Outcome - -If no rule matches - -``` - -matchedRuleId = "none" - - - -reason = "no\_rule\_matched" - - - -outcome = REJECT - -``` - -Status - -✅ Verified - -\--- - -\## Trace Generation - -The engine records - -``` - -evaluatedRules - - - -matchedPath - -``` - -These provide deterministic evidence of the evaluation process. - -Status - -✅ Verified - -\--- - -\## Security Properties - -The Policy Engine is deterministic. - -Given: - -\- identical Policy - -\- identical PolicySignals - -it will always produce the same PolicyDecision. - -The engine has: - -\- no randomness - -\- no timestamps - -\- no network access - -\- no mutable state - -Status - -✅ Verified - -\--- - -\## Architectural Notes - -The Policy Engine performs only evaluation. - -Authorization, execution, verification, and trust generation occur in - -later Runtime stages. - -Comparison semantics are delegated to `OperatorEvaluator`, allowing the - -evaluation engine to remain focused on rule traversal and decision - -construction. - -Status - -✅ Verified - -\--- - -\## Audit Notes - -The engine implements a deterministic \*\*first-match-wins\*\* evaluation - -model. - -\--- - -\# Stage 26: Operator Evaluator - -\## File - -``` - -packages/policy/src/OperatorEvaluator.ts - -``` - -\## Responsibility - -Evaluate deterministic policy operators against JSON values. - -The Operator Evaluator is responsible for: - -\- evaluating comparison operators - -\- evaluating logical predicates - -\- evaluating collection operators - -\- evaluating string operators - -\- evaluating type operators - -\- returning deterministic boolean results - -It does not: - -\- load policies - -\- traverse policy rules - -\- authorize execution - -\- access external systems - -\- mutate state - -Status - -✅ Verified - -\--- - -\## Position in Decision Flow - -``` - -PolicyCondition - - │ - - ▼ - -OperatorEvaluator - - │ - - ▼ - -true / false - - │ - - ▼ - -PolicyEngine - -``` - -Status - -✅ Verified - -\--- - -\## Evaluation Categories - -Supported operator groups - -\### Equality - -``` - -eq - -neq - -``` - -Status - -✅ Verified - -\--- - -\### Numeric - -``` - -gt - -gte - -lt - -lte - -between - -``` - -Status - -✅ Verified - -\--- - -\### Collection - -``` - -in - -not\_in - -contains - -not\_contains - -contains\_all - -contains\_any - -``` - -Status - -✅ Verified - -\--- - -\### String - -``` - -starts\_with - -ends\_with - -matches - -``` - -Status - -✅ Verified - -\--- - -\### Existence - -``` - -exists - -not\_exists - -``` - -Status - -✅ Verified - -\--- - -\### Boolean - -``` - -is\_true - -is\_false - -``` - -Status - -✅ Verified - -\--- - -\### Null - -``` - -is\_null - -is\_not\_null - -``` - -Status - -✅ Verified - -\--- - -\### Length - -``` - -length\_eq - -length\_gt - -length\_gte - -length\_lt - -length\_lte - -``` - -Status - -✅ Verified - -\--- - -\### Type - -``` - -type\_is - -``` - -Status - -✅ Verified - -\--- - -\## Type Safety - -Numeric operators execute only for numeric values. - -String operators execute only for string values. - -Collection operators execute only for arrays. - -Invalid type combinations evaluate to false rather than producing implicit coercion. - -Status - -✅ Verified - -\--- - -\## Missing Values - -Missing values are handled explicitly. - -Unsupported comparisons do not rely on JavaScript type coercion. - -Status - -✅ Verified - -\--- - -\## Regular Expressions - -Regex matching is delegated to - -``` - -matches() - -``` - -The evaluator assumes patterns have already been validated by - -`PolicyValidator`. - -Status - -✅ Verified - -\--- - -\## Helper Functions - -Internal helpers provide deterministic behavior for - -\- type detection - -\- length calculation - -\- numeric checks - -\- string checks - -Status - -✅ Verified - -\--- - -\## Security Properties - -The evaluator is pure. - -Given identical inputs - -``` - -actual - - - -operator - - - -expected - -``` - -it always returns the same result. - -It has - -\- no mutable state - -\- no timestamps - -\- no randomness - -\- no I/O - -\- no network access - -Status - -✅ Verified - -\--- - -\## Architectural Notes - -OperatorEvaluator is intentionally isolated from policy traversal. - -PolicyEngine decides \*\*which\*\* conditions to evaluate. - -OperatorEvaluator decides \*\*how\*\* an individual condition evaluates. - -This separation keeps comparison semantics independent from rule traversal. - -Status - -✅ Verified - -\--- - -\## Audit Notes - -Operator evaluation is deterministic and side-effect free. - -It forms the lowest execution layer of Parmana's deterministic policy evaluation engine. - -Status - -✅ Verified - -\--- - -\# Stage 27: Decision Builder - -\## File - -``` - -packages/runtime/src/DecisionBuilder.ts - -``` - -\## Responsibility - -⏳ Pending Audit - -Expected responsibilities - -\- Build immutable Decision artifacts - -\- Transform PolicyDecision into Decision - -\- Capture decision metadata - -Questions - -\- Which fields are copied? - -\- Is decisionId generated here? - -\- Is timestamp generated here? - -\- Is the BusinessTransaction embedded? - -\- Are policy traces preserved? - -Status - -⏳ Pending - -It supports nested logical expressions (`all`, `any`), simple leaf - -conditions, and an unconditional `always` condition. - -When no rule matches, the default decision is rejection. - -Status - -✅ Verified - -It contains only: - -\- execution intent - -\- runtime authorization - -No policy information. - -No decision object. - -No runtime context. - -No trust record. - -The ExecutionRequest is therefore the minimal object required to authorize execution outside the Runtime. - -\# Remaining Audit - -The following components have not yet been audited. - -``` - -RuntimeEngine - - - -RuntimeBuilder - - - -TrustChainValidationComponent - - - -ExecutionComponent - - - -ExecutionSystem - - - -ExecutionGateway - - - -Connector SDK - - - -ReceiptService - - - -VerificationService - - - -ExecutionTrustRecord - - - -Replay - - - -Storage - -``` - -Status: - -⏳ Pending - -# Stage 21: Verification Service - -## File - -```text -packages/runtime/src/services/verification-service.ts -``` - -## Responsibility - -Verify an immutable `ExecutionTrustRecord` and produce a `Verification` -artifact. - -The Verification Service loads the Execution Trust Record, performs -deterministic verification checks, records the verification result, -persists the Verification artifact, and returns the completed -Verification. - -It validates the integrity and authenticity of the complete Execution -Trust Record. - -Status - -✅ Verified - ---- - -## Creates - -- Verification - -Uses - -```text -ExecutionTrustRecordRepository - -VerificationCrypto -``` - -Status - -✅ Verified - ---- - -## Public Operation - -```text -verify( - businessTransactionId -) -``` - -Input - -```text -businessTransactionId -``` - -Output - -```text -Verification -``` - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -Load Execution Trust Record - -↓ - -Run Verification Checks - -↓ - -Create Verification - -↓ - -Append Verification - -↓ - -Return Verification -``` - -Status - -✅ Verified - ---- - -## Phase 1: Load Execution Trust Record - -Component - -```text -ExecutionTrustRecordRepository.findByTransactionId() -``` - -Input - -```text -businessTransactionId -``` - -Output - -```text -ExecutionTrustRecord -``` - -Failure - -```text -VerificationFailedError - -Execution Trust Record not found. -``` - -Status - -✅ Verified - ---- - -## Phase 2: Execute Verification Checks - -Component - -```text -runChecks(...) -``` - -## Responsibility - -Execute every verification independently. - -Unlike fail-fast validation, every verification check executes even if -earlier checks fail. - -All failures are accumulated and returned together. - -Output - -```text -string[] -``` - -Status - -✅ Verified - ---- - -## Verification Check 1: Trust Record Integrity - -Component - -```text -VerificationCrypto.hash(...) -``` - -Input - -```text -ExecutionTrustRecord -``` - -Output - -```text -Expected Trust Record Hash -``` - -Purpose - -Recompute the Trust Record hash and compare it with the stored hash. - -Validation - -```text -expectedHash -== -trustRecord.trustRecordHash -``` - -Failure - -```text -Integrity check failed -``` - -Status - -✅ Verified - ---- - -## Verification Check 2: Signature Verification - -Component - -```text -VerificationCrypto.verifySignature(...) -``` - -Input - -```text -ExecutionTrustRecord -``` - -Output - -```text -boolean -``` - -Purpose - -Verify the cryptographic signature stored inside the Trust Record. - -Validation - -```text -Signature verifies -``` - -Failure - -```text -Signature check failed -``` - -Status - -✅ Verified - ---- - -## Verification Check 3: Authorization Binding - -Purpose - -Every APPROVED execution must contain an authorization reference. - -Validation - -```text -DecisionOutcome.APPROVED - -↓ - -metadata.authorizationId exists -``` - -Rejected executions are skipped. - -Failure - -```text -Authorization binding check failed -``` - -Status - -✅ Verified - ---- - -## Phase 3: Create Verification - -Creates - -```text -Verification -``` - -Fields - -- verificationId -- businessTransactionId -- status -- message -- verifiedAt -- trustRecordHash - -Status - -✅ Verified - ---- - -## Verification Status - -Successful - -```text -VerificationStatus.VERIFIED -``` - -Failed - -```text -VerificationStatus.FAILED -``` - -If verification succeeds - -```text -Execution Trust Record verified successfully. -``` - -Otherwise - -```text -All verification failures are concatenated into a single message. -``` - -Status - -✅ Verified - ---- - -## Phase 4: Persist Verification - -Component - -```text -ExecutionTrustRecordRepository.appendVerification(...) -``` - -Purpose - -Append the Verification artifact to the existing Execution Trust Record. - -Status - -✅ Verified - ---- - -## Security Properties - -The Verification Service performs deterministic verification. - -It validates - -- Trust Record integrity -- Digital signature -- Authorization binding - -Every verification executes regardless of previous failures. - -The original Execution Trust Record is never modified. - -Status - -✅ Verified - ---- - -## Architectural Notes - -The Verification Service performs orchestration and verification. - -Cryptographic operations are delegated to - -```text -VerificationCrypto -``` - -Persistence is delegated to - -```text -ExecutionTrustRecordRepository -``` - -The verification algorithm itself is implemented inside - -```text -runChecks(...) -``` - -Status - -✅ Verified - ---- - -## Audit Notes - -The Verification Service validates an existing Execution Trust Record. - -It does not - -- execute business operations -- evaluate policy -- modify Trust Records -- generate receipts - -It performs three deterministic verification checks - -- Trust Record integrity -- Signature verification -- Authorization binding - -Unlike fail-fast validation, every verification executes and every -failure is reported. - -Status - -✅ Verified - -# Stage 22: Receipt Service - -## File - -```text -packages/runtime/src/services/receipt-service.ts -``` - -## Responsibility - -⏳ Pending Audit - -The Receipt Service is responsible for generating immutable -Execution Receipts from verified Execution Trust Records. - -Expected responsibilities - -- generate Execution Receipts -- assign Receipt identifiers -- record receipt creation time -- persist Receipts -- retrieve existing Receipts - -It should not - -- evaluate policy -- execute business operations -- verify Trust Records -- modify Execution Trust Records - -Status - -⏳ Pending - ---- - -## Creates - -Expected - -- Receipt - -Uses - -```text -ExecutionTrustRecordRepository - -ReceiptBuilder -``` - -Status - -⏳ Pending - ---- - -## Public Operations - -Expected - -```text -generate( - businessTransactionId -) -``` - -Returns - -```text -Receipt -``` - -Additional operations to verify - -```text -get(...) - -list(...) -``` - -Status - -⏳ Pending - ---- - -## Expected Execution Sequence - -```text -Load Execution Trust Record - -↓ - -Validate Verification - -↓ - -Build Receipt - -↓ - -Persist Receipt - -↓ - -Return Receipt -``` - -Status - -⏳ Pending - ---- - -## Questions To Verify - -- How is the Receipt created? -- Does generation require successful verification? -- Which builder creates the Receipt? -- Is hashing performed here? -- Is signing performed here? -- Are Receipts immutable? -- Where are Receipts persisted? -- Can multiple Receipts exist for one Trust Record? - -Status - -⏳ Pending - ---- - -## Expected Dependencies - -To verify - -```text -ExecutionTrustRecordRepository - -ReceiptBuilder - -VerificationService -``` - -Status - -⏳ Pending - ---- - -## Security Properties - -To verify - -- Receipt generated only from verified Trust Records -- Receipt immutable after creation -- Receipt contains verifiable references -- Receipt creation is deterministic - -Status - -⏳ Pending - ---- - -## Architectural Notes - -To verify - -- Receipt Service should orchestrate receipt generation. -- Construction should be delegated to ReceiptBuilder. -- Persistence should be delegated to the repository. - -Status - -⏳ Pending - ---- - -## Audit Notes - -Pending implementation audit. - -Source code inspection required before documenting behavior. - -Status - -⏳ Pending - -# Stage 22: Receipt Service - -## File - -```text -packages/runtime/src/services/receipt-service.ts -``` - -## Responsibility - -Generate immutable cryptographic Receipts for verified -Execution Trust Records. - -The Receipt Service loads the Execution Trust Record, -ensures the latest Verification succeeded, computes the -Receipt hash, creates a signed Receipt, persists it, and -returns the completed Receipt. - -Status - -✅ Verified - ---- - -## Creates - -- Receipt - -Uses - -```text -ExecutionTrustRecordRepository - -ReceiptCrypto -``` - -Status - -✅ Verified - ---- - -## Public Operation - -```text -generate( - businessTransactionId -) -``` - -Input - -```text -businessTransactionId -``` - -Output - -```text -Receipt -``` - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -Load Execution Trust Record - -↓ - -Validate Latest Verification - -↓ - -Compute Receipt Hash - -↓ - -Create Signed Receipt - -↓ - -Persist Receipt - -↓ - -Return Receipt -``` - -Status - -✅ Verified - ---- - -## Phase 1: Load Execution Trust Record - -Component - -```text -ExecutionTrustRecordRepository.findByTransactionId() -``` - -Input - -```text -businessTransactionId -``` - -Output - -```text -ExecutionTrustRecord -``` - -Failure - -```text -VerificationFailedError - -Execution Trust Record not found. -``` - -Status - -✅ Verified - ---- - -## Phase 2: Validate Latest Verification - -Purpose - -Ensure the latest Verification completed successfully before -a Receipt may be generated. - -Validation - -```text -trustRecord.verifications.at(-1) - -↓ - -VerificationStatus.VERIFIED -``` - -Failure - -```text -ReceiptGenerationError - -Execution Trust Record must be successfully verified before a -Receipt can be generated. -``` - -Status - -✅ Verified - ---- - -## Phase 3: Compute Receipt Hash - -Component - -```text -ReceiptCrypto.hash(...) -``` - -Input - -```text -ExecutionTrustRecord -``` - -Output - -```text -receiptHash -``` - -Purpose - -Compute the cryptographic hash used when constructing the -Receipt. - -Status - -✅ Verified - ---- - -## Phase 4: Create Signed Receipt - -Component - -```text -ReceiptCrypto.createReceipt(...) -``` - -Creates - -```text -Receipt -``` - -Fields - -- receiptId -- businessTransactionId -- trustRecordHash -- receiptHash -- issuedAt - -Purpose - -Construct and digitally sign the Receipt. - -Status - -✅ Verified - ---- - -## Phase 5: Persist Receipt - -Component - -```text -ExecutionTrustRecordRepository.appendReceipt(...) -``` - -Purpose - -Append the Receipt to the existing Execution Trust Record. - -Status - -✅ Verified - ---- - -## Security Properties - -Receipt generation requires a successfully verified -Execution Trust Record. - -A Receipt cannot be generated when - -- no Verification exists -- the latest Verification failed - -Receipt creation delegates all cryptographic operations to - -```text -ReceiptCrypto -``` - -Status - -✅ Verified - ---- - -## Architectural Notes - -The Receipt Service is an orchestration layer. - -Cryptographic operations are delegated to - -```text -ReceiptCrypto -``` - -Persistence is delegated to - -```text -ExecutionTrustRecordRepository -``` - -The service performs no policy evaluation or execution. - -Status - -✅ Verified - ---- - -## Audit Notes - -The Receipt Service performs five responsibilities only. - -- Load the Execution Trust Record. -- Verify the latest Verification succeeded. -- Compute the Receipt hash. -- Create the signed Receipt. -- Persist and return the Receipt. - -The service does not - -- evaluate policy -- execute business operations -- modify the Execution Trust Record -- implement cryptographic algorithms - -Receipt generation is only permitted after successful -verification, establishing a strict lifecycle: - -```text -Execution - -↓ - -Verification - -↓ - -Receipt -``` - -Status - -✅ Verified - -# Stage 23: Receipt Crypto - -## File - -```text -packages/crypto/src/ReceiptCrypto.ts -``` - -## Responsibility - -Own all cryptographic operations related to Receipts. - -The Receipt Crypto component computes canonical Receipt hashes, -digitally signs Receipt artifacts, and creates immutable signed -Receipts. - -It delegates hashing, signing, and key management to dedicated -cryptographic components. - -Status - -✅ Verified - ---- - -## Creates - -- Receipt - -Uses - -```text -CryptoBootstrap - -FileKeyProvider - -TrustRecordHasher - -ReceiptHasher - -ArtifactSigner -``` - -Status - -✅ Verified - ---- - -## Public Operations - -```text -hash( - trustRecord -) -``` - -```text -sign( - value -) -``` - -```text -createReceipt( - payload -) -``` - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -Execution Trust Record - -↓ - -ReceiptHasher.hash() - -↓ - -Receipt Hash - -↓ - -Build Unsigned Receipt - -↓ - -Load Private Key - -↓ - -ArtifactSigner.sign() - -↓ - -Signed Receipt - -↓ - -Return Receipt -``` - -Status - -✅ Verified - ---- - -## Phase 1: Compute Receipt Hash - -Component - -```text -ReceiptHasher -``` - -Method - -```text -hash(...) -``` - -Input - -```text -ExecutionTrustRecord -``` - -Output - -```text -receiptHash -``` - -Purpose - -Compute the canonical Receipt hash from the supplied -Execution Trust Record. - -Implementation - -```text -ReceiptHasher - -↓ - -TrustRecordHasher - -↓ - -Crypto Provider -``` - -Status - -✅ Verified - ---- - -## Phase 2: Load Signing Key - -Component - -```text -FileKeyProvider -``` - -Method - -```text -getPrivateKey(DEFAULT_KEY_ID) -``` - -Purpose - -Load the Runtime private signing key. - -The implementation currently uses a filesystem-backed key provider. - -Status - -✅ Verified - ---- - -## Phase 3: Sign Artifact - -Component - -```text -ArtifactSigner -``` - -Method - -```text -sign(...) -``` - -Input - -```text -Unsigned Receipt -``` - -Output - -```text -Digital Signature -``` - -Purpose - -Digitally sign the canonical Receipt using the configured -SignatureProvider. - -Status - -✅ Verified - ---- - -## Phase 4: Create Receipt - -Method - -```text -createReceipt(...) -``` - -Input - -```text -Receipt Payload -``` - -Creates - -```text -Unsigned Receipt -``` - -Fields - -- receiptId -- businessTransactionId -- trustRecordHash -- receiptHash -- issuedAt -- algorithm - -The signature is then generated and appended. - -Output - -```text -Receipt -``` - -Fields - -- receiptId -- businessTransactionId -- trustRecordHash -- receiptHash -- issuedAt -- algorithm -- signature - -Status - -✅ Verified - ---- - -## Dependencies - -```text -CryptoBootstrap - -↓ - -SignatureProvider - -↓ - -ArtifactSigner - -↓ - -ReceiptCrypto -``` - -Hashing - -```text -TrustRecordHasher - -↓ - -ReceiptHasher - -↓ - -ReceiptCrypto -``` - -Status - -✅ Verified - ---- - -## Security Properties - -ReceiptCrypto never implements cryptographic algorithms directly. - -It delegates all cryptographic operations to dedicated components. - -Key material is never embedded in the Receipt. - -The signing algorithm is obtained from the configured -SignatureProvider. - -Status - -✅ Verified - ---- - -## Architectural Notes - -ReceiptCrypto is a cryptographic orchestration component. - -It coordinates - -- Receipt hashing -- Private key retrieval -- Digital signing -- Receipt construction - -Cryptographic implementations remain isolated inside reusable -crypto components. - -Status - -✅ Verified - ---- - -## Audit Notes - -ReceiptCrypto performs four responsibilities only. - -- Compute the canonical Receipt hash. -- Load the Runtime signing key. -- Digitally sign Receipt artifacts. -- Produce immutable signed Receipts. - -It does not - -- evaluate policy -- verify signatures -- execute business operations -- persist Receipts - -All cryptographic primitives are delegated through -CryptoBootstrap, enabling the signing implementation to evolve -without changing Receipt generation. - -Status - -✅ Verified - -# Stage 24: Verification Crypto - -## File - -```text -packages/crypto/src/VerificationCrypto.ts -``` - -## Responsibility - -Provide cryptographic operations for verifying -Execution Trust Records. - -The Verification Crypto component computes canonical -Trust Record hashes, creates digital signatures, -verifies digital signatures, and validates the -cryptographic integrity and authenticity of an -Execution Trust Record. - -All cryptographic primitives are delegated to -specialized cryptographic components. - -Status - -✅ Verified - ---- - -## Creates - -- Signature - -Uses - -```text -CryptoBootstrap - -FileKeyProvider - -TrustRecordHasher - -ArtifactSigner - -SignatureVerifier -``` - -Status - -✅ Verified - ---- - -## Public Operations - -```text -hash( - trustRecord -) -``` - -```text -sign( - trustRecord -) -``` - -```text -verifySignature( - trustRecord -) -``` - -```text -verify( - trustRecord -) -``` - -Status - -✅ Verified - ---- - -## Internal Component - -```text -canonicalRecord( - trustRecord -) -``` - -## Responsibility - -Create the canonical immutable representation of an -Execution Trust Record used for hashing and signing. - -The canonical view intentionally excludes mutable -lifecycle artifacts so that cryptographic operations -remain deterministic. - -Included - -- trustRecordId -- businessTransactionId -- transaction -- overrides -- executions -- createdAt - -Excluded - -- verifications -- receipts -- trustRecordHash -- signature -- updatedAt - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -Execution Trust Record - -↓ - -Canonical Record - -↓ - -Hash - -↓ - -Sign / Verify - -↓ - -Verification Result -``` - -Status - -✅ Verified - ---- - -## Phase 1: Canonicalize Trust Record - -Component - -```text -canonicalRecord(...) -``` - -Input - -```text -ExecutionTrustRecord -``` - -Output - -```text -Canonical Trust Record -``` - -Purpose - -Produce the immutable representation used for every -cryptographic operation. - -Status - -✅ Verified - ---- - -## Phase 2: Compute Trust Record Hash - -Component - -```text -TrustRecordHasher -``` - -Method - -```text -hash(...) -``` - -Input - -```text -Canonical Trust Record -``` - -Output - -```text -trustRecordHash -``` - -Purpose - -Compute the deterministic hash of the canonical Trust -Record. - -Status - -✅ Verified - ---- - -## Phase 3: Create Digital Signature - -Component - -```text -ArtifactSigner -``` - -Method - -```text -sign(...) -``` - -Input - -```text -Canonical Trust Record -``` - -Dependencies - -```text -FileKeyProvider - -↓ - -Private Key -``` - -Creates - -```text -Signature -``` - -Fields - -- algorithm -- keyId -- value -- signedAt - -Status - -✅ Verified - ---- - -## Phase 4: Verify Signature - -Component - -```text -SignatureVerifier -``` - -Method - -```text -verify(...) -``` - -Dependencies - -```text -FileKeyProvider - -↓ - -Public Key -``` - -Purpose - -Verify the digital signature independently of Trust -Record hash verification. - -Output - -```text -boolean -``` - -Status - -✅ Verified - ---- - -## Phase 5: Verify Trust Record - -Method - -```text -verify(...) -``` - -Execution Sequence - -```text -Canonical Record - -↓ - -Compute Expected Hash - -↓ - -Compare Stored Hash - -↓ - -Load Public Key - -↓ - -Verify Signature - -↓ - -Return Result -``` - -Validation - -```text -expectedHash -== -trustRecord.trustRecordHash -``` - -Only if hash validation succeeds does signature -verification continue. - -Output - -```text -boolean -``` - -Status - -✅ Verified - ---- - -## Security Properties - -Every cryptographic operation uses the same canonical -representation. - -Mutable lifecycle artifacts are excluded from hashing -and signing. - -Signature verification is independent of integrity -verification through the dedicated - -```text -verifySignature(...) -``` - -operation. - -Private and public keys are obtained through - -```text -FileKeyProvider -``` - -The signing algorithm is supplied by - -```text -CryptoBootstrap -``` - -Status - -✅ Verified - ---- - -## Architectural Notes - -VerificationCrypto is a cryptographic orchestration -component. - -It delegates - -- hashing -- signing -- signature verification -- key management - -to dedicated cryptographic components. - -No cryptographic algorithms are implemented directly -inside this class. - -Status - -✅ Verified - ---- - -## Audit Notes - -VerificationCrypto performs five responsibilities only. - -- Build the canonical Trust Record. -- Compute deterministic Trust Record hashes. -- Create digital signatures. -- Verify digital signatures. -- Verify Trust Record integrity and authenticity. - -It does not - -- evaluate policy -- execute business operations -- persist Trust Records -- generate Verification artifacts -- generate Receipts - -The canonical representation is the foundation of all -cryptographic operations, ensuring hashing and signing -remain deterministic regardless of later lifecycle -artifacts such as Verification or Receipt records. - -Status - -✅ Verified - -# Stage 25: Artifact Signer - -## File - -```text -packages/crypto/src/ArtifactSigner.ts -``` - -## Responsibility - -Create digital signatures for canonical artifacts. - -The Artifact Signer serializes an artifact into its canonical byte -representation and delegates digital signing to the configured -cryptographic provider. - -It provides a common signing implementation used throughout the -Parmana cryptographic infrastructure. - -Status - -✅ Verified - ---- - -## Uses - -```text -CanonicalSerializer - -CryptoProvider -``` - -Status - -✅ Verified - ---- - -## Public Operation - -```text -sign( - artifact, - privateKey -) -``` - -Input - -```text -artifact - -privateKey -``` - -Output - -```text -Digital Signature -``` - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -Artifact - -↓ - -CanonicalSerializer.serialize() - -↓ - -Canonical Bytes - -↓ - -CryptoProvider.signature.sign() - -↓ - -Digital Signature -``` - -Status - -✅ Verified - ---- - -## Phase 1: Canonical Serialization - -Component - -```text -CanonicalSerializer -``` - -Method - -```text -serialize(...) -``` - -Input - -```text -Artifact -``` - -Output - -```text -Canonical Bytes -``` - -Purpose - -Convert the supplied artifact into a deterministic byte sequence -before signing. - -Status - -✅ Verified - ---- - -## Phase 2: Digital Signing - -Component - -```text -CryptoProvider.signature -``` - -Method - -```text -sign(...) -``` - -Input - -```text -Canonical Bytes - -Private Key -``` - -Output - -```text -Digital Signature -``` - -Purpose - -Generate the cryptographic signature using the configured -Signature Provider. - -Status - -✅ Verified - ---- - -## Security Properties - -The Artifact Signer always signs the canonical serialized -representation of an artifact. - -It never signs mutable object representations directly. - -The private key is supplied by the caller. - -The signing algorithm is determined by the configured -CryptoProvider. - -Status - -✅ Verified - ---- - -## Architectural Notes - -ArtifactSigner is a reusable cryptographic utility. - -It contains no knowledge of - -- Execution Trust Records -- Receipts -- Runtime Authorization -- Business Transactions - -Its responsibility is limited to deterministic artifact signing. - -Serialization is delegated to - -```text -CanonicalSerializer -``` - -Cryptographic signing is delegated to - -```text -CryptoProvider -``` - -Status - -✅ Verified - ---- - -## Audit Notes - -ArtifactSigner performs two responsibilities only. - -- Canonically serialize an artifact. -- Produce a digital signature using the configured - cryptographic provider. - -It does not - -- hash artifacts -- verify signatures -- manage cryptographic keys -- implement signing algorithms -- persist signed artifacts - -This class serves as the common signing abstraction used by -VerificationCrypto, ReceiptCrypto, RuntimeAuthorizationSigner, -and other cryptographic components. - -Status - -✅ Verified - -# Stage 26: Trust Record Hasher - -## File - -```text -packages/crypto/src/TrustRecordHasher.ts -``` - -## Responsibility - -⏳ Pending Audit - -The Trust Record Hasher is responsible for computing the -canonical cryptographic hash of an Execution Trust Record. - -Expected responsibilities - -- canonical serialization -- deterministic hashing -- produce Trust Record hash - -It should not - -- sign artifacts -- verify signatures -- manage keys -- persist data - -Status - -⏳ Pending - ---- - -## Uses - -```text -CanonicalSerializer - -CryptoProvider -``` - -Status - -⏳ Pending - ---- - -## Public Operation - -```text -hash( - trustRecord -) -``` - -Input - -```text -ExecutionTrustRecord -``` - -Output - -```text -trustRecordHash -``` - -Status - -⏳ Pending - ---- - -## Expected Execution Sequence - -```text -Execution Trust Record - -↓ - -Canonical Serialization - -↓ - -Canonical Bytes - -↓ - -Hash Function - -↓ - -Trust Record Hash -``` - -Status - -⏳ Pending - ---- - -## Questions To Verify - -- What fields are included in the hash? -- Is canonical serialization used? -- Which hash algorithm is configured? -- Is hashing deterministic? -- Does hashing delegate to CryptoProvider? -- Does hashing exclude mutable lifecycle artifacts? - -Status - -⏳ Pending - ---- - -## Security Properties - -To verify - -- Deterministic hashing -- Canonical serialization -- Stable cryptographic output -- No mutable state included - -Status - -⏳ Pending - ---- - -## Architectural Notes - -To verify - -- TrustRecordHasher should perform hashing only. -- Serialization should be delegated. -- Cryptographic hashing should be delegated to CryptoProvider. - -Status - -⏳ Pending - ---- - -## Audit Notes - -Pending implementation audit. - -Source code inspection required before documenting behavior. - -Status - -⏳ Pending - -# Stage 26: Trust Record Hasher - -## File - -```text -packages/crypto/src/TrustRecordHasher.ts -``` - -## Responsibility - -Produce deterministic cryptographic hashes for immutable -trust artifacts. - -The Trust Record Hasher converts an object into its canonical -serialized representation and delegates hash computation to the -configured cryptographic provider. - -It provides a reusable hashing abstraction used throughout the -Parmana cryptographic infrastructure. - -Status - -✅ Verified - ---- - -## Uses - -```text -CanonicalSerializer - -CryptoProvider -``` - -Status - -✅ Verified - ---- - -## Public Operation - -```text -hash( - value -) -``` - -Input - -```text -Immutable Artifact -``` - -Output - -```text -Cryptographic Hash -``` - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -Artifact - -↓ - -CanonicalSerializer.serialize() - -↓ - -Canonical Bytes - -↓ - -CryptoProvider.hash.hash() - -↓ - -Cryptographic Hash -``` - -Status - -✅ Verified - ---- - -## Phase 1: Canonical Serialization - -Component - -```text -CanonicalSerializer -``` - -Method - -```text -serialize(...) -``` - -Input - -```text -Artifact -``` - -Output - -```text -Canonical Bytes -``` - -Purpose - -Convert the supplied artifact into a deterministic byte -representation before hashing. - -Status - -✅ Verified - ---- - -## Phase 2: Compute Hash - -Component - -```text -CryptoProvider.hash -``` - -Method - -```text -hash(...) -``` - -Input - -```text -Canonical Bytes -``` - -Output - -```text -Cryptographic Hash -``` - -Purpose - -Compute the deterministic cryptographic hash of the canonical -serialized artifact. - -Status - -✅ Verified - ---- - -## Security Properties - -Every artifact is hashed from its canonical serialized -representation. - -Objects are never hashed directly. - -The hashing algorithm is supplied by the configured -CryptoProvider. - -The Trust Record Hasher performs no object mutation. - -Status - -✅ Verified - ---- - -## Architectural Notes - -The Trust Record Hasher is a reusable cryptographic utility. - -It has no knowledge of - -- Execution Trust Records -- Runtime Authorization -- Receipts -- Business Transactions - -Its responsibility is limited to deterministic hashing. - -Serialization is delegated to - -```text -CanonicalSerializer -``` - -Hash computation is delegated to - -```text -CryptoProvider -``` - -Status - -✅ Verified - ---- - -## Audit Notes - -The Trust Record Hasher performs two responsibilities only. - -- Canonically serialize an artifact. -- Produce a deterministic cryptographic hash using the - configured cryptographic provider. - -It does not - -- sign artifacts -- verify signatures -- manage cryptographic keys -- implement hashing algorithms -- persist artifacts - -This class serves as the common hashing abstraction used by -VerificationCrypto, ReceiptCrypto, and other cryptographic -components that require deterministic hashing. - -Status - -✅ Verified - -# Stage 27: Receipt Hasher - -## File - -```text -packages/crypto/src/ReceiptHasher.ts -``` - -## Responsibility - -Compute the canonical cryptographic hash used for -Receipt generation. - -The Receipt Hasher delegates hash computation entirely to the -Trust Record Hasher. - -It contains no receipt-specific hashing logic. - -Status - -✅ Verified - ---- - -## Uses - -```text -TrustRecordHasher -``` - -Status - -✅ Verified - ---- - -## Public Operation - -```text -hash( - receipt -) -``` - -Input - -```text -Artifact -``` - -Output - -```text -Cryptographic Hash -``` - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -Artifact - -↓ - -TrustRecordHasher.hash() - -↓ - -Cryptographic Hash - -↓ - -Return Hash -``` - -Status - -✅ Verified - ---- - -## Phase 1: Delegate Hash Computation - -Component - -```text -TrustRecordHasher -``` - -Method - -```text -hash(...) -``` - -Input - -```text -Artifact -``` - -Output - -```text -Cryptographic Hash -``` - -Purpose - -Delegate deterministic hash computation to the shared -Trust Record Hasher. - -No additional processing occurs. - -Status - -✅ Verified - ---- - -## Security Properties - -ReceiptHasher performs no cryptographic operations directly. - -It relies entirely on - -```text -TrustRecordHasher -``` - -for - -- canonical serialization -- deterministic hashing -- cryptographic algorithm selection - -This guarantees Receipt hashing remains consistent with the -rest of the cryptographic infrastructure. - -Status - -✅ Verified - ---- - -## Architectural Notes - -ReceiptHasher is a thin wrapper around -`TrustRecordHasher`. - -It introduces no additional - -- serialization -- hashing logic -- cryptographic algorithms -- receipt-specific transformations - -Its only responsibility is to expose a Receipt-specific -hashing interface while reusing the shared hashing -implementation. - -Status - -✅ Verified - ---- - -## Audit Notes - -ReceiptHasher performs one responsibility only. - -- Delegate hash computation to the shared Trust Record Hasher. - -It does not - -- serialize artifacts -- implement hashing algorithms -- sign Receipts -- verify signatures -- manage cryptographic keys -- persist artifacts - -The implementation intentionally avoids duplicating hashing -logic by reusing the common `TrustRecordHasher`, ensuring a -single deterministic hashing implementation across the -cryptographic subsystem. - -Status - -✅ Verified - -# Stage 28: Authorization Signer - -## File - -```text -packages/crypto/src/AuthorizationSigner.ts -``` - -## Responsibility - -Produce a cryptographically signed execution authorization for an -approved decision. - -The Authorization Signer constructs an -`ExecutionAuthorizationPayload`, computes the hash of the executable -content, signs the payload, and returns a complete -`SignedExecutionAuthorization`. - -Status - -✅ Verified - ---- - -## Uses - -```text -ArtifactSigner - -ExecutableContentHasher - -CryptoProvider -``` - -Status - -✅ Verified - ---- - -## Public Operation - -```text -sign( - input, - privateKey, - keyId, - ttlSeconds -) -``` - -Input - -```text -Decision Information - -Executable Content - -Private Key - -Key Identifier - -Authorization TTL -``` - -Output - -```text -SignedExecutionAuthorization -``` - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -Decision Input - -↓ - -Validate TTL - -↓ - -Generate -• authorizationId -• nonce - -↓ - -Compute -businessTransactionHash - -↓ - -Construct -ExecutionAuthorizationPayload - -↓ - -ArtifactSigner.sign() - -↓ - -SignedExecutionAuthorization -``` - -Status - -✅ Verified - ---- - -## Phase 1: Validate Authorization Lifetime - -Input - -```text -ttlSeconds -``` - -Validation - -```text -ttlSeconds > 0 - -Finite Number -``` - -Failure - -```text -Throws Error -``` - -Purpose - -Prevent creation of authorizations with invalid expiry periods. - -Status - -✅ Verified - ---- - -## Phase 2: Generate Authorization Metadata - -Generated Values - -```text -authorizationId - -nonce - -authorizedAt - -expiresAt -``` - -Purpose - -Create a unique authorization instance together with its validity -window. - -Status - -✅ Verified - ---- - -## Phase 3: Compute Business Transaction Hash - -Component - -```text -ExecutableContentHasher -``` - -Method - -```text -hash( - executableContent -) -``` - -Output - -```text -businessTransactionHash -``` - -Purpose - -Bind the authorization to the exact executable content that was -approved. - -Status - -✅ Verified - ---- - -## Phase 4: Build Authorization Payload - -Constructs - -```text -ExecutionAuthorizationPayload -``` - -Fields - -```text -version - -authorizationId - -nonce - -decisionId - -businessTransactionId - -policyName - -policyVersion - -authorizedAt - -expiresAt - -businessTransactionHash -``` - -Status - -✅ Verified - ---- - -## Phase 5: Sign Authorization - -Component - -```text -ArtifactSigner -``` - -Method - -```text -sign( - payload, - privateKey -) -``` - -Output - -```text -Digital Signature -``` - -Purpose - -Produce a deterministic cryptographic signature over the canonical -authorization payload. - -Status - -✅ Verified - ---- - -## Phase 6: Return Signed Authorization - -Returns - -```text -SignedExecutionAuthorization -``` - -Containing - -```text -payload - -signature - -keyId - -algorithm -``` - -The algorithm is obtained from - -```text -CryptoProvider.signature.algorithm -``` - -Status - -✅ Verified - ---- - -## Security Properties - -Every authorization is cryptographically bound to the executable -content through `businessTransactionHash`. - -Each authorization contains - -- unique authorization identifier -- nonce -- issuance timestamp -- expiry timestamp - -Only the canonical payload is signed. - -The signing algorithm is supplied by the configured -`CryptoProvider`. - -Status - -✅ Verified - ---- - -## Architectural Notes - -AuthorizationSigner is an orchestration component. - -It delegates - -- canonical signing to `ArtifactSigner` -- executable content hashing to `ExecutableContentHasher` - -It does not implement - -- canonical serialization -- hashing algorithms -- digital signature algorithms -- key management - -Status - -✅ Verified - ---- - -## Audit Notes - -AuthorizationSigner performs six responsibilities. - -- Validate authorization TTL. -- Generate authorization metadata. -- Compute executable content hash. -- Construct an `ExecutionAuthorizationPayload`. -- Sign the payload. -- Return a complete `SignedExecutionAuthorization`. - -All cryptographic primitives are delegated to dedicated helper -components, keeping this class focused on authorization -construction and orchestration. - -Status - -✅ Verified - -# Stage 29: Executable Content Hasher - -## File - -```text -packages/crypto/src/ExecutableContentHasher.ts -``` - -## Responsibility - -Produce the canonical cryptographic hash of an -`ExecutableContent` object. - -The Executable Content Hasher computes the hash that is embedded -into an `ExecutionAuthorizationPayload` as the -`businessTransactionHash`. - -It delegates all hashing operations to the shared -`TrustRecordHasher`, ensuring the signing side and the execution -gateway use the identical canonical serialization and hashing -algorithm. - -Status - -✅ Verified - ---- - -## Uses - -```text -TrustRecordHasher - -CryptoProvider -``` - -Status - -✅ Verified - ---- - -## Public Operation - -```text -hash( - content -) -``` - -Input - -```text -ExecutableContent -``` - -Output - -```text -businessTransactionHash -``` - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -Executable Content - -↓ - -TrustRecordHasher.hash() - -↓ - -Canonical Serialization - -↓ - -Cryptographic Hash - -↓ - -businessTransactionHash -``` - -Status - -✅ Verified - ---- - -## Phase 1: Receive Executable Content - -Input - -```text -ExecutableContent -``` - -Purpose - -Accept the exact business content that will later be authorized -and executed. - -Status - -✅ Verified - ---- - -## Phase 2: Delegate Hash Computation - -Component - -```text -TrustRecordHasher -``` - -Method - -```text -hash( - content -) -``` - -Purpose - -Reuse the common deterministic hashing implementation instead of -creating a separate executable-content hashing algorithm. - -Status - -✅ Verified - ---- - -## Phase 3: Return Content Hash - -Output - -```text -businessTransactionHash -``` - -Purpose - -Return the canonical hash that will be embedded into the -`ExecutionAuthorizationPayload`. - -This hash is later used by the execution gateway to verify that -the executable content has not changed between authorization and -execution. - -Status - -✅ Verified - ---- - -## Security Properties - -The Executable Content Hasher performs no cryptographic -implementation itself. - -It guarantees that both the authorization producer and the -execution verifier rely on the same deterministic hashing -implementation. - -By embedding the resulting -`businessTransactionHash` into the authorization payload, the -system cryptographically binds an authorization to the exact -executable content it approved. - -Status - -✅ Verified - ---- - -## Architectural Notes - -ExecutableContentHasher is a thin wrapper around -`TrustRecordHasher`. - -It introduces no additional - -- serialization -- hashing logic -- cryptographic algorithms - -Its purpose is to provide a domain-specific abstraction for -hashing executable content while ensuring a single canonical -implementation is reused throughout the system. - -Status - -✅ Verified - ---- - -## Audit Notes - -ExecutableContentHasher performs one responsibility only. - -- Compute the canonical hash of `ExecutableContent` by delegating - to `TrustRecordHasher`. - -It does not - -- implement hashing algorithms -- perform canonical serialization directly -- sign authorizations -- verify hashes -- manage cryptographic keys -- persist artifacts - -The implementation deliberately avoids duplicate hashing logic, -ensuring that authorization generation and execution verification -always compute identical hashes for identical executable content. - -Status - -✅ Verified - -# Stage 30: Crypto Bootstrap - -## File - -```text -packages/crypto/src/CryptoBootstrap.ts -``` - -## Responsibility - -Construct and configure the Parmana cryptographic subsystem. - -CryptoBootstrap is the composition root for the crypto package. It -creates configured `CryptoProvider` instances by registering built-in -hash and signature providers, loading runtime configuration, and -assembling the provider through `CryptoBuilder`. - -It also supports hybrid cryptography by constructing both primary and -secondary providers. - -Status - -✅ Verified - ---- - -## Uses - -```text -CryptoBuilder - -HashRegistry - -SignatureRegistry - -SHA256HashProvider - -Ed25519SignatureProvider - -Dilithium3SignatureProvider - -loadConfig() -``` - -Status - -✅ Verified - ---- - -## Public Operations - -```text -create() - -createHybrid() -``` - -Private Operation - -```text -buildProvider( - signatureAlgorithm -) -``` - -Status - -✅ Verified - ---- - -## Execution Sequence (Single Provider) - -```text -loadConfig() - -↓ - -Create Registries - -↓ - -Register -Hash Provider - -↓ - -Register -Signature Providers - -↓ - -CryptoBuilder - -↓ - -CryptoProvider -``` - -Status - -✅ Verified - ---- - -## Execution Sequence (Hybrid Provider) - -```text -loadConfig() - -↓ - -Primary Algorithm - -↓ - -buildProvider() - -↓ - -Primary Provider - - + - -Secondary Algorithm - -↓ - -buildProvider() - -↓ - -Secondary Provider - -↓ - -HybridCryptoProvider -``` - -Status - -✅ Verified - ---- - -## Phase 1: Load Configuration - -Component - -```text -loadConfig() -``` - -Purpose - -Load the configured cryptographic algorithms from the application -configuration. - -Configuration includes - -```text -Hash Provider - -Primary Signature Provider - -Secondary Signature Provider -``` - -Status - -✅ Verified - ---- - -## Phase 2: Register Built-in Providers - -Hash Providers - -```text -SHA256HashProvider -``` - -Signature Providers - -```text -Ed25519SignatureProvider - -Dilithium3SignatureProvider -``` - -Purpose - -Populate the provider registries with the supported cryptographic -implementations. - -Status - -✅ Verified - ---- - -## Phase 3: Build Crypto Provider - -Component - -```text -CryptoBuilder -``` - -Operations - -```text -withHash() - -withSignature() - -build() -``` - -Purpose - -Assemble a `CryptoProvider` using the configured hash provider and -selected signature algorithm. - -Status - -✅ Verified - ---- - -## Phase 4: Cache Provider Instance - -Static Fields - -```text -provider - -hybrid -``` - -Purpose - -Reuse previously constructed providers and avoid rebuilding the crypto -configuration multiple times during application execution. - -Status - -✅ Verified - ---- - -## Phase 5: Hybrid Provider Construction - -Method - -```text -createHybrid() -``` - -Validation - -```text -Secondary Signature Provider -must be configured -``` - -Output - -```text -HybridCryptoProvider -``` - -Containing - -```text -primary - -secondary -``` - -Purpose - -Support migration or interoperability scenarios requiring two -cryptographic signature algorithms. - -Status - -✅ Verified - ---- - -## Security Properties - -Supported hash provider - -```text -SHA-256 -``` - -Supported signature providers - -```text -Ed25519 - -Dilithium3 -``` - -Cryptographic implementations are selected through configuration rather -than hard-coded throughout the application. - -Hybrid mode constructs two independent cryptographic providers using -different signature algorithms. - -Provider instances are cached after creation. - -Status - -✅ Verified - ---- - -## Architectural Notes - -CryptoBootstrap is the composition root of the crypto subsystem. - -It is responsible for - -- loading cryptographic configuration -- registering built-in providers -- assembling configured providers -- exposing singleton provider instances -- constructing hybrid providers - -It does not - -- hash artifacts -- sign artifacts -- verify signatures -- manage cryptographic keys -- perform cryptographic operations directly - -Those responsibilities are delegated to the constructed -`CryptoProvider` instances. - -Status - -✅ Verified - ---- - -## Audit Notes - -CryptoBootstrap performs five responsibilities. - -- Load runtime crypto configuration. -- Register built-in hash and signature providers. -- Assemble configured `CryptoProvider` instances. -- Cache provider instances for reuse. -- Construct hybrid cryptographic providers when configured. - -All cryptographic operations remain delegated to the providers it -creates. CryptoBootstrap serves solely as the composition and -configuration entry point for the Parmana cryptographic subsystem. - -Status - -✅ Verified - -# Stage 31: File Key Provider - -## File - -```text -packages/crypto/src/providers/key/FileKeyProvider.ts -``` - -## Responsibility - -Load cryptographic signing keys from the local filesystem. - -The File Key Provider implements the `KeyProvider` interface and -provides access to private keys, public keys, and key metadata for the -Parmana cryptographic subsystem. - -It abstracts key retrieval so higher-level cryptographic components do -not interact with the filesystem directly. - -Status - -✅ Verified - ---- - -## Implements - -```text -KeyProvider -``` - -Status - -✅ Verified - ---- - -## Uses - -```text -loadConfig() - -Node.js Crypto - -Node.js File System - -Node.js Path -``` - -Status - -✅ Verified - ---- - -## Public Operations - -```text -hasKey() - -getMetadata() - -getPrivateKey() - -getPublicKey() -``` - -Status - -✅ Verified - ---- - -## Key Storage Layout - -Keys are expected to exist within the configured key directory. - -```text -keys/ - - .private.pem - - .public.pem -``` - -The directory defaults to - -```text -./keys -``` - -unless overridden through configuration. - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -loadConfig() - -↓ - -Resolve Key Directory - -↓ - -Resolve Key Path - -↓ - -Verify File Exists - -↓ - -Read PEM File - -↓ - -Create KeyObject - -↓ - -Return Key -``` - -Status - -✅ Verified - ---- - -## Phase 1: Load Configuration - -Component - -```text -loadConfig() -``` - -Configuration - -```text -keys.keyDirectory -``` - -Default - -```text -./keys -``` - -Purpose - -Determine the root directory containing cryptographic keys. - -Status - -✅ Verified - ---- - -## Phase 2: Validate Key Directory - -Method - -```text -getKeyDirectory() -``` - -Validation - -```text -Directory Exists -``` - -Failure - -```text -Throws Error -``` - -Purpose - -Prevent cryptographic operations from using an invalid key location. - -Status - -✅ Verified - ---- - -## Phase 3: Resolve Key Paths - -Private Methods - -```text -privateKeyPath() - -publicKeyPath() -``` - -Generated Paths - -```text -.private.pem - -.public.pem -``` - -Purpose - -Construct the filesystem paths for the requested key pair. - -Status - -✅ Verified - ---- - -## Phase 4: Load Keys - -Methods - -```text -getPrivateKey() - -getPublicKey() -``` - -Operations - -```text -Read PEM File - -↓ - -createPrivateKey() - -or - -createPublicKey() -``` - -Output - -```text -KeyObject -``` - -Purpose - -Convert stored PEM files into Node.js cryptographic key objects. - -Status - -✅ Verified - ---- - -## Phase 5: Retrieve Metadata - -Method - -```text -getMetadata() -``` - -Returns - -```text -keyId - -algorithm -``` - -The algorithm is obtained from - -```text -config.crypto.primarySignatureProvider -``` - -Purpose - -Provide metadata describing the configured signing key. - -Status - -✅ Verified - ---- - -## Security Properties - -The File Key Provider - -- validates the configured key directory exists -- verifies key files exist before loading -- never generates cryptographic keys -- never performs signing or verification -- returns Node.js `KeyObject` instances - -Private and public keys are loaded independently from separate PEM -files. - -Status - -✅ Verified - ---- - -## Architectural Notes - -FileKeyProvider is a development and self-hosted key management -implementation. - -It is responsible only for retrieving cryptographic key material from -the filesystem. - -It does not - -- generate keys -- hash artifacts -- sign artifacts -- verify signatures -- manage cryptographic algorithms - -The implementation explicitly notes that it can be replaced by external -key management systems such as - -- KMS -- HSM -- Cloud Key Vault - -without changing the higher-level cryptographic components. - -Status - -✅ Verified - ---- - -## Audit Notes - -FileKeyProvider performs five responsibilities. - -- Resolve the configured key directory. -- Determine private and public key file paths. -- Load PEM-encoded keys from the filesystem. -- Return Node.js `KeyObject` instances. -- Provide metadata describing the configured signing algorithm. - -It serves as the key retrieval abstraction for the Parmana crypto -subsystem, allowing cryptographic services to remain independent of the -underlying key storage mechanism. - -Status - -✅ Verified - -# Stage 32: Hash Registry - -## File - -```text -packages/crypto/src/providers/HashRegistry.ts -``` - -## Responsibility - -Maintain a registry of available hash providers. - -The Hash Registry maps a `HashAlgorithm` identifier to its -corresponding `HashProvider` implementation and provides lookup -services for the crypto subsystem. - -It acts as the central registry used during crypto provider -construction. - -Status - -✅ Verified - ---- - -## Uses - -```text -HashProvider - -HashAlgorithm -``` - -Status - -✅ Verified - ---- - -## Internal State - -```text -Map< - HashAlgorithm, - HashProvider -> -``` - -Purpose - -Store registered hash providers keyed by their supported algorithm. - -Status - -✅ Verified - ---- - -## Public Operations - -```text -register( - provider -) - -get( - algorithm -) -``` - -Status - -✅ Verified - ---- - -## Execution Sequence: Register Provider - -```text -HashProvider - -↓ - -Read provider.algorithm - -↓ - -Store in Map - -↓ - -Return Registry -``` - -Status - -✅ Verified - ---- - -## Execution Sequence: Retrieve Provider - -```text -HashAlgorithm - -↓ - -Lookup Map - -↓ - -Provider Found - -↓ - -Return HashProvider -``` - -If no provider exists - -```text -Throw Error - -Unknown hash provider -``` - -Status - -✅ Verified - ---- - -## Phase 1: Register Provider - -Method - -```text -register( - provider -) -``` - -Operation - -```text -providers.set( - provider.algorithm, - provider -) -``` - -Purpose - -Associate a hash provider implementation with its supported -algorithm identifier. - -Returns - -```text -this -``` - -allowing fluent registration. - -Status - -✅ Verified - ---- - -## Phase 2: Resolve Provider - -Method - -```text -get( - algorithm -) -``` - -Operation - -```text -providers.get( - algorithm -) -``` - -Purpose - -Retrieve the registered provider corresponding to the requested -hash algorithm. - -Status - -✅ Verified - ---- - -## Phase 3: Validate Lookup - -Validation - -```text -Provider Exists -``` - -Failure - -```text -Throw Error - -Unknown hash provider: - -``` - -Purpose - -Prevent the crypto subsystem from operating with an -unregistered hash implementation. - -Status - -✅ Verified - ---- - -## Security Properties - -The Hash Registry - -- maintains a single provider per algorithm -- prevents use of unknown hash algorithms -- performs no cryptographic operations -- contains no hashing logic - -It serves purely as an algorithm-to-provider lookup mechanism. - -Status - -✅ Verified - ---- - -## Architectural Notes - -HashRegistry is an infrastructure component. - -It is responsible only for - -- registering hash providers -- resolving providers by algorithm identifier - -It does not - -- compute hashes -- serialize artifacts -- sign artifacts -- verify signatures -- manage cryptographic keys - -The registry is populated by `CryptoBootstrap` during crypto -subsystem initialization. - -Status - -✅ Verified - ---- - -## Audit Notes - -HashRegistry performs two responsibilities. - -- Register `HashProvider` implementations. -- Resolve providers by `HashAlgorithm`. - -It acts as the lookup table connecting configured algorithm -identifiers to concrete hashing implementations, enabling the -crypto subsystem to remain extensible without embedding -algorithm-specific logic into higher-level components. - -Status - -✅ Verified - -# Stage 32: Hash Registry - -## File - -```text -packages/crypto/src/providers/HashRegistry.ts -``` - -## Responsibility - -Maintain a registry of available hash providers. - -The Hash Registry maps a `HashAlgorithm` identifier to its -corresponding `HashProvider` implementation and provides lookup -services for the crypto subsystem. - -It acts as the central registry used during crypto provider -construction. - -Status - -✅ Verified - ---- - -## Uses - -```text -HashProvider - -HashAlgorithm -``` - -Status - -✅ Verified - ---- - -## Internal State - -```text -Map< - HashAlgorithm, - HashProvider -> -``` - -Purpose - -Store registered hash providers keyed by their supported algorithm. - -Status - -✅ Verified - ---- - -## Public Operations - -```text -register( - provider -) - -get( - algorithm -) -``` - -Status - -✅ Verified - ---- - -## Execution Sequence: Register Provider - -```text -HashProvider - -↓ - -Read provider.algorithm - -↓ - -Store in Map - -↓ - -Return Registry -``` - -Status - -✅ Verified - ---- - -## Execution Sequence: Retrieve Provider - -```text -HashAlgorithm - -↓ - -Lookup Map - -↓ - -Provider Found - -↓ - -Return HashProvider -``` - -If no provider exists - -```text -Throw Error - -Unknown hash provider -``` - -Status - -✅ Verified - ---- - -## Phase 1: Register Provider - -Method - -```text -register( - provider -) -``` - -Operation - -```text -providers.set( - provider.algorithm, - provider -) -``` - -Purpose - -Associate a hash provider implementation with its supported -algorithm identifier. - -Returns - -```text -this -``` - -allowing fluent registration. - -Status - -✅ Verified - ---- - -## Phase 2: Resolve Provider - -Method - -```text -get( - algorithm -) -``` - -Operation - -```text -providers.get( - algorithm -) -``` - -Purpose - -Retrieve the registered provider corresponding to the requested -hash algorithm. - -Status - -✅ Verified - ---- - -## Phase 3: Validate Lookup - -Validation - -```text -Provider Exists -``` - -Failure - -```text -Throw Error - -Unknown hash provider: - -``` - -Purpose - -Prevent the crypto subsystem from operating with an -unregistered hash implementation. - -Status - -✅ Verified - ---- - -## Security Properties - -The Hash Registry - -- maintains a single provider per algorithm -- prevents use of unknown hash algorithms -- performs no cryptographic operations -- contains no hashing logic - -It serves purely as an algorithm-to-provider lookup mechanism. - -Status - -✅ Verified - ---- - -## Architectural Notes - -HashRegistry is an infrastructure component. - -It is responsible only for - -- registering hash providers -- resolving providers by algorithm identifier - -It does not - -- compute hashes -- serialize artifacts -- sign artifacts -- verify signatures -- manage cryptographic keys - -The registry is populated by `CryptoBootstrap` during crypto -subsystem initialization. - -Status - -✅ Verified - ---- - -## Audit Notes - -HashRegistry performs two responsibilities. - -- Register `HashProvider` implementations. -- Resolve providers by `HashAlgorithm`. - -It acts as the lookup table connecting configured algorithm -identifiers to concrete hashing implementations, enabling the -crypto subsystem to remain extensible without embedding -algorithm-specific logic into higher-level components. - -Status - -✅ Verified - -# Stage 33: Signature Registry - -## File - -```text -packages/crypto/src/providers/SignatureRegistry.ts -``` - -## Responsibility - -Maintain a registry of available signature providers. - -The Signature Registry maps a `SignatureAlgorithm` identifier to its -corresponding `SignatureProvider` implementation and provides lookup -services for the crypto subsystem. - -It acts as the central registry used during crypto provider -construction. - -Status - -✅ Verified - ---- - -## Uses - -```text -SignatureProvider - -SignatureAlgorithm -``` - -Status - -✅ Verified - ---- - -## Internal State - -```text -Map< - SignatureAlgorithm, - SignatureProvider -> -``` - -Purpose - -Store registered signature providers keyed by their supported -algorithm. - -Status - -✅ Verified - ---- - -## Public Operations - -```text -register( - provider -) - -get( - algorithm -) -``` - -Status - -✅ Verified - ---- - -## Execution Sequence: Register Provider - -```text -SignatureProvider - -↓ - -Read provider.algorithm - -↓ - -Store in Map - -↓ - -Return Registry -``` - -Status - -✅ Verified - ---- - -## Execution Sequence: Retrieve Provider - -```text -SignatureAlgorithm - -↓ - -Lookup Map - -↓ - -Provider Found - -↓ - -Return SignatureProvider -``` - -If no provider exists - -```text -Throw Error - -Unknown signature provider -``` - -Status - -✅ Verified - ---- - -## Phase 1: Register Provider - -Method - -```text -register( - provider -) -``` - -Operation - -```text -providers.set( - provider.algorithm, - provider -) -``` - -Purpose - -Associate a signature provider implementation with its supported -algorithm identifier. - -Returns - -```text -this -``` - -allowing fluent registration. - -Status - -✅ Verified - ---- - -## Phase 2: Resolve Provider - -Method - -```text -get( - algorithm -) -``` - -Operation - -```text -providers.get( - algorithm -) -``` - -Purpose - -Retrieve the registered provider corresponding to the requested -signature algorithm. - -Status - -✅ Verified - ---- - -## Phase 3: Validate Lookup - -Validation - -```text -Provider Exists -``` - -Failure - -```text -Throw Error - -Unknown signature provider: - -``` - -Purpose - -Prevent the crypto subsystem from operating with an -unregistered signature implementation. - -Status - -✅ Verified - ---- - -## Security Properties - -The Signature Registry - -- maintains a single provider per algorithm -- prevents use of unknown signature algorithms -- performs no cryptographic operations -- contains no signing or verification logic - -It serves purely as an algorithm-to-provider lookup mechanism. - -Status - -✅ Verified - ---- - -## Architectural Notes - -SignatureRegistry is an infrastructure component. - -It is responsible only for - -- registering signature providers -- resolving providers by algorithm identifier - -It does not - -- generate signatures -- verify signatures -- hash artifacts -- serialize artifacts -- manage cryptographic keys - -The registry is populated by `CryptoBootstrap` during crypto -subsystem initialization. - -Status - -✅ Verified - ---- - -## Audit Notes - -SignatureRegistry performs two responsibilities. - -- Register `SignatureProvider` implementations. -- Resolve providers by `SignatureAlgorithm`. - -It acts as the lookup table connecting configured signature -algorithms to concrete provider implementations, enabling the -crypto subsystem to support multiple signature algorithms without -embedding algorithm-specific logic into higher-level components. - -Status - -✅ Verified - -# Stage 34: Crypto Builder - -## File - -```text -packages/crypto/src/CryptoBuilder.ts -``` - -## Responsibility - -Construct a configured `CryptoProvider` from independently supplied -hash and signature providers. - -The Crypto Builder is algorithm-agnostic. It does not select -cryptographic implementations itself. Instead, it assembles a -`CryptoProvider` from providers supplied by higher-level components, -such as `CryptoBootstrap`. - -Status - -✅ Verified - ---- - -## Uses - -```text -HashProvider - -SignatureProvider - -CryptoProvider - -CryptoError -``` - -Status - -✅ Verified - ---- - -## Internal State - -```text -HashProvider? - -SignatureProvider? -``` - -Purpose - -Temporarily hold the configured providers until the builder -constructs the final `CryptoProvider`. - -Status - -✅ Verified - ---- - -## Public Operations - -```text -withHash( - provider -) - -withSignature( - provider -) - -build() -``` - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -HashProvider - -↓ - -withHash() - -↓ - -SignatureProvider - -↓ - -withSignature() - -↓ - -Validate Configuration - -↓ - -Construct CryptoProvider -``` - -Status - -✅ Verified - ---- - -## Phase 1: Configure Hash Provider - -Method - -```text -withHash( - provider -) -``` - -Operation - -```text -this.hashProvider = provider -``` - -Purpose - -Assign the hash provider that will become part of the -constructed `CryptoProvider`. - -Returns - -```text -this -``` - -allowing fluent configuration. - -Status - -✅ Verified - ---- - -## Phase 2: Configure Signature Provider - -Method - -```text -withSignature( - provider -) -``` - -Operation - -```text -this.signatureProvider = provider -``` - -Purpose - -Assign the signature provider that will become part of the -constructed `CryptoProvider`. - -Returns - -```text -this -``` - -allowing fluent configuration. - -Status - -✅ Verified - ---- - -## Phase 3: Validate Configuration - -Method - -```text -build() -``` - -Validation - -```text -HashProvider configured - -SignatureProvider configured -``` - -Failure - -```text -CryptoError - -HashProvider has not been configured. - -or - -SignatureProvider has not been configured. -``` - -Purpose - -Prevent construction of an incomplete cryptographic provider. - -Status - -✅ Verified - ---- - -## Phase 4: Construct CryptoProvider - -Returns - -```text -CryptoProvider -``` - -Containing - -```text -hash - -signature -``` - -Purpose - -Produce the configured cryptographic provider used throughout -the Parmana crypto subsystem. - -Status - -✅ Verified - ---- - -## Security Properties - -CryptoBuilder - -- requires both a hash provider and a signature provider -- rejects incomplete configurations -- performs no cryptographic operations -- contains no algorithm-specific logic - -It assembles existing provider implementations into a single -`CryptoProvider`. - -Status - -✅ Verified - ---- - -## Architectural Notes - -CryptoBuilder is a composition utility. - -It is responsible only for assembling a `CryptoProvider`. - -It does not - -- select algorithms -- register providers -- hash artifacts -- generate signatures -- verify signatures -- manage cryptographic keys - -Algorithm selection is delegated to `CryptoBootstrap`, while -cryptographic operations are delegated to the configured providers. - -Status - -✅ Verified - ---- - -## Audit Notes - -CryptoBuilder performs three responsibilities. - -- Accept a configured `HashProvider`. -- Accept a configured `SignatureProvider`. -- Construct a validated `CryptoProvider`. - -It serves as the final assembly step of the Parmana cryptographic -configuration pipeline, separating provider selection from provider -construction and ensuring the resulting `CryptoProvider` is complete -before use. - -Status - -✅ Verified - -# Stage 35: SHA-256 Hash Provider - -## File - -```text -packages/crypto/src/providers/hash/SHA256HashProvider.ts -``` - -## Responsibility - -Compute SHA-256 cryptographic hashes over binary data. - -The SHA-256 Hash Provider implements the `HashProvider` interface and -provides the concrete hashing implementation used by the Parmana crypto -subsystem when the configured hash algorithm is SHA-256. - -Status - -✅ Verified - ---- - -## Implements - -```text -HashProvider -``` - -Status - -✅ Verified - ---- - -## Uses - -```text -Node.js Crypto - -createHash() -``` - -Status - -✅ Verified - ---- - -## Supported Algorithm - -```text -HashAlgorithms.SHA256 -``` - -Exposed as - -```text -algorithm -``` - -Status - -✅ Verified - ---- - -## Public Operation - -```text -hash( - data -) -``` - -Input - -```text -Uint8Array -``` - -Output - -```text -Hexadecimal Hash -``` - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -Uint8Array - -↓ - -createHash("sha256") - -↓ - -update(data) - -↓ - -digest("hex") - -↓ - -SHA-256 Hash -``` - -Status - -✅ Verified - ---- - -## Phase 1: Receive Binary Data - -Input - -```text -Uint8Array -``` - -Purpose - -Accept canonical binary data produced by higher-level components for -hash computation. - -Status - -✅ Verified - ---- - -## Phase 2: Compute SHA-256 Digest - -Operation - -```text -createHash("sha256") -``` - -Processing - -```text -update(data) - -↓ - -digest("hex") -``` - -Purpose - -Compute the SHA-256 digest of the supplied binary data and encode the -result as a hexadecimal string. - -Status - -✅ Verified - ---- - -## Phase 3: Return Hash - -Output - -```text -Hexadecimal String -``` - -Purpose - -Return the deterministic SHA-256 hash for downstream cryptographic -operations. - -Status - -✅ Verified - ---- - -## Security Properties - -The SHA-256 Hash Provider - -- implements the SHA-256 hashing algorithm -- accepts binary input only -- produces deterministic hexadecimal output -- performs no serialization -- maintains no internal state - -Status - -✅ Verified - ---- - -## Architectural Notes - -SHA256HashProvider is a concrete implementation of the `HashProvider` -interface. - -It is responsible only for computing SHA-256 hashes. - -It does not - -- serialize artifacts -- select algorithms -- register providers -- sign artifacts -- verify signatures -- manage cryptographic keys - -Algorithm selection is performed by `CryptoBootstrap`; this class -implements only the hashing operation. - -Status - -✅ Verified - ---- - -## Audit Notes - -SHA256HashProvider performs one responsibility. - -- Compute the SHA-256 hash of a supplied `Uint8Array` and return the - digest as a hexadecimal string. - -It is the concrete hash implementation registered with -`HashRegistry` and assembled into the `CryptoProvider` by -`CryptoBootstrap` when SHA-256 is configured as the active hash -algorithm. - -Status - -✅ Verified - -# Stage 35: SHA-256 Hash Provider - -## File - -```text -packages/crypto/src/providers/hash/SHA256HashProvider.ts -``` - -## Responsibility - -Compute SHA-256 cryptographic hashes over binary data. - -The SHA-256 Hash Provider implements the `HashProvider` interface and -provides the concrete hashing implementation used by the Parmana crypto -subsystem when the configured hash algorithm is SHA-256. - -Status - -✅ Verified - ---- - -## Implements - -```text -HashProvider -``` - -Status - -✅ Verified - ---- - -## Uses - -```text -Node.js Crypto - -createHash() -``` - -Status - -✅ Verified - ---- - -## Supported Algorithm - -```text -HashAlgorithms.SHA256 -``` - -Exposed as - -```text -algorithm -``` - -Status - -✅ Verified - ---- - -## Public Operation - -```text -hash( - data -) -``` - -Input - -```text -Uint8Array -``` - -Output - -```text -Hexadecimal Hash -``` - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -Uint8Array - -↓ - -createHash("sha256") - -↓ - -update(data) - -↓ - -digest("hex") - -↓ - -SHA-256 Hash -``` - -Status - -✅ Verified - ---- - -## Phase 1: Receive Binary Data - -Input - -```text -Uint8Array -``` - -Purpose - -Accept canonical binary data produced by higher-level components for -hash computation. - -Status - -✅ Verified - ---- - -## Phase 2: Compute SHA-256 Digest - -Operation - -```text -createHash("sha256") -``` - -Processing - -```text -update(data) - -↓ - -digest("hex") -``` - -Purpose - -Compute the SHA-256 digest of the supplied binary data and encode the -result as a hexadecimal string. - -Status - -✅ Verified - ---- - -## Phase 3: Return Hash - -Output - -```text -Hexadecimal String -``` - -Purpose - -Return the deterministic SHA-256 hash for downstream cryptographic -operations. - -Status - -✅ Verified - ---- - -## Security Properties - -The SHA-256 Hash Provider - -- implements the SHA-256 hashing algorithm -- accepts binary input only -- produces deterministic hexadecimal output -- performs no serialization -- maintains no internal state - -Status - -✅ Verified - ---- - -## Architectural Notes - -SHA256HashProvider is a concrete implementation of the `HashProvider` -interface. - -It is responsible only for computing SHA-256 hashes. - -It does not - -- serialize artifacts -- select algorithms -- register providers -- sign artifacts -- verify signatures -- manage cryptographic keys - -Algorithm selection is performed by `CryptoBootstrap`; this class -implements only the hashing operation. - -Status - -✅ Verified - ---- - -## Audit Notes - -SHA256HashProvider performs one responsibility. - -- Compute the SHA-256 hash of a supplied `Uint8Array` and return the - digest as a hexadecimal string. - -It is the concrete hash implementation registered with -`HashRegistry` and assembled into the `CryptoProvider` by -`CryptoBootstrap` when SHA-256 is configured as the active hash -algorithm. - -Status - -✅ Verified - -# Stage 36: Ed25519 Signature Provider - -## File - -```text -packages/crypto/src/providers/signature/Ed25519SignatureProvider.ts -``` - -## Responsibility - -Generate and verify digital signatures using the Ed25519 signature -algorithm. - -The Ed25519 Signature Provider implements the `SignatureProvider` -interface and provides a stateless implementation of Ed25519 signing -and signature verification. - -Key management is delegated to a `KeyProvider`. - -Status - -✅ Verified - ---- - -## Implements - -```text -SignatureProvider -``` - -Status - -✅ Verified - ---- - -## Uses - -```text -Node.js Crypto - -sign() - -verify() - -assertKeyType() -``` - -Status - -✅ Verified - ---- - -## Supported Algorithm - -```text -SignatureAlgorithms.ED25519 -``` - -Exposed as - -```text -algorithm -``` - -Status - -✅ Verified - ---- - -## Public Operations - -```text -sign( - data, - privateKey -) - -verify( - data, - signature, - publicKey -) -``` - -Status - -✅ Verified - ---- - -## Execution Sequence: Sign - -```text -Uint8Array - -↓ - -Validate Private Key Type - -↓ - -Node.js sign() - -↓ - -Base64 Encode - -↓ - -Digital Signature -``` - -Status - -✅ Verified - ---- - -## Execution Sequence: Verify - -```text -Uint8Array - -↓ - -Validate Public Key Type - -↓ - -Base64 Decode Signature - -↓ - -Node.js verify() - -↓ - -Boolean Result -``` - -Status - -✅ Verified - ---- - -## Phase 1: Validate Key Type - -Component - -```text -assertKeyType() -``` - -Expected Key Type - -```text -ed25519 -``` - -Purpose - -Ensure the supplied key matches the Ed25519 algorithm before any -cryptographic operation is performed. - -Status - -✅ Verified - ---- - -## Phase 2: Generate Signature - -Method - -```text -sign() -``` - -Operation - -```text -Node.js sign( - null, - Buffer.from(data), - privateKey -) -``` - -Output - -```text -Base64 Signature -``` - -Purpose - -Generate an Ed25519 digital signature over the supplied binary data. - -Status - -✅ Verified - ---- - -## Phase 3: Verify Signature - -Method - -```text -verify() -``` - -Operation - -```text -Node.js verify( - null, - Buffer.from(data), - publicKey, - Buffer.from(signature, "base64") -) -``` - -Output - -```text -Boolean -``` - -Purpose - -Verify that the supplied Base64-encoded signature matches the binary -data and public key. - -Status - -✅ Verified - ---- - -## Security Properties - -The Ed25519 Signature Provider - -- validates key type before signing -- validates key type before verification -- accepts canonical binary input -- encodes signatures as Base64 -- performs no serialization -- maintains no mutable internal state - -The provider is frozen during construction using - -```text -Object.freeze(this) -``` - -making the instance immutable after initialization. - -Status - -✅ Verified - ---- - -## Architectural Notes - -Ed25519SignatureProvider is a concrete implementation of the -`SignatureProvider` interface. - -It is responsible only for Ed25519 signing and verification. - -It does not - -- load cryptographic keys -- serialize artifacts -- hash artifacts -- select signature algorithms -- register providers - -Key management is delegated to a `KeyProvider`, while algorithm -selection is performed by `CryptoBootstrap`. - -Status - -✅ Verified - ---- - -## Audit Notes - -Ed25519SignatureProvider performs two responsibilities. - -- Generate Ed25519 digital signatures. -- Verify Ed25519 digital signatures. - -It delegates key lifecycle management outside the provider and relies -on Node.js cryptographic primitives for all signing and verification -operations. The provider remains stateless and immutable throughout its -lifecycle. - -Status - -✅ Verified - -# Stage 37: Dilithium3 Signature Provider - -## File - -```text -packages/crypto/src/providers/signature/Dilithium3SignatureProvider.ts -``` - -## Responsibility - -Generate and verify digital signatures using the Dilithium3 -(ML-DSA-65) post-quantum signature algorithm. - -The Dilithium3 Signature Provider implements the -`SignatureProvider` interface and provides a stateless -implementation of ML-DSA-65 signing and signature verification -using Node.js native cryptographic support. - -Key management is delegated to a `KeyProvider`. - -Status - -✅ Verified - ---- - -## Implements - -```text -SignatureProvider -``` - -Status - -✅ Verified - ---- - -## Uses - -```text -Node.js Crypto - -sign() - -verify() - -assertKeyType() -``` - -Status - -✅ Verified - ---- - -## Supported Algorithm - -```text -SignatureAlgorithms.DILITHIUM3 -``` - -Node.js Key Type - -```text -ml-dsa-65 -``` - -Exposed as - -```text -algorithm -``` - -Status - -✅ Verified - ---- - -## Public Operations - -```text -sign( - data, - privateKey -) - -verify( - data, - signature, - publicKey -) -``` - -Status - -✅ Verified - ---- - -## Execution Sequence: Sign - -```text -Uint8Array - -↓ - -Validate Private Key Type - -↓ - -Node.js sign() - -↓ - -Base64 Encode - -↓ - -Digital Signature -``` - -Status - -✅ Verified - ---- - -## Execution Sequence: Verify - -```text -Uint8Array - -↓ - -Validate Public Key Type - -↓ - -Base64 Decode Signature - -↓ - -Node.js verify() - -↓ - -Boolean Result -``` - -Status - -✅ Verified - ---- - -## Phase 1: Validate Key Type - -Component - -```text -assertKeyType() -``` - -Expected Key Type - -```text -ml-dsa-65 -``` - -Purpose - -Ensure the supplied key is an ML-DSA-65 key before any -cryptographic operation is performed. - -Status - -✅ Verified - ---- - -## Phase 2: Generate Signature - -Method - -```text -sign() -``` - -Operation - -```text -Node.js sign( - null, - Buffer.from(data), - privateKey -) -``` - -Output - -```text -Base64 Signature -``` - -Purpose - -Generate an ML-DSA-65 digital signature over the supplied binary -data. - -Status - -✅ Verified - ---- - -## Phase 3: Verify Signature - -Method - -```text -verify() -``` - -Operation - -```text -Node.js verify( - null, - Buffer.from(data), - publicKey, - Buffer.from(signature, "base64") -) -``` - -Output - -```text -Boolean -``` - -Purpose - -Verify that the supplied Base64-encoded signature matches the -binary data and public key. - -Status - -✅ Verified - ---- - -## Security Properties - -The Dilithium3 Signature Provider - -- validates key type before signing -- validates key type before verification -- accepts canonical binary input -- encodes signatures as Base64 -- performs no serialization -- maintains no mutable internal state - -The provider instance is frozen during construction using - -```text -Object.freeze(this) -``` - -making it immutable after initialization. - -Unlike Ed25519, ML-DSA-65 signatures are **randomized**. Signing the -same message multiple times with the same private key produces -different signatures, while each remains valid for verification. - -Status - -✅ Verified - ---- - -## Architectural Notes - -Dilithium3SignatureProvider is a concrete implementation of the -`SignatureProvider` interface. - -It is responsible only for ML-DSA-65 signing and verification. - -It does not - -- load cryptographic keys -- serialize artifacts -- hash artifacts -- select signature algorithms -- register providers - -Key management is delegated to a `KeyProvider`, while algorithm -selection is performed by `CryptoBootstrap`. - -Status - -✅ Verified - ---- - -## Audit Notes - -Dilithium3SignatureProvider performs two responsibilities. - -- Generate ML-DSA-65 digital signatures. -- Verify ML-DSA-65 digital signatures. - -The implementation relies entirely on Node.js native cryptographic -support. Compared to the Ed25519 provider, the primary behavioral -difference is that ML-DSA-65 signatures are intentionally -non-deterministic, producing different valid signatures for identical -inputs while preserving verification correctness. - -Status - -✅ Verified - -# Stage 38: Hybrid Signer - -## File - -```text -packages/crypto/src/HybridSigner.ts -``` - -## Responsibility - -Generate a bundle containing digital signatures from two independent -cryptographic providers. - -The Hybrid Signer signs the same canonical artifact using both the -primary and secondary `CryptoProvider` instances and returns the -resulting signatures as a `SignatureBundle`. - -This enables simultaneous support for multiple signature algorithms, -such as Ed25519 and Dilithium3. - -Status - -✅ Verified - ---- - -## Uses - -```text -ArtifactSigner - -HybridCryptoProvider - -SignatureBundle - -SignatureEntry -``` - -Status - -✅ Verified - ---- - -## Public Operation - -```text -sign( - artifact, - primaryPrivateKey, - secondaryPrivateKey, - primaryKeyId, - secondaryKeyId -) -``` - -Input - -```text -Artifact - -Primary Private Key - -Secondary Private Key - -Primary Key Identifier - -Secondary Key Identifier -``` - -Output - -```text -SignatureBundle -``` - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -Artifact - -↓ - -Primary ArtifactSigner - -↓ - -Primary Signature - - + - -Secondary ArtifactSigner - -↓ - -Secondary Signature - -↓ - -Create Signature Entries - -↓ - -SignatureBundle -``` - -Status - -✅ Verified - ---- - -## Phase 1: Create Signers - -Construct - -```text -ArtifactSigner( - primary CryptoProvider -) - -ArtifactSigner( - secondary CryptoProvider -) -``` - -Purpose - -Create independent signing components for each configured -cryptographic provider. - -Status - -✅ Verified - ---- - -## Phase 2: Generate Primary Signature - -Component - -```text -ArtifactSigner -``` - -Provider - -```text -Primary CryptoProvider -``` - -Output - -```text -Primary Signature -``` - -Purpose - -Generate the signature using the configured primary algorithm. - -Status - -✅ Verified - ---- - -## Phase 3: Generate Secondary Signature - -Component - -```text -ArtifactSigner -``` - -Provider - -```text -Secondary CryptoProvider -``` - -Output - -```text -Secondary Signature -``` - -Purpose - -Generate the signature using the configured secondary algorithm. - -Status - -✅ Verified - ---- - -## Phase 4: Build Signature Entries - -Each entry contains - -```text -algorithm - -keyId - -signature -``` - -The algorithm value is obtained from the corresponding -`CryptoProvider.signature.algorithm`. - -Status - -✅ Verified - ---- - -## Phase 5: Return Signature Bundle - -Returns - -```text -SignatureBundle -``` - -Containing - -```text -signatures[] -``` - -where each element is a `SignatureEntry`. - -Status - -✅ Verified - ---- - -## Security Properties - -The Hybrid Signer - -- signs the identical artifact twice -- uses independent cryptographic providers -- maintains separate key identifiers -- preserves the signature algorithm associated with each signature -- delegates canonical serialization and signing to `ArtifactSigner` - -It performs no cryptographic implementation directly. - -Status - -✅ Verified - ---- - -## Architectural Notes - -HybridSigner is an orchestration component. - -It is responsible only for coordinating multi-algorithm signing. - -It does not - -- serialize artifacts -- hash artifacts -- implement signature algorithms -- verify signatures -- manage cryptographic keys - -All cryptographic operations are delegated to two independent -`ArtifactSigner` instances configured with separate -`CryptoProvider` implementations. - -Status - -✅ Verified - ---- - -## Audit Notes - -HybridSigner performs five responsibilities. - -- Create signers for the primary and secondary crypto providers. -- Generate the primary signature. -- Generate the secondary signature. -- Associate each signature with its algorithm and key identifier. -- Return a `SignatureBundle` containing both signatures. - -The implementation supports cryptographic migration and -multi-algorithm interoperability by producing signatures from two -independent providers over the same canonical artifact without -duplicating signing logic. - -Status - -✅ Verified - -# Stage 39: Hybrid Verifier - -## File - -```text -packages/crypto/src/HybridVerifier.ts -``` - -## Responsibility - -Verify a `SignatureBundle` using two independent cryptographic -providers. - -The Hybrid Verifier validates that an artifact has been correctly -signed by both the primary and secondary signature algorithms. A -verification succeeds only when both signatures are present and both -verify successfully. - -Status - -✅ Verified - ---- - -## Uses - -```text -SignatureVerifier - -HybridCryptoProvider - -SignatureBundle -``` - -Status - -✅ Verified - ---- - -## Public Operation - -```text -verify( - artifact, - bundle, - primaryPublicKey, - secondaryPublicKey -) -``` - -Input - -```text -Artifact - -SignatureBundle - -Primary Public Key - -Secondary Public Key -``` - -Output - -```text -Boolean -``` - -Status - -✅ Verified - ---- - -## Execution Sequence - -```text -SignatureBundle - -↓ - -Validate Bundle Size - -↓ - -Create Primary Verifier - -↓ - -Create Secondary Verifier - -↓ - -Verify Primary Signature - -↓ - -Verify Secondary Signature - -↓ - -Return - -primaryVerified && -secondaryVerified -``` - -Status - -✅ Verified - ---- - -## Phase 1: Validate Signature Bundle - -Validation - -```text -bundle.signatures.length == 2 -``` - -Failure - -```text -Return false -``` - -Purpose - -Ensure the bundle contains exactly two signatures before attempting -verification. - -Status - -✅ Verified - ---- - -## Phase 2: Create Verifiers - -Construct - -```text -SignatureVerifier( - primary CryptoProvider -) - -SignatureVerifier( - secondary CryptoProvider -) -``` - -Purpose - -Create independent verification components for each configured -cryptographic provider. - -Status - -✅ Verified - ---- - -## Phase 3: Validate Signature Entries - -Extract - -```text -primarySignature - -secondarySignature -``` - -Validation - -```text -Both entries exist -``` - -Failure - -```text -Return false -``` - -Purpose - -Ensure both expected signature entries are present before -verification. - -Status - -✅ Verified - ---- - -## Phase 4: Verify Primary Signature - -Component - -```text -SignatureVerifier -``` - -Provider - -```text -Primary CryptoProvider -``` - -Input - -```text -Artifact - -Primary Signature - -Primary Public Key -``` - -Output - -```text -Boolean -``` - -Purpose - -Verify the primary digital signature. - -Status - -✅ Verified - ---- - -## Phase 5: Verify Secondary Signature - -Component - -```text -SignatureVerifier -``` - -Provider - -```text -Secondary CryptoProvider -``` - -Input - -```text -Artifact - -Secondary Signature - -Secondary Public Key -``` - -Output - -```text -Boolean -``` - -Purpose - -Verify the secondary digital signature. - -Status - -✅ Verified - ---- - -## Phase 6: Return Verification Result - -Operation - -```text -primaryVerified && -secondaryVerified -``` - -Output - -```text -Boolean -``` - -Purpose - -Return success only when both signatures verify successfully. - -Status - -✅ Verified - ---- - -## Security Properties - -The Hybrid Verifier - -- requires exactly two signatures -- validates both signature entries exist -- independently verifies each signature -- fails closed by returning `false` if bundle validation fails -- requires both verifications to succeed - -It performs no cryptographic implementation directly. - -Status - -✅ Verified - ---- - -## Architectural Notes - -HybridVerifier is an orchestration component. - -It is responsible only for coordinating multi-algorithm verification. - -It does not - -- serialize artifacts -- hash artifacts -- implement signature algorithms -- manage cryptographic keys - -All verification operations are delegated to two independent -`SignatureVerifier` instances configured with separate -`CryptoProvider` implementations. - -Status - -✅ Verified - ---- - -## Audit Notes - -HybridVerifier performs six responsibilities. - -- Validate the signature bundle structure. -- Create verifiers for the primary and secondary crypto providers. -- Validate the presence of both signature entries. -- Verify the primary signature. -- Verify the secondary signature. -- Return a single verification result requiring both signatures to - succeed. - -The implementation follows a fail-closed approach: malformed bundles, -missing signatures, or any failed verification result in an overall -verification failure. This supports hybrid cryptographic deployments by -requiring successful verification across all configured signature -algorithms. - -Status - -✅ Verified - -# Stage 40: End-to-End Execution Flow - -## Purpose - -Trace the complete execution lifecycle implemented by the Parmana -repository. - -This section consolidates the audited implementation into a single -execution sequence, showing how business transactions move through -authorization, execution, verification, and receipt generation. - -Every stage below references components previously verified during the -implementation audit. - -Status - -✅ Verified - ---- - -## End-to-End Execution Pipeline - -```text -Business Transaction - -↓ - -Business Transaction Mapper - -↓ - -Execution Request Builder - -↓ - -Execution Trust Application - -↓ - -Runtime - -↓ - -Runtime Engine - -↓ - -Runtime Pipeline - -↓ - -Policy Evaluation - -↓ - -Decision - -↓ - -Execution Authorization - -↓ - -AuthorizationSigner - -↓ - -ExecutableContentHasher - -↓ - -ArtifactSigner - -↓ - -Execution Gateway - -↓ - -Execution Service - -↓ - -Execution System - -↓ - -Business Trust Record Builder - -↓ - -Execution Trust Record - -↓ - -Verification Service - -↓ - -VerificationCrypto - -↓ - -Receipt Service - -↓ - -ReceiptCrypto - -↓ - -Execution Receipt -``` - -Status - -✅ Verified - ---- - -## Phase 1: Business Transaction - -Component - -```text -BusinessTransactionService -``` - -Responsibility - -Receive the incoming business transaction and prepare it for runtime -processing. - -Status - -✅ Verified - ---- - -## Phase 2: Runtime Evaluation - -Components - -```text -ExecutionTrustApplication - -Runtime - -RuntimeEngine - -RuntimePipeline -``` - -Responsibility - -Evaluate policy, execute runtime processing, and produce a -deterministic decision. - -Output - -```text -Decision -``` - -Status - -✅ Verified - ---- - -## Phase 3: Authorization - -Components - -```text -AuthorizationSigner - -ExecutableContentHasher - -ArtifactSigner -``` - -Responsibilities - -- Validate authorization lifetime. -- Compute executable content hash. -- Construct authorization payload. -- Produce cryptographic signature. - -Output - -```text -SignedExecutionAuthorization -``` - -Status - -✅ Verified - ---- - -## Phase 4: Execution - -Components - -```text -Execution Gateway - -Execution Service - -Execution System -``` - -Responsibilities - -Execute the approved business operation using the signed -authorization. - -Output - -```text -Execution -``` - -Status - -✅ Verified - ---- - -## Phase 5: Trust Record Construction - -Components - -```text -Business Trust Record Builder - -Execution Trust Record Builder -``` - -Responsibilities - -Create the immutable Execution Trust Record describing the completed -execution. - -Output - -```text -ExecutionTrustRecord -``` - -Status - -✅ Verified - ---- - -## Phase 6: Verification - -Components - -```text -VerificationService - -VerificationCrypto -``` - -Responsibilities - -Verify - -- Trust Record hash -- Digital signature -- Authorization binding - -Output - -```text -Verification -``` - -Status - -✅ Verified - ---- - -## Phase 7: Receipt Generation - -Components - -```text -ReceiptService - -ReceiptCrypto -``` - -Responsibilities - -Generate the signed execution receipt after successful verification. - -Output - -```text -ExecutionReceipt -``` - -Status - -✅ Verified - ---- - -## Cryptographic Flow - -```text -ExecutableContent - -↓ - -ExecutableContentHasher - -↓ - -businessTransactionHash - -↓ - -AuthorizationSigner - -↓ - -ArtifactSigner - -↓ - -Ed25519 / Dilithium3 - -↓ - -Execution - -↓ - -VerificationCrypto - -↓ - -ReceiptCrypto -``` - -Status - -✅ Verified - ---- - -## Component Relationships - -```text -CryptoBootstrap - -↓ - -HashRegistry -SignatureRegistry - -↓ - -CryptoBuilder - -↓ - -CryptoProvider - -↓ - -HashProvider -SignatureProvider - -↓ - -ArtifactSigner -AuthorizationSigner -VerificationCrypto -ReceiptCrypto -HybridSigner -HybridVerifier -``` - -Status - -✅ Verified - ---- - -## Deterministic Properties - -The implementation performs deterministic processing through - -- Canonical serialization -- Shared hashing infrastructure -- Immutable trust artifacts -- Centralized cryptographic provider construction -- Shared authorization hashing -- Shared signature generation -- Shared signature verification - -Status - -✅ Verified - ---- - -## Audit Notes - -The implementation separates responsibilities into four major layers. - -```text -Runtime - -↓ - -Authorization - -↓ - -Execution - -↓ - -Verification & Receipt -``` - -Cryptographic primitives remain isolated within the crypto subsystem, -while runtime services orchestrate business execution. Shared -components such as `ArtifactSigner`, `TrustRecordHasher`, and -`CryptoBuilder` eliminate duplicated cryptographic logic and provide -consistent behavior across authorization, verification, and receipt -generation. - -Status - -✅ Verified - -# Stage 41: Trust Boundaries - -## Purpose - -Identify the trust boundaries enforced by the Parmana execution -pipeline. - -This chapter documents where trust changes within the implementation -and which components enforce each boundary. - -Every boundary described below is derived from previously audited -components. - -Status - -✅ Verified - ---- - -## Trust Boundary Overview - -```text -Client - -│ -│ Untrusted Input -▼ - -Business Transaction Service - -────────────────────────────────── -Boundary 1 -Application Entry -────────────────────────────────── - -↓ - -Runtime - -↓ - -Policy Evaluation - -↓ - -Decision - -────────────────────────────────── -Boundary 2 -Authorization -────────────────────────────────── - -↓ - -AuthorizationSigner - -↓ - -SignedExecutionAuthorization - -────────────────────────────────── -Boundary 3 -Execution -────────────────────────────────── - -↓ - -Execution Gateway - -↓ - -Enterprise System - -↓ - -Execution Result - -────────────────────────────────── -Boundary 4 -Trust Record -────────────────────────────────── - -↓ - -ExecutionTrustRecord - -↓ - -VerificationService - -────────────────────────────────── -Boundary 5 -Verification -────────────────────────────────── - -↓ - -ReceiptService - -↓ - -ExecutionReceipt -``` - -Status - -✅ Verified - ---- - -## Boundary 1: Application Entry - -Components - -```text -BusinessTransactionService - -BusinessTransactionMapper -``` - -Incoming Data - -```text -Business Transaction -``` - -Responsibility - -Convert external business requests into the internal execution model. - -The implementation begins processing only after the transaction has -been mapped into the runtime domain model. - -Status - -✅ Verified - ---- - -## Boundary 2: Authorization - -Components - -```text -AuthorizationSigner - -ExecutableContentHasher - -ArtifactSigner -``` - -Input - -```text -Approved Decision -``` - -Output - -```text -SignedExecutionAuthorization -``` - -Responsibility - -Create a cryptographically signed authorization bound to the exact -executable content and constrained by an authorization lifetime. - -Status - -✅ Verified - ---- - -## Boundary 3: Execution - -Components - -```text -Execution Gateway - -Execution Service - -Execution System -``` - -Input - -```text -SignedExecutionAuthorization -``` - -Output - -```text -Execution -``` - -Responsibility - -Execute only operations that have been authorized by the runtime. - -The execution boundary separates decision-making from external system -interaction. - -Status - -✅ Verified - ---- - -## Boundary 4: Trust Record - -Components - -```text -BusinessTrustRecordBuilder - -ExecutionTrustRecordBuilder -``` - -Output - -```text -ExecutionTrustRecord -``` - -Responsibility - -Capture an immutable record describing the completed execution. - -This artifact becomes the authoritative evidence supplied to the -verification stage. - -Status - -✅ Verified - ---- - -## Boundary 5: Verification - -Components - -```text -VerificationService - -VerificationCrypto - -ReceiptService - -ReceiptCrypto -``` - -Responsibilities - -- Validate trust record integrity. -- Verify digital signatures. -- Verify authorization binding. -- Produce a signed execution receipt. - -The receipt is generated only after successful verification. - -Status - -✅ Verified - ---- - -## Trust Transitions - -```text -External Request - -↓ - -Internal Runtime - -↓ - -Authorized Execution - -↓ - -Executed Operation - -↓ - -Verified Evidence - -↓ - -Signed Receipt -``` - -Status - -✅ Verified - ---- - -## Cryptographic Trust Boundaries - -The implementation establishes cryptographic trust at three points. - -### Authorization - -```text -AuthorizationSigner -``` - -Produces - -```text -SignedExecutionAuthorization -``` - ---- - -### Verification - -```text -VerificationCrypto -``` - -Validates - -- hash integrity -- digital signature -- authorization binding - ---- - -### Receipt - -```text -ReceiptCrypto -``` - -Produces - -```text -ExecutionReceipt -``` - -Status - -✅ Verified - ---- - -## Audit Notes - -The implementation separates trust into distinct stages rather than -relying on a single trusted component. - -Trust progresses through five implementation-defined boundaries: - -- Application Entry -- Authorization -- Execution -- Trust Record -- Verification - -Each boundary is enforced by dedicated components that were verified -individually during the implementation audit. - -Status - -✅ Verified - -# Stage 42: Security Invariants - -## Purpose - -Document the security guarantees that are enforced across the Parmana -implementation. - -Unlike previous sections, this chapter does not introduce new -components. Instead, it consolidates the implementation-backed -properties established throughout the repository audit. - -Each invariant is enforced by one or more audited components. - -Status - -✅ Verified - ---- - -# Invariant 1: Deterministic Decisions - -## Guarantee - -The same execution request always produces the same decision when -evaluated against the same policy version and execution context. - -Enforced By - -```text -RuntimeEngine - -Policy Evaluation - -CanonicalSerializer -``` - -Purpose - -Ensures policy evaluation is reproducible and replayable. - -Status - -✅ Verified - ---- - -# Invariant 2: Canonical Data Representation - -## Guarantee - -Every artifact is serialized into a canonical byte sequence before -hashing or signing. - -Enforced By - -```text -CanonicalSerializer - -ArtifactSigner - -TrustRecordHasher -``` - -Purpose - -Prevents different JSON representations from producing different -cryptographic results. - -Status - -✅ Verified - ---- - -# Invariant 3: Content Integrity - -## Guarantee - -Cryptographic hashes represent the exact executable content that was -authorized. - -Enforced By - -```text -ExecutableContentHasher - -TrustRecordHasher -``` - -Output - -```text -businessTransactionHash -``` - -Purpose - -Detects modification of execution content after authorization. - -Status - -✅ Verified - ---- - -# Invariant 4: Authorization Integrity - -## Guarantee - -Only signed execution authorizations are considered valid. - -Enforced By - -```text -AuthorizationSigner - -ArtifactSigner -``` - -Purpose - -Ensures execution authorizations cannot be forged or modified without -detection. - -Status - -✅ Verified - ---- - -# Invariant 5: Signature Authenticity - -## Guarantee - -Every signature is verified using the corresponding public key and -configured signature algorithm. - -Enforced By - -```text -SignatureVerifier - -VerificationCrypto - -HybridVerifier -``` - -Purpose - -Confirms artifact authenticity and signer identity. - -Status - -✅ Verified - ---- - -# Invariant 6: Authorization Binding - -## Guarantee - -Execution authorization is cryptographically bound to the executable -content through the business transaction hash. - -Enforced By - -```text -ExecutableContentHasher - -AuthorizationSigner - -VerificationCrypto -``` - -Purpose - -Prevents reuse of an authorization for different executable content. - -Status - -✅ Verified - ---- - -# Invariant 7: Immutable Trust Evidence - -## Guarantee - -Execution Trust Records are treated as immutable evidence once -constructed. - -Enforced By - -```text -ExecutionTrustRecordBuilder - -TrustRecordHasher - -VerificationService -``` - -Purpose - -Provides reproducible evidence for auditing and verification. - -Status - -✅ Verified - ---- - -# Invariant 8: Receipt Authenticity - -## Guarantee - -Execution receipts are generated only after successful verification. - -Enforced By - -```text -ReceiptService - -ReceiptCrypto - -VerificationService -``` - -Purpose - -Ensures receipts represent verified executions rather than merely -completed executions. - -Status - -✅ Verified - ---- - -# Invariant 9: Algorithm Isolation - -## Guarantee - -Cryptographic algorithms are interchangeable without changing business -logic. - -Enforced By - -```text -CryptoBuilder - -CryptoBootstrap - -HashRegistry - -SignatureRegistry -``` - -Purpose - -Separates cryptographic implementation from execution logic. - -Status - -✅ Verified - ---- - -# Invariant 10: Key Separation - -## Guarantee - -Private keys are never embedded in business logic. - -Enforced By - -```text -FileKeyProvider - -SignatureProvider - -CryptoProvider -``` - -Purpose - -Centralizes key access and separates key management from signing -operations. - -Status - -✅ Verified - ---- - -# Invariant 11: Provider Validation - -## Guarantee - -Every cryptographic provider is validated before use. - -Enforced By - -```text -CryptoBuilder - -HashRegistry - -SignatureRegistry -``` - -Purpose - -Prevents incomplete or unsupported cryptographic configurations. - -Status - -✅ Verified - ---- - -# Invariant 12: Hybrid Cryptographic Consistency - -## Guarantee - -Hybrid signatures require successful processing by both configured -cryptographic providers. - -Enforced By - -```text -HybridSigner - -HybridVerifier -``` - -Purpose - -Supports cryptographic migration and multi-algorithm deployments while -maintaining equivalent protection across all configured algorithms. - -Status - -✅ Verified - ---- - -# Invariant 13: Fail-Closed Verification - -## Guarantee - -Verification fails whenever required evidence is missing, malformed, or -invalid. - -Enforced By - -```text -VerificationService - -SignatureVerifier - -HybridVerifier -``` - -Examples - -- Missing signatures -- Incorrect bundle structure -- Signature verification failure -- Hash mismatch - -Purpose - -Ensures execution evidence is never accepted on partial validation. - -Status - -✅ Verified - ---- - -# Security Model - -The implementation establishes security through multiple independent -controls rather than relying on a single mechanism. - -```text -Canonical Serialization - -↓ - -Hash Integrity - -↓ - -Digital Signatures - -↓ - -Authorization Binding - -↓ - -Verification - -↓ - -Receipt Generation -``` - -Each layer contributes independent protection against tampering, -forgery, or inconsistent execution evidence. - -Status - -✅ Verified - ---- - -# Audit Notes - -The Parmana implementation enforces security through a layered model of -deterministic processing, canonical representation, cryptographic -binding, and independent verification. - -The repository consistently separates: - -- Business logic from cryptographic operations. -- Key management from signing operations. -- Authorization from execution. -- Execution from verification. -- Verification from receipt generation. - -These invariants collectively provide the foundation for reproducible, -auditable, and cryptographically verifiable execution authorization. - -Status - -✅ Verified - -# Stage 43: Determinism Guarantees - -## Purpose - -Document the implementation mechanisms that ensure Parmana produces -reproducible execution results. - -Determinism is a foundational property of the repository. It ensures -that identical execution inputs produce identical policy decisions, -cryptographic artifacts, and verification outcomes. - -Every guarantee described below is derived from previously audited -components. - -Status - -✅ Verified - ---- - -# Deterministic Execution Model - -```text -Execution Request - -↓ - -Canonical Representation - -↓ - -Policy Evaluation - -↓ - -Authorization - -↓ - -Execution Trust Record - -↓ - -Verification - -↓ - -Execution Receipt -``` - -Every stage operates on deterministic inputs and produces reproducible -outputs. - -Status - -✅ Verified - ---- - -# Guarantee 1: Canonical Serialization - -## Components - -```text -CanonicalSerializer - -ArtifactSigner - -TrustRecordHasher -``` - -Guarantee - -Every artifact is converted into a canonical byte representation before -being hashed or signed. - -Purpose - -Eliminate differences caused by object ordering, formatting, or JSON -encoding variations. - -Result - -```text -Identical Artifact - -↓ - -Identical Bytes -``` - -Status - -✅ Verified - ---- - -# Guarantee 2: Stable Hash Generation - -## Components - -```text -TrustRecordHasher - -ExecutableContentHasher - -SHA256HashProvider -``` - -Guarantee - -The same canonical byte sequence always produces the same SHA-256 hash. - -Result - -```text -Canonical Bytes - -↓ - -SHA-256 - -↓ - -Deterministic Hash -``` - -Status - -✅ Verified - ---- - -# Guarantee 3: Stable Authorization Content - -## Components - -```text -AuthorizationSigner - -ExecutableContentHasher -``` - -Guarantee - -The executable content hash is calculated from the exact business -content being authorized, ensuring that authorization is bound to a -specific executable payload. - -Result - -```text -Executable Content - -↓ - -businessTransactionHash -``` - -Status - -✅ Verified - ---- - -# Guarantee 4: Deterministic Signature Input - -## Components - -```text -ArtifactSigner - -SignatureProvider -``` - -Guarantee - -Every signature operation receives the same canonical byte sequence for -an identical artifact. - -This ensures the input to the signature algorithm is deterministic, -regardless of the algorithm itself. - -Status - -✅ Verified - ---- - -# Guarantee 5: Deterministic Verification - -## Components - -```text -SignatureVerifier - -VerificationCrypto - -HybridVerifier -``` - -Guarantee - -Verification always evaluates the canonical artifact together with the -provided signatures and public keys. - -Given identical inputs, verification produces the same boolean result. - -Status - -✅ Verified - ---- - -# Guarantee 6: Immutable Trust Evidence - -## Components - -```text -ExecutionTrustRecordBuilder - -TrustRecordHasher - -VerificationService -``` - -Guarantee - -Verification operates against immutable trust evidence rather than -reconstructed execution state. - -Purpose - -Ensure replayability and reproducibility. - -Status - -✅ Verified - ---- - -# Guarantee 7: Shared Cryptographic Infrastructure - -## Components - -```text -CryptoBootstrap - -CryptoBuilder - -HashRegistry - -SignatureRegistry -``` - -Guarantee - -All cryptographic consumers use centrally constructed providers rather -than creating independent implementations. - -Purpose - -Ensure consistent hashing and signing behavior across the repository. - -Status - -✅ Verified - ---- - -# Guarantee 8: Algorithm Independence - -## Components - -```text -CryptoProvider - -Ed25519SignatureProvider - -Dilithium3SignatureProvider - -HybridSigner - -HybridVerifier -``` - -Guarantee - -Business logic is independent of the underlying signature algorithm. - -Changing algorithms affects the cryptographic output but does not alter -the execution flow or repository behavior. - -Status - -✅ Verified - ---- - -# Guarantee 9: Deterministic Verification Pipeline - -The verification process always follows the same sequence. - -```text -ExecutionTrustRecord - -↓ - -Canonical Serialization - -↓ - -Hash Verification - -↓ - -Signature Verification - -↓ - -Authorization Validation - -↓ - -Verification Result -``` - -Every verification evaluates the same evidence in the same order. - -Status - -✅ Verified - ---- - -# Guarantee 10: Deterministic Receipt Generation - -## Components - -```text -ReceiptService - -ReceiptCrypto -``` - -Guarantee - -Execution receipts are derived from verified execution evidence rather -than reconstructed execution state. - -Purpose - -Ensure receipts consistently represent the outcome of the verified -execution. - -Status - -✅ Verified - ---- - -# Determinism Boundaries - -The implementation enforces deterministic behavior within the -repository's control while relying on cryptographic primitives for -security. - -```text -Business Input - -↓ - -Canonical Serialization - -↓ - -Hash Generation - -↓ - -Policy Evaluation - -↓ - -Authorization Construction - -↓ - -Verification - -↓ - -Receipt Generation -``` - -These stages are deterministic for identical inputs. - -**Implementation Note** - -The audited repository also includes `Dilithium3SignatureProvider`, -whose source code explicitly documents that **ML-DSA-65 signatures are -randomized**. Signing the same canonical artifact twice produces -different valid signatures. Therefore, determinism applies to the -artifact, hash, authorization content, and verification outcome, not to -the raw signature bytes produced by randomized signature algorithms. - -Status - -✅ Verified - ---- - -# Audit Notes - -The Parmana implementation achieves reproducibility by combining -canonical serialization, shared cryptographic infrastructure, immutable -execution evidence, and deterministic processing of business artifacts. - -The audit confirms that determinism is enforced at the data, -authorization, verification, and evidence layers. Where cryptographic -algorithms intentionally introduce randomness (such as ML-DSA-65), the -repository preserves deterministic verification by operating over -canonical inputs and validating signatures against the corresponding -public keys rather than requiring identical signature values. - -Status - -✅ Verified - -# Stage 44: Repository Cross-Reference Matrix - -## Purpose - -Provide a consolidated index of the major implementation components -audited throughout the Parmana repository. - -This matrix summarizes each component's primary responsibility, its key -dependencies, its principal consumers, and the audit stage in which it -was examined. It serves as the master reference for navigating the -implementation. - -Status - -✅ Verified - ---- - -# Runtime Layer - -| Component | Primary Responsibility | Primary Dependencies | Primary Consumers | Audit Stage | -| -------------------------- | ----------------------------------------- | -------------------- | ----------------- | ----------: | -| BusinessTransactionService | Receive and prepare business transactions | Mapper layer | Runtime | Earlier | -| BusinessTransactionMapper | Convert business input into runtime model | Domain models | Runtime | Earlier | -| ExecutionTrustApplication | Entry point for execution trust | Runtime | RuntimeEngine | Earlier | -| Runtime | Execute runtime orchestration | RuntimeEngine | Application | Earlier | -| RuntimeEngine | Coordinate execution pipeline | RuntimePipeline | Runtime | Earlier | -| RuntimePipeline | Execute runtime stages | Policy engine | RuntimeEngine | Earlier | - -Status - -✅ Verified - ---- - -# Policy Layer - -| Component | Primary Responsibility | Primary Dependencies | Primary Consumers | Audit Stage | -| ----------------- | ------------------------- | -------------------- | ----------------- | ----------: | -| Policy Evaluation | Evaluate execution policy | Rules | RuntimePipeline | Earlier | -| Decision | Produce execution outcome | Policy Evaluation | Authorization | Earlier | - -Status - -✅ Verified - ---- - -# Authorization Layer - -| Component | Primary Responsibility | Primary Dependencies | Primary Consumers | Audit Stage | -| ----------------------- | ------------------------------------- | --------------------------------------- | -------------------------------------------------------------------- | ----------: | -| AuthorizationSigner | Create signed execution authorization | ArtifactSigner, ExecutableContentHasher | Execution | 28 | -| ExecutableContentHasher | Hash executable content | TrustRecordHasher | AuthorizationSigner | 29 | -| ArtifactSigner | Canonically sign artifacts | CryptoProvider | AuthorizationSigner, ReceiptCrypto, VerificationCrypto, HybridSigner | 25 | - -Status - -✅ Verified - ---- - -# Execution Layer - -| Component | Primary Responsibility | Primary Dependencies | Primary Consumers | Audit Stage | -| ----------------- | -------------------------- | -------------------- | ------------------ | ----------: | -| Execution Gateway | Control outbound execution | ExecutionService | Enterprise Systems | Earlier | -| ExecutionService | Execute approved operation | Gateway | Runtime | Earlier | -| Execution System | External business platform | Outside repository | ExecutionService | External | - -Status - -✅ Verified - ---- - -# Trust Record Layer - -| Component | Primary Responsibility | Primary Dependencies | Primary Consumers | Audit Stage | -| --------------------------- | --------------------------------- | ----------------------------------- | -------------------------------------- | ----------: | -| BusinessTrustRecordBuilder | Build business execution evidence | Execution result | Trust Record | Earlier | -| ExecutionTrustRecordBuilder | Construct immutable trust record | BusinessTrustRecordBuilder | Verification | Earlier | -| TrustRecordHasher | Canonically hash trust artifacts | CanonicalSerializer, CryptoProvider | ReceiptHasher, ExecutableContentHasher | 26 | -| ReceiptHasher | Hash receipt artifacts | TrustRecordHasher | ReceiptCrypto | 27 | - -Status - -✅ Verified - ---- - -# Verification Layer - -| Component | Primary Responsibility | Primary Dependencies | Primary Consumers | Audit Stage | -| ------------------- | ----------------------------------- | --------------------------------- | ---------------------------------- | ----------: | -| VerificationService | Coordinate verification | VerificationCrypto | ReceiptService | Earlier | -| VerificationCrypto | Verify signatures and authorization | SignatureVerifier, ArtifactSigner | VerificationService | Earlier | -| SignatureVerifier | Verify digital signatures | CryptoProvider | VerificationCrypto, HybridVerifier | Earlier | - -Status - -✅ Verified - ---- - -# Receipt Layer - -| Component | Primary Responsibility | Primary Dependencies | Primary Consumers | Audit Stage | -| -------------- | -------------------------- | ----------------------------- | ------------------ | ----------: | -| ReceiptService | Produce execution receipts | ReceiptCrypto | External consumers | Earlier | -| ReceiptCrypto | Generate signed receipts | ArtifactSigner, ReceiptHasher | ReceiptService | Earlier | - -Status - -✅ Verified - ---- - -# Cryptographic Infrastructure - -| Component | Primary Responsibility | Primary Dependencies | Primary Consumers | Audit Stage | -| --------------------------- | --------------------------------------- | ------------------------------- | ------------------- | ----------: | -| CryptoBootstrap | Register and construct crypto providers | CryptoBuilder, Registries | Application startup | 30 | -| CryptoBuilder | Assemble CryptoProvider | HashRegistry, SignatureRegistry | CryptoBootstrap | 34 | -| HashRegistry | Register hash providers | HashProvider | CryptoBuilder | 32 | -| SignatureRegistry | Register signature providers | SignatureProvider | CryptoBuilder | 33 | -| SHA256HashProvider | SHA-256 hashing | Node.js crypto | CryptoProvider | 35 | -| Ed25519SignatureProvider | Ed25519 signatures | Node.js crypto | CryptoProvider | 36 | -| Dilithium3SignatureProvider | ML-DSA-65 signatures | Node.js crypto | CryptoProvider | 37 | -| FileKeyProvider | Load cryptographic keys | Filesystem | Signature providers | 31 | - -Status - -✅ Verified - ---- - -# Hybrid Cryptography - -| Component | Primary Responsibility | Primary Dependencies | Primary Consumers | Audit Stage | -| -------------- | ----------------------------------------- | -------------------- | ------------------ | ----------: | -| HybridSigner | Produce multi-algorithm signature bundles | ArtifactSigner | Hybrid deployments | 38 | -| HybridVerifier | Verify multi-algorithm signature bundles | SignatureVerifier | Hybrid deployments | 39 | - -Status - -✅ Verified - ---- - -# Repository Dependency Flow - -```text -Business Layer - -↓ - -Runtime Layer - -↓ - -Policy Layer - -↓ - -Authorization Layer - -↓ - -Execution Layer - -↓ - -Trust Record Layer - -↓ - -Verification Layer - -↓ - -Receipt Layer - -↓ - -Audit Evidence -``` - -Status - -✅ Verified - ---- - -# Cryptographic Dependency Flow - -```text -CryptoBootstrap - -↓ - -CryptoBuilder - -↓ - -CryptoProvider - -↓ - -HashProvider -SignatureProvider - -↓ - -ArtifactSigner - -↓ - -AuthorizationSigner -VerificationCrypto -ReceiptCrypto -HybridSigner - -↓ - -HybridVerifier -``` - -Status - -✅ Verified - ---- - -# Repository Architecture Summary - -The audited implementation is organized into six functional domains. - -```text -Runtime - -↓ - -Authorization - -↓ - -Execution - -↓ - -Trust Evidence - -↓ - -Verification - -↓ - -Receipt Generation -``` - -Cross-cutting cryptographic services are isolated within the crypto -subsystem and reused consistently throughout the repository. - -This separation minimizes duplicated logic while ensuring that -authorization, verification, and receipt generation all rely on the -same canonical serialization, hashing, and signing infrastructure. - -Status - -✅ Verified - ---- - -# Audit Notes - -The Repository Cross-Reference Matrix consolidates the complete -implementation audit into a single navigational reference. - -It identifies: - -- each major implementation component, -- its primary responsibility, -- its principal dependencies, -- its primary consumers, and -- the audit stage where its implementation was verified. - -This matrix provides the linkage between detailed component audits and -the overall repository architecture, making it easier to trace how -individual classes contribute to the end-to-end execution authorization -pipeline. - -Status - -✅ Verified - -# Stage 45: Architecture Summary - -## Purpose - -Provide a consolidated architectural assessment of the Parmana -repository based exclusively on the audited implementation. - -This chapter summarizes the repository's structure, design principles, -major subsystems, and implementation characteristics without -introducing new technical details. - -Status - -✅ Verified - ---- - -# Architectural Overview - -The Parmana repository implements an execution authorization platform that -controls, authorizes, verifies, and records business operations through -deterministic processing and cryptographically verifiable evidence. - -Rather than combining business logic, cryptography, and auditing into a -single subsystem, the implementation separates these responsibilities -into distinct layers with clearly defined interfaces. - -At a high level, the repository follows the execution lifecycle: - -```text -Business Request - -↓ - -Runtime Evaluation - -↓ - -Policy Decision - -↓ - -Execution Authorization - -↓ - -Business Execution - -↓ - -Execution Trust Record - -↓ - -Verification - -↓ - -Execution Receipt -``` - -Each stage is implemented by dedicated components that communicate -through well-defined artifacts rather than shared mutable state. - -Status - -✅ Verified - ---- - -# Architectural Layers - -The repository is organized into six primary functional layers. - -## Runtime - -Responsible for evaluating business requests and coordinating execution. - -Primary responsibilities include: - -- runtime orchestration -- policy evaluation -- decision production - ---- - -## Authorization - -Responsible for converting runtime decisions into signed execution -authorizations. - -Primary responsibilities include: - -- executable content hashing -- authorization construction -- digital signing - ---- - -## Execution - -Responsible for performing approved business operations. - -Primary responsibilities include: - -- execution coordination -- gateway-controlled execution -- interaction with enterprise systems - ---- - -## Trust Evidence - -Responsible for constructing immutable execution evidence. - -Primary responsibilities include: - -- execution trust record generation -- deterministic hashing -- evidence preservation - ---- - -## Verification - -Responsible for independently validating execution evidence. - -Primary responsibilities include: - -- signature verification -- authorization validation -- integrity verification - ---- - -## Receipt Generation - -Responsible for producing cryptographically verifiable execution -receipts after successful verification. - -Status - -✅ Verified - ---- - -# Cross-Cutting Infrastructure - -Several shared infrastructure components support every architectural -layer. - -These include: - -```text -Canonical Serialization - -↓ - -Cryptographic Providers - -↓ - -Hash Providers - -↓ - -Signature Providers - -↓ - -Artifact Signing - -↓ - -Verification -``` - -Centralizing these capabilities eliminates duplicated cryptographic -logic while ensuring consistent behavior across authorization, -verification, and receipt generation. - -Status - -✅ Verified - ---- - -# Cryptographic Architecture - -The repository abstracts cryptographic functionality behind provider -interfaces. - -```text -CryptoBootstrap - -↓ - -CryptoBuilder - -↓ - -CryptoProvider - -↓ - -Hash Provider - -Signature Provider -``` - -Concrete implementations are registered during bootstrap and consumed -through shared interfaces. - -The audited implementation includes support for: - -- SHA-256 hashing -- Ed25519 digital signatures -- ML-DSA-65 (Dilithium3) digital signatures -- Hybrid multi-algorithm signing and verification - -This architecture enables algorithm replacement or extension without -modifying higher-level business logic. - -Status - -✅ Verified - ---- - -# Evidence Model - -The repository consistently treats execution evidence as immutable. - -Key evidence artifacts include: - -```text -SignedExecutionAuthorization - -↓ - -ExecutionTrustRecord - -↓ - -Verification Result - -↓ - -ExecutionReceipt -``` - -Each artifact is constructed, signed or verified, and then consumed by -the next stage of the execution pipeline. - -This chained evidence model supports traceability across the execution -lifecycle. - -Status - -✅ Verified - ---- - -# Design Principles Observed - -The audited implementation consistently applies the following design -principles: - -- Separation of concerns. -- Layered architecture. -- Deterministic processing. -- Canonical data representation. -- Shared cryptographic infrastructure. -- Interface-driven component design. -- Immutable execution evidence. -- Independent verification. -- Provider-based cryptographic abstraction. -- Fail-closed validation. - -These principles appear consistently across runtime, authorization, -verification, and cryptographic components. - -Status - -✅ Verified - ---- - -# Security Characteristics - -The implementation establishes security through multiple complementary -mechanisms rather than a single control. - -These mechanisms include: - -```text -Canonical Serialization - -↓ - -Deterministic Hashing - -↓ - -Digital Signatures - -↓ - -Authorization Binding - -↓ - -Independent Verification - -↓ - -Receipt Generation -``` - -Each mechanism protects a different aspect of execution integrity, -providing defense in depth throughout the execution lifecycle. - -Status - -✅ Verified - ---- - -# Implementation Assessment - -Based on the audited implementation, the repository exhibits several -notable architectural characteristics: - -### Strong Layering - -Business logic, execution control, cryptographic operations, and audit -evidence are separated into dedicated subsystems with clear -responsibilities. - -### Reusable Infrastructure - -Core cryptographic services such as serialization, hashing, signing, -and verification are reused across multiple workflows instead of being -reimplemented. - -### Extensibility - -Provider registries and builder patterns allow additional -cryptographic algorithms or implementations to be introduced with -minimal changes to business logic. - -### Auditability - -Execution artifacts are transformed into verifiable evidence through -deterministic hashing, digital signatures, and independent -verification, supporting reproducible audit trails. - -### Maintainability - -Shared abstractions, explicit interfaces, and layered dependencies -reduce coupling between functional domains, simplifying future -maintenance and evolution. - -Status - -✅ Verified - ---- - -# Overall Architectural Assessment - -The audited repository implements a modular execution authorization -architecture centered on deterministic processing and cryptographically -verifiable execution evidence. - -Its primary architectural strengths are: - -- Clear separation between business execution and cryptographic - services. -- Consistent use of shared infrastructure for hashing, signing, and - verification. -- Strong evidence lifecycle from authorization through receipt - generation. -- Support for algorithm agility through provider abstractions. -- Layered trust model with independent verification. - -The implementation demonstrates a coherent design in which execution -authorization, trust evidence, verification, and receipt generation -form a continuous chain of verifiable artifacts. - -Status - -✅ Verified - ---- - -# Audit Conclusion - -This implementation audit examined the Parmana repository component by -component, validating responsibilities, dependencies, execution flows, -and architectural relationships directly from the source code. - -The audit covered: - -- Runtime orchestration. -- Policy evaluation. -- Execution authorization. -- Business execution. -- Trust record construction. -- Verification. -- Receipt generation. -- Cryptographic infrastructure. -- Hybrid cryptography. -- Security invariants. -- Determinism guarantees. -- Repository architecture. - -The resulting architecture is structured around a deterministic -execution pipeline in which business decisions are transformed into -cryptographically protected evidence that can be independently verified -after execution. - -Status - -✅ Verified diff --git a/docs/architecture/KEY-MANAGEMENT.md b/docs/architecture/KEY-MANAGEMENT.md deleted file mode 100644 index 13f66476..00000000 --- a/docs/architecture/KEY-MANAGEMENT.md +++ /dev/null @@ -1,311 +0,0 @@ -\# Key Management Architecture - -\## Purpose - -The Key Management subsystem is responsible for the complete lifecycle of cryptographic keys used by Parmana. - -It provides a single, consistent API for generating, loading, storing, rotating, and managing signing keys while keeping cryptographic operations independent of key storage. - -\--- - -\# Design Principles - -\- Algorithm agnostic - -\- Provider independent - -\- Secure by default - -\- Extensible - -\- Testable - -\- No business logic - -\- No policy evaluation - -\- No signing logic - -Key Management only manages keys. - -\--- - -\# Responsibilities - -The Key Management subsystem is responsible for: - -\- generating key pairs - -\- loading key pairs - -\- storing key pairs - -\- exporting keys - -\- importing keys - -\- rotating keys - -\- listing keys - -\- deleting keys - -\- retrieving key metadata - -\--- - -\# Non-Responsibilities - -Key Management does NOT: - -\- sign data - -\- verify signatures - -\- hash data - -\- evaluate policies - -\- authorize execution - -These responsibilities belong elsewhere. - -\--- - -\# Public API - -\## generate() - -Creates a new key pair. - -Inputs - -\- algorithm - -\- keyId - -Output - -\- generated key pair - -\--- - -\## load() - -Loads an existing key pair. - -Inputs - -\- algorithm - -\- keyId - -Output - -\- key pair - -\--- - -\## save() - -Persists a key pair. - -Inputs - -\- key pair - -\- destination - -Output - -\- none - -\--- - -\## export() - -Exports keys to PEM. - -Inputs - -\- key pair - -Output - -\- PEM - -\--- - -\## import() - -Imports PEM. - -Inputs - -\- PEM - -Output - -\- key pair - -\--- - -\## rotate() - -\*\*Status note (2026-09-11, docs/VERIFICATION-GAPS.md gap 53):\*\* the unified `generate/load/save/export/import/rotate/list/delete` API this whole "Public API" section describes has never been implemented as such. The real `FileKeyProvider` (`packages/crypto/src/providers/key/FileKeyProvider.ts`) implements only `hasKey`, `getMetadata`, `getPrivateKey`, `getPublicKey`, and (added 2026-09-11) `listKeys()` — a flat enumeration of `*.public.pem` files, not the richer `list()` this section describes. Key generation is a separate script (`scripts/generate-keypair.ts`), not a `KeyProvider` method. There is no `save()`, `export()`, `import()`, or `delete()` anywhere in this codebase. Real key-directory layout is `.private.pem` / `.public.pem` (no algorithm prefix), not the `ed25519.default.private.pem` shape the "Storage Layout" section below describes. - -Real rotation, as it actually exists today: `scripts/rotate-verification-key.ts` generates a new keyId's key pair (never touching or deleting an existing one), and an operator points `PARMANA_VERIFICATION_KEY_ID` (or `PARMANA_VERIFICATION_SECONDARY_KEY_ID` for the hybrid-mode secondary) at it for future signing — mirroring `PARMANA_GATEWAY_KEY_ID`'s existing precedent for the Gateway's own key. Every already-issued Trust Record, Refusal Record, or Audit Event keeps verifying unaffected, since verification always resolves the public key by the record's own stored `keyId`, never a hardcoded "current" one. - -Creates a replacement key pair while preserving historical keys. - -Inputs - -\- algorithm - -\- keyId - -Output - -\- new key metadata - -\--- - -\## list() - -Lists available keys. - -Output - -\- key metadata - -\--- - -\## delete() - -Deletes a key pair. - -Inputs - -\- algorithm - -\- keyId - -Output - -\- none - -\--- - -\# Storage Layout - -``` - -keys/ - - - -ed25519.default.private.pem - -ed25519.default.public.pem - - - -dilithium3.default.private.pem - -dilithium3.default.public.pem - -``` - -Algorithm names are part of the filename to avoid ambiguity. - -\--- - -\# Future Providers - -The design allows additional providers without changing callers. - -Examples - -\- Local Files - -\- AWS KMS - -\- Azure Key Vault - -\- Google Cloud KMS - -\- HashiCorp Vault - -\- HSM - -\--- - -\# Security Requirements - -\- Private keys must never be logged. - -\- Private keys must never be serialized into audit records. - -\- Key material should remain outside business objects. - -\- Historical keys should remain available for verification. - -\- Active keys must be identifiable. - -\- Key rotation must not invalidate historical signatures. - -\--- - -\# Relationship to Other Components - -``` - -Key Management - - │ - - ▼ - -KeyProvider - - │ - - ▼ - -ArtifactSigner - - │ - - ▼ - -SignatureVerifier - -``` - -Key Management owns keys. - -Signing components consume keys. - -\--- - -\# Future Enhancements - -\- Hardware Security Modules - -\- Cloud Key Management Services - -\- Automatic rotation - -\- Key versioning - -\- Certificate chains - -\- Multi-tenant key stores - -\--- - -\# Summary - -The Key Management subsystem provides a single abstraction for the lifecycle of cryptographic keys used by Parmana. - -It is responsible for managing keys, while signing, verification, hashing, and execution authorization remain separate concerns. diff --git a/docs/architecture/README.md b/docs/architecture/README.md deleted file mode 100644 index 91547047..00000000 --- a/docs/architecture/README.md +++ /dev/null @@ -1,75 +0,0 @@ -\# Architecture Decision Records (ADRs) - -This directory contains the Architecture Decision Records (ADRs) for the Parmana Execution Trust Platform. - -An ADR captures a significant architectural decision, the context in which it was made, the alternatives considered, the decision itself, and its consequences. - -Unlike implementation documentation, ADRs explain \*\*why\*\* the system is designed the way it is. - -\--- - -\# ADR Index - -| ADR | Title | Status | - -|-----|-------|--------| - -| ADR-0001 | Deterministic Execution | Accepted | - -| ADR-0002 | Immutable Execution Trust Records | Accepted | - -| ADR-0003 | Canonical Representation | Accepted | - -| ADR-0004 | Asymmetric Cryptography | Accepted | - -| ADR-0005 | Independent Verification | Accepted | - -| ADR-0006 | Authorization Binding | Accepted | - -| ADR-0007 | Append-Only Lifecycle Artifacts | Accepted | - -| ADR-0008 | Layered Package Architecture | Accepted | - -| ADR-0009 | API-First Design | Accepted | - -| ADR-0010 | Replay Without Re-Execution | Accepted | - -\--- - -\# ADR Template - -Every ADR should include: - -1\. Title - -2\. Status - -3\. Context - -4\. Decision - -5\. Alternatives Considered - -6\. Consequences - -\--- - -\# ADR Lifecycle - -Typical statuses include: - -\- Proposed - -\- Accepted - -\- Superseded - -\- Deprecated - -When a decision changes, create a new ADR rather than editing historical records. - -\--- - -\# Purpose - -ADRs preserve the architectural history of Parmana and help future contributors understand the rationale behind important technical decisions. diff --git a/docs/architecture/crypto-provider.md b/docs/architecture/crypto-provider.md deleted file mode 100644 index 7ac01f7a..00000000 --- a/docs/architecture/crypto-provider.md +++ /dev/null @@ -1,253 +0,0 @@ -\# Crypto Provider Architecture - -\## Overview - -Parmana separates cryptographic operations from business logic through a provider architecture. - -The runtime never depends on a specific cryptographic algorithm. Instead, algorithms are selected through configuration and resolved at startup. - -This architecture enables Parmana to support multiple hash functions, signature algorithms, and key management implementations without changing application code. - -\--- - -\# Design Goals - -The crypto subsystem is designed around the following principles: - -\* Algorithm independence - -\* Configuration-driven selection - -\* Pluggable implementations - -\* Immutable provider instances - -\* Deterministic cryptographic operations - -\--- - -\# Architecture - -```text - - CryptoBootstrap - - │ - - ┌──────────────┴──────────────┐ - - ▼ ▼ - - HashRegistry SignatureRegistry - - │ │ - - HashProvider SignatureProvider - - │ │ - - SHA-256 / SHA3 Ed25519 / ML-DSA-65 - -``` - -The application interacts only with the `CryptoProvider` abstraction. - -\--- - -\# CryptoBootstrap - -`CryptoBootstrap` is the composition root of the cryptography subsystem. - -Responsibilities: - -\* Load immutable configuration - -\* Register built-in providers - -\* Resolve configured providers - -\* Build the `CryptoProvider` - -\* Cache the configured provider for the process lifetime - -The bootstrap is the only location that directly references concrete cryptographic implementations. - -\--- - -\# CryptoBuilder - -`CryptoBuilder` constructs the immutable `CryptoProvider`. - -Responsibilities: - -\* Accept configured hash provider - -\* Accept configured signature provider - -\* Produce immutable provider instances - -Business code never constructs providers directly. - -\--- - -\# Hash Registry - -The `HashRegistry` maintains available hash implementations. - -Current providers: - -| Algorithm | Provider | - -| --------- | ------------------ | - -| SHA-256 | SHA256HashProvider | - -Future providers may include: - -\* SHA3-512 - -\* BLAKE3 - -Adding new providers does not require changes to business logic. - -\--- - -\# Signature Registry - -The `SignatureRegistry` maintains available signature implementations. - -Current providers: - -| Algorithm | Provider | - -| --------- | --------------------------- | - -| Ed25519 | Ed25519SignatureProvider | - -| ML-DSA-65 | Dilithium3SignatureProvider | - -The configured provider is selected using: - -```dotenv - -SIGNATURE\_PROVIDER=ed25519 - -``` - -or - -```dotenv - -SIGNATURE\_PROVIDER=dilithium3 - -``` - -\--- - -\# CryptoProvider - -`CryptoProvider` exposes the active cryptographic implementation to the rest of Parmana. - -Responsibilities: - -\* Hash canonical data - -\* Sign canonical data - -\* Verify signatures - -Higher-level services remain independent of concrete algorithms. - -\--- - -\# Runtime Flow - -```text - -Application - - │ - - ▼ - -CryptoBootstrap - - │ - - ▼ - -Load Configuration - - │ - - ▼ - -Register Providers - - │ - - ▼ - -Resolve Configured Algorithms - - │ - - ▼ - -Build CryptoProvider - - │ - - ▼ - -ReceiptCrypto / VerificationCrypto - -``` - -The runtime never directly instantiates individual algorithms after startup. - -\--- - -\# Extending Parmana - -To add a new signature algorithm: - -1\. Implement `SignatureProvider` - -2\. Register it with `SignatureRegistry` - -3\. Add the algorithm identifier - -4\. Extend configuration validation - -No changes are required to: - -\* Receipt generation - -\* Verification - -\* Runtime - -\* Replay - -\* Storage - -This keeps cryptographic evolution isolated from business logic. - -\--- - -\# Benefits - -The provider architecture offers several advantages: - -\* Configuration-driven cryptography - -\* Easy migration to new algorithms - -\* Support for post-quantum cryptography - -\* Minimal impact on higher-level services - -\* Consistent cryptographic interfaces across the platform - -This design ensures that Parmana can evolve its cryptographic capabilities while preserving the stability of its execution trust infrastructure. diff --git a/docs/architecture/documentation-audit.md b/docs/architecture/documentation-audit.md deleted file mode 100644 index 7b54c7a3..00000000 --- a/docs/architecture/documentation-audit.md +++ /dev/null @@ -1,103 +0,0 @@ -# Documentation Audit — Phase 1G - -Verification report for the Phase 1G documentation set: `system-architecture.md`, `execution-walkthrough.md`, `../developer/extending-parmana.md`, plus their two Phase 1E/1F foundations (`execution-pipeline-report.md`, `repository-invariants.md`). Produced by direct comparison against source — every claim below was checked against the file it describes, not re-derived from the docs themselves. - -## Scope - -This is a **lightweight** audit, matching the lightweight automated check it's paired with (`tests/architecture/documentation-references.test.ts`): confirm the docs are accurate and internally consistent, not a full prose/style review. Four things were checked: - -1. Every backtick-quoted file path the docs cite as evidence resolves to a real file. -2. A sample of concrete, falsifiable claims (class names, method line numbers, package dependency edges) matches source. -3. The five documents cross-reference each other correctly and don't contradict one another. -4. No production source file was modified in the course of this work. - -## 1. Automated reference check - -`tests/architecture/documentation-references.test.ts` scans all five documents for backtick-quoted paths matching real repo-path shapes (`packages/*/src|tests/**/*.ts(x)`, `tests/architecture/**/*.ts`, `docs/**/*.md`, `policies/`) and asserts each one exists. - -**First run surfaced 6 false-positive failures**, all in paths that are intentionally hypothetical, not evidence: - -| Path | Where | Why it doesn't exist | -| ------------------------------------------------------------------------------------ | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | -| `packages/connector-stripe/src/StripeCapabilities.ts` | `extending-parmana.md`, "Adding a new vendor" walkthrough | Stripe is a worked example of a vendor _not yet built_ — the walkthrough shows what you'd create | -| `packages/execution-gateway/src/connector-execution/GatewayStripeAdapter.ts` | same | same | -| `packages/execution-gateway/src/connector-execution/createGatewayStripeConnector.ts` | same | same | -| `packages/api/src/bootstrap/createStripeConnector.ts` | same | same | -| `packages/connector-stripe/src/StripeConnector.ts` | `repository-invariants.md`, Invariant 2 "Regression example" | Describes a file that must **never** be created — it's the counterexample the invariant test guards against | -| `packages/api/src/routes/admin-replay.ts` | `repository-invariants.md`, Invariant 5 "Regression example" | Same — a hypothetical bad route used to illustrate what the test catches | - -These aren't documentation errors — creating any of these files to satisfy the test would be wrong (in two cases, it would mean adding the exact bypass the invariant exists to prevent). The test's own docstring scopes it to "evidence citations," and these six are illustrative counterfactuals, not citations. Fixed by adding a small, explicit, documented allowlist (`HYPOTHETICAL_EXAMPLE_PATHS`) to the test rather than weakening the pattern generally or fabricating files — any new hypothetical path added to a doc in the future will still fail loudly until someone deliberately allowlists it. - -**After the fix: 43/43 pass.** - -## 2. Manual spot-checks against source - -### Method line numbers (`execution-walkthrough.md`) - -All 10 `file.ts:N` citations checked against actual method declarations: - -| Claim | Verified | -| --------------------------------------------------------------------------------------- | -------- | -| `ExecutionTrustApplication.execute()` — `ExecutionTrustApplication.ts:64` | ✅ | -| `Runtime.execute()` — `Runtime.ts:31` | ✅ | -| `RuntimeEngine.execute()` — `RuntimeEngine.ts:125` | ✅ | -| `RuntimePipeline.execute()` — `RuntimePipeline.ts:30` | ✅ | -| `ExecutionComponent.execute()` — `ExecutionComponent.ts:44` | ✅ | -| `ExecutionGateway.execute()` — `ExecutionGateway.ts:214` | ✅ | -| `ExecutionControlService.execute()` — `ExecutionControlService.ts:44` | ✅ | -| `SessionCredentialSecureConnector.execute()` — `SessionCredentialSecureConnector.ts:70` | ✅ | -| `SdkConnectorExecutor.execute()` — `SdkConnectorExecutor.ts:41` | ✅ | -| `BusinessTrustPipeline.execute()` — `BusinessTrustPipeline.ts:31` | ✅ | - -### Class/function existence (`system-architecture.md`, `execution-walkthrough.md`, `extending-parmana.md`) - -Checked 17 additional named classes not covered by the line-number pass above (`PolicyRouter`, `PolicyEngine`, `SignalIntentBinder`, `ExecutionGate`, `RefusalRecordBuilder`, `VerificationService`, `ReceiptService`, `BusinessTransactionService`, `RuntimeAuthorizationSigner`, `GatewayAttestationSigner`, `RuntimeBuilder`, `RuntimeFactory`, `TrustChainValidationComponent`, `BusinessTrustRecordBuilder`, `GatewayConnectorRegistry`, `InMemorySessionCredentialVault`, `CredentialVaultAdapter`) plus the four gateway adapters and the public barrel's factory exports (`createExecutionSystem.ts`, `RazorpaySettlementProcessor.ts`). All exist exactly as named. Note `ExecutionGate` (in `runtime/src/ExecutionGate.ts`) is a distinct class from `ExecutionGateway` — both are real, docs use each correctly in context. - -`packages/execution-gateway/src/index.ts` (the public barrel) was read directly: it exports only `ExecutionGateway` and the documented extension-point types, plus the three named factories — never a concrete adapter/registry/executor class. Matches Invariant 7's claim. - -### Package dependency graph (`system-architecture.md` §8) - -Checked every edge in the graph against each package's `package.json` `dependencies` and, where a dependency looked type-only, its `tsconfig.json` project `references`. - -**Found and fixed one real inaccuracy:** the graph stated `shared, crypto → (everything else depends on these; they depend on nothing internal)`. `@parmana/crypto` in fact depends on `@parmana/shared` — declared in `package.json`, referenced in `tsconfig.json`, and imported in 18 of `crypto`'s source files (key providers, signers, hashers all use shared domain types). Corrected to: - -``` -shared → (everything else depends on this; depends on nothing internal) -crypto → shared -``` - -All other edges in the graph (`policy`, `envelope-verifier`, `execution-system`, `receipt`, `runtime`, `execution-control`, `connector-sdk`, `connector-hubspot`, `execution-gateway`, `api`) were verified correct, including `receipt`'s `execution-control (type-only)` edge — confirmed via `tsconfig.json` project reference plus the actual type-only import in `ExecutionReceiptBuilder.ts` (no runtime `package.json` dependency, matching the "type-only" label). - -**Minor observation, not fixed (below the bar for this pass):** the graph omits `storage` and `replay` from its package list entirely, and `runtime`'s row omits its (harmless, Invariant-4-irrelevant) reference to `storage`. The graph reads as a summary of the packages central to the execution pipeline, not a claim of exhaustiveness, and prose elsewhere in the document doesn't assert completeness — so this wasn't treated as an error. Worth tightening in a future pass if the graph's scope is meant to be exhaustive. - -## 3. Cross-document consistency - -- `system-architecture.md` → `execution-pipeline-report.md`, `repository-invariants.md`, `execution-walkthrough.md`, `../developer/extending-parmana.md`: all four links resolve, and each target's own framing (Phase 1E baseline, Phase 1F canonical invariants) matches how `system-architecture.md` describes them. -- `execution-walkthrough.md` ↔ `system-architecture.md` §3: the 14-stage walkthrough and the pipeline diagram describe the same call chain in the same order; no contradictions. -- `extending-parmana.md` ↔ `repository-invariants.md`: every "must never be modified directly" row cites an invariant number that exists and matches that invariant's actual enforcement description. -- `repository-invariants.md`'s "How to run these checks locally" commands (`npm test -- --maxWorkers=2`, the two `npx vitest run` invocations) were run as part of this audit — pass. - -No internal contradictions found across the five documents. - -## 4. Build and test verification - -``` -npx tsc -b → clean, no errors -npm test -- --maxWorkers=2 → 135 test files passed, 16 skipped; 931 tests passed, 40 skipped, 0 failed -``` - -The only failures seen at any point were the 6 documentation-reference false positives in §1, fixed as described. No other test — including the Phase 1E/1F architectural invariant tests this audit depends on — regressed. - -## 5. Files touched by this audit - -| File | Change | Why | -| ----------------------------------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -| `tests/architecture/documentation-references.test.ts` | Added `HYPOTHETICAL_EXAMPLE_PATHS` allowlist | Exclude 6 intentionally-hypothetical example/counterexample paths from the evidence-citation check (§1) | -| `docs/architecture/system-architecture.md` | Corrected §8 dependency graph's `crypto` edge | `crypto` depends on `shared`; the doc claimed it didn't (§2) | -| `docs/architecture/documentation-audit.md` | New (this file) | Phase 1G deliverable | - -No production source file (anything under `packages/*/src`) was modified. Both changes above are documentation and test-infrastructure only. - -## Conclusion - -Phase 1G's documentation set is accurate and internally consistent as of this audit: every evidence citation resolves, every sampled method/line/class claim matches source exactly, and the one real drift found (the `crypto`→`shared` dependency edge) has been corrected. `tsc -b` and the full test suite are clean. diff --git a/docs/architecture/execution-control.md b/docs/architecture/execution-control.md deleted file mode 100644 index e6ae6983..00000000 --- a/docs/architecture/execution-control.md +++ /dev/null @@ -1,59 +0,0 @@ -# Execution control - -Parmana's authorization proves which content was approved. The execution-control -layer adds the release boundary that makes approved execution flow through a -Parmana-controlled connector. It is additive: Runtime, `ExecutableContent`, the -authorization envelope, cryptography, and `EnvelopeVerifier` remain unchanged. - -## Release path - -The Runtime produces a decision, executable content, and a content-bound signed -authorization as before. The Execution Gateway verifies the envelope, content -hash, expiry, and replay nonce. Only after those checks pass does it call -`ExecutionControlService`. - -The service authenticates the Gateway, resolves a connector, creates a short-lived -session, freezes the request, and invokes the connector. The connector independently -checks its trusted identity, Gateway identity, verification state, capability, and -the session before obtaining a credential and calling its enterprise executor. - -## Credential isolation - -Credentials are held by `CredentialVault` and requested inside `SecureConnector` -only after policy and session checks succeed. They are passed only to the connector's -`ConnectorExecutor`; they are absent from Runtime, Gateway requests, executable -content, audit events, and execution results. The included vault is an in-memory -demonstration, not a production secret store. - -AI, agents, middleware, and Runtime therefore have no credential-bearing API in -this design. A direct connector call cannot invent a valid session and never reaches -credential acquisition. - -## Session and tamper binding - -Each `GatewaySession` is short-lived and single-use. Its private store record binds -the connector, authorization ID, complete signed authorization, and executable -content using deterministic canonical hashes. The connector atomically validates -and consumes that record. Reuse, expiry, connector substitution, modified content, -or modified authorization is rejected before credential access. - -Gateway nonce protection and connector session protection cover different replay -boundaries: the Gateway rejects reuse of an authorization, while the connector -rejects reuse of an execution release. - -## Trust and scope - -Connectors trust the authenticated Gateway and session authority, not caller claims -from an AI. Connector identities are also allow-listed by the authenticator. The -in-memory authenticator models this boundary with an unforgeable object reference; -production deployments must replace it with workload identity and network controls. - -This package enforces the path for enterprise systems whose credentials and APIs are -exclusively placed behind these connectors. It does not claim to prevent execution -through separately issued credentials or an independently reachable enterprise API. -OAuth, cloud IAM, managed identity, HSMs, and network-policy enforcement are outside -this implementation. - -Connector capabilities and executors are independent of Runtime, so SAP, -Salesforce, Stripe, Kubernetes, databases, internal APIs, robots, and IoT systems -can be integrated without changing Runtime or authorization formats. diff --git a/docs/architecture/execution-pipeline-report.md b/docs/architecture/execution-pipeline-report.md deleted file mode 100644 index 5aa47f91..00000000 --- a/docs/architecture/execution-pipeline-report.md +++ /dev/null @@ -1,140 +0,0 @@ -# Execution Pipeline Report — Phase 1E Architectural Baseline - -Status: canonical baseline as of Phase 1E. Supersedes any prior informal description of execution ownership. Produced by static repository search plus automated regression tests (`tests/architecture/execution-boundary.test.ts`), not by inspection alone — every claim below is backed by a passing or explicitly-approved-exception test. - -## 1. Execution entry points - -Repository-wide search for `execute(`, `dispatch(`, `perform(`, `invoke(`, `run(` method definitions, and for `new Gateway*`, `new Connector*`, `new Http*`, `new Razorpay*`, `new HubSpot*` construction sites, classified: - -| Symbol / file | Classification | -| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ExecutionGateway.execute()` (`packages/execution-gateway/src/ExecutionGateway.ts`) | **Canonical production execution** — the sole production execution API | -| `ExecutionControlService.execute()` (`packages/execution-control/src/ExecutionControlService.ts`) | **Canonical production execution** — stage 2, resolves and dispatches to a `SecureConnector` | -| `SessionCredentialSecureConnector.execute()`, `SecureConnector.execute()` (`packages/execution-control/src`) | **Canonical production execution** — stage 3, session/credential-scoped dispatch | -| `SdkConnectorExecutor.execute()` (`packages/execution-gateway/src/connector-execution`) | **Canonical production execution** — stage 4, dispatches to the raw vendor `Connector` | -| `GatewayRazorpayAdapter.execute()`, `GatewayHubSpotAdapter.execute()`, `GatewayHttpAdapter.execute()` | **Canonical production execution** — stage 5, the vendor-facing HTTP call | -| `RuntimeEngine.execute()`, `Runtime.execute()`, `ExecutionTrustApplication.execute()`, `RuntimePipeline.execute()`, `BusinessTrustPipeline.execute()`, `RuntimeComponent` subclasses (`ExecutionComponent`, `ExecutionEvidenceComponent`, `ReceiptComponent`, `TrustChainValidationComponent`) | **Internal helper** — orchestration/pipeline stages that call into the canonical pipeline via an injected `ExecutionSystem`; none dispatch to a vendor directly | -| `ExecutionSystem.execute()` interface, `DefaultExecutionSystem`, `HttpExecutionSystem` (`packages/execution-system`) | **Internal helper (interface + alternate implementations)** — the seam `ExecutionGateway` implements; `DefaultExecutionSystem`/`HttpExecutionSystem` exist in the package but are not constructed anywhere in production bootstrap (see §3) | -| `createExecutionSystem()`, `createExecutionGateway()`, `createExecutionControl()`, `createConnectorRegistry()`, `createRazorpayConnector()`, `createHubSpotConnector()` (`packages/api/src/bootstrap`) | **Bootstrap** — compose the pipeline, never call `.execute(` themselves (enforced, see §6) | -| `executeRazorpayCapability()`, `executeHubSpotCapability()` (`connector-sdk`/`connector-hubspot`) | **Signal verification** — sign an authorization and call the _injected_ `ExecutionSystem.execute()` (i.e. `ExecutionGateway`); used by `RazorpaySignalStateVerifier`/`HubSpotSignalStateVerifier` to re-fetch vendor state before policy evaluation. Does not dispatch to a vendor directly | -| `RazorpaySettlementProcessor`'s `this.options.connector.execute(...)` (`packages/api/src/webhooks`) | **Worker — approved gateway-owned verification infrastructure** (see §8) — a read-only fetch-verify call confirming a webhook's claimed settlement state, bypassing the authorization chain because it authorizes nothing; it re-confirms state, it doesn't execute a business action | -| `MockConnector.execute()` (`connector-sdk`), `InMemorySecureConnector`/execution-control test fixtures, `RecordingConnector`/`RecordingHttpConnector` in test/example files | **Test-only / Example-tutorial** | -| `examples/tutorials/**/run.ts`, `examples/scenarios/**/run.ts`, `examples/04-verified-execution/run.ts` | **Example/tutorial** | -| `packages/execution-gateway/src/connector-runtime/*` (`DefaultSecureConnector.invoke()`, `InMemoryConnectorRegistry`, `CapabilityConnectorPolicy`, `DefaultExecutionChannel`, `MemoryExecutionAuditSink`) | **Legacy — confirmed dead in production** (see §4) | -| `ReplayExecutor.execute()` (`packages/replay`) | **Internal helper** — replay-testing tooling, not a production execution path | - -## 2. Complete execution graph - -Verified by direct source inspection, not inference: - -``` -HTTP request - → packages/api/src/routes/{execute.ts, transactions.ts} - → application.execute(...) [ExecutionTrustApplication.execute(), packages/runtime/src/ExecutionTrustApplication.ts:64] - → Runtime.execute(...) [packages/runtime/src/Runtime.ts:31] - → RuntimePipeline / BusinessTrustPipeline [RuntimeComponent stages, incl. ExecutionComponent] - → ExecutionComponent.execute() [packages/runtime/src/components/ExecutionComponent.ts:44] - → this.executionSystem.execute(request) [injected ExecutionSystem — see binding below] - → ExecutionGateway.execute() [packages/execution-gateway/src/ExecutionGateway.ts:214] - → verify() [signature, expiry, TTL, content hash, nonce] - → ExecutionControlService.execute() [packages/execution-control/src/ExecutionControlService.ts:44] - → registry.resolveCapability(action) → SecureConnector - → SecureConnector.execute() [SessionCredentialSecureConnector, packages/execution-control/src/SessionCredentialSecureConnector.ts:70] - → SdkConnectorExecutor.execute() [packages/execution-gateway/src/connector-execution/SdkConnectorExecutor.ts:41] - → connector.execute(request, context) [raw vendor Connector] - → GatewayRazorpayAdapter.execute() | GatewayHubSpotAdapter.execute() | GatewayHttpAdapter.execute() - → Business System (Razorpay / HubSpot / generic HTTP) -``` - -**ExecutionSystem binding** — the one place production decides what `ExecutionSystem` means, confirmed by direct read of `packages/api/src/bootstrap/createExecutionSystem.ts`: - -```ts -export function createExecutionSystem(): ExecutionSystem { - return createExecutionGateway(); -} -``` - -Its own doc comment: _"This is the single architectural entry point for execution-system composition."_ `createExecutionGateway()` constructs `ExecutionGateway` with the `executionControl` option (never the alternate `connector`/`ExecutionChannel` option that would reach the legacy `connector-runtime` subsystem — confirmed in `packages/api/src/bootstrap/createExecutionGateway.ts`). - -The same `executionSystem` instance is threaded into `createRazorpaySignalStateVerifier(executionSystem)` / `createHubSpotSignalStateVerifier(executionSystem)` (`packages/api/src/application.ts:50-51`), so pre-execution signal verification's `executeRazorpayCapability`/`executeHubSpotCapability` calls also flow through this exact same `ExecutionGateway` instance, not a separate path. - -## 3. Ownership map - -| Responsibility | Owner | Evidence | -| ------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| Execution dispatch API | `execution-gateway` (`ExecutionGateway.execute()`) | Sole production `ExecutionSystem` binding (§2) | -| Connector registration | `execution-gateway` (`GatewayConnectorRegistry`, internal) | `createConnectorRegistry()` calls `createGatewayConnectorRegistry()`, never constructs the class directly (Phase 1D) | -| Credential-backed execution | `execution-gateway` (`CredentialVaultAdapter`, `SdkConnectorExecutor`, internal) | Zero external consumers (`tests/architecture` + Phase 1D audit) | -| Vendor adapters (Razorpay, HubSpot, generic HTTP) | `execution-gateway` (`connector-execution/Gateway*Adapter`, internal) | Every `implements Connector` in `packages/*/src` is either inside `execution-gateway` or the named `MockConnector` test double (enforced test) | -| Capability constants, DTOs, schemas, signal models | `connector-sdk`, `connector-hubspot` | Zero `fetch(` calls in either package's `src/` (enforced test) | -| Orchestration / policy / signal verification / receipts | `runtime` (`RuntimeEngine`, `ExecutionTrustApplication`, `RuntimeComponent` stages) | No import of `execution-gateway`, `connector-sdk`, or `connector-hubspot`; no adapter construction; no `fetch(` (enforced test) | -| HTTP routing | `api` (`routes/*.ts`) | Calls only `application.execute(...)`; no adapter import/construction (enforced test) | -| Composition root | `api` (`bootstrap/*.ts`) | Constructs but never calls `.execute(` (enforced test) | -| Out-of-band settlement verification | `api` (`RazorpaySettlementProcessor`, driven by `scripts/process-razorpay-settlements.ts`) | Named, approved exception (§8) | - -## 4. Remaining legacy execution components - -`packages/execution-gateway/src/connector-runtime/*` — `DefaultSecureConnector`, `InMemoryConnectorRegistry`, `CapabilityConnectorPolicy` (connector-runtime's own, distinct from `GatewayCapabilityConnectorPolicy`), `DefaultExecutionChannel`, `InMemoryGatewaySessionAuthority`, `MemoryExecutionAuditSink`, and their shared `types.ts`. - -Confirmed dead in production: `createExecutionGateway.ts` only ever constructs `ExecutionGateway` via the `executionControl` option; the `connector`/`ExecutionChannel` option that would reach this subsystem is never exercised anywhere in `packages/api/src`. Not removed this phase — first flagged in the Phase 1C audit and repeatedly carried forward as explicitly out of scope. Recommended as optional cleanup for a future phase, not a Phase 1E blocker (it owns no production execution today, so it doesn't violate the single-pipeline invariant; it's inert weight, not a second path). - -## 5. Duplicate execution analysis - -**None found.** Every executable `Connector` implementation lives in `execution-gateway/src/connector-execution/`, one class per vendor, none duplicated. `connector-runtime`'s classes (§4) implement a _different, unused_ interface pairing (`ExecutionChannel`/`SecureConnector.invoke()`) that is structurally distinct from the canonical pipeline and is never wired into production — it is dead weight, not a live duplicate pipeline. No consumer migration or duplicate removal was required this phase (Task 3 — no action taken because no duplicate exists to remove). - -## 6. Architecture enforcement test results - -`tests/architecture/execution-boundary.test.ts` — **116/116 passing**. Scans `packages/*/src` generically (not a fixed file list) so a new package or file is covered automatically. Verified non-vacuous: a temporary probe file (`implements Connector` + direct `fetch()` inside `connector-sdk/src`, never committed) was independently caught by three separate checks, then removed. - -| Check | Result | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | -| Adapter ownership: only `execution-gateway` (+ named `MockConnector` exception) implements `Connector` | ✓ enforced | -| No direct `connector.execute()`/`adapter.execute()` call outside the 3-entry allowlist (`ExecutionControlService`, `SdkConnectorExecutor`, `RazorpaySettlementProcessor`) | ✓ enforced | -| `RuntimeEngine` imports none of `execution-gateway`/`connector-sdk`/`connector-hubspot`, constructs no adapter, calls no `fetch()` | ✓ enforced | -| `ExecutionTrustApplication` — same | ✓ enforced | -| Every file under `packages/api/src/routes/` — no adapter import/construction, no `fetch()` | ✓ enforced | -| Every file under `packages/api/src/webhooks/` — no `fetch()` except the named `RazorpaySettlementProcessor` exception | ✓ enforced | -| Every file under `packages/api/src/bootstrap/` — no `.execute(` call | ✓ enforced | -| Every connector package's `src/` — no `fetch()` call | ✓ enforced | -| `createExecutionSystem.ts` binds `ExecutionSystem` to `createExecutionGateway()` and constructs no other adapter | ✓ enforced | - -Full regression suite: **865/865 tests passing**, 40 skipped, `tsc -b` clean. - -## 7. Static search results - -- `implements Connector` across `packages/*/src`: 5 matches, all approved (4 inside `execution-gateway`, 1 named `MockConnector` test double). -- `connector.execute(`/`adapter.execute(` call sites across `packages/*/src`: 3 matches, all approved (`ExecutionControlService`, `SdkConnectorExecutor`, `RazorpaySettlementProcessor`). -- `fetch(` calls across `connector-sdk/src` and `connector-hubspot/src`: 0. -- `.execute(` calls across `packages/api/src/bootstrap/`: 0. -- `implements Connector` across `examples/`: 1 (`examples/04-verified-execution/run.ts`'s `RecordingHttpConnector`) — classified Example/tutorial, out of scope for production enforcement (the test suite scans `packages/*/src` only, by design; examples are documentation, not shipped code). - -## 8. Explicit list of approved exceptions - -| Exception | File | Reason it's approved | -| -------------------------------------------------------------------- | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `RazorpaySettlementProcessor`'s direct `connector.execute(...)` call | `packages/api/src/webhooks/RazorpaySettlementProcessor.ts` | Read-only fetch-verify against Razorpay to confirm a webhook's claimed refund status before writing a `SettlementConfirmation` — authorizes nothing, executes no business action, and is driven out-of-band by `scripts/process-razorpay-settlements.ts` (a worker, not the request path). Pre-dates Phase 1C; flagged and left unchanged in the Phase 1C credential-handling audit and again here. Codified in the architecture test's allowlist so it can't silently grow into a second business-action execution path — any _new_ direct-execute call site anywhere else still fails the build | -| `MockConnector` implementing `Connector` in `connector-sdk/src` | `packages/connector-sdk/src/MockConnector.ts` | **Correction (Phase 1H certification):** this row previously claimed MockConnector is "never constructed by production bootstrap" — that was false when written and remains false. `packages/api/src/bootstrap/createVendorPaymentConnector.ts` constructs it unconditionally, and `createConnectorRegistry.ts` registers it as the live handler for the `vendor-payment` connector's `payments:execute` capability, reachable from `POST /execute` through the real `ExecutionGateway → ExecutionControlService → SecureConnector` pipeline (`server.ts:35` → `createExecutionSystem()` → `createExecutionGateway()` → `createExecutionControl()` → `createConnectorRegistry()`). This does not violate Invariant 2 — MockConnector is still the one class outside `execution-gateway` permitted to `implement Connector`, and it goes through the same full authorization/credential/audit ceremony as every other connector. But unlike Razorpay/HubSpot (which fail closed and skip registration when unconfigured), `vendor-payment` is always registered, and its `MockConnector` unconditionally returns `{ success: true }` with no real vendor call — so a `payments:execute` transaction against it receives a genuinely signed Trust Record and Receipt for an execution that never left the process. `docs/CLAIMS.md` §3 already discusses `vendor-payment` as a real, production-reachable connector used in adversarial-testing demonstrations, so this was a known repository fact — Phase 1E's specific "never constructed by production bootstrap" claim was simply wrong. See `docs/architecture/repository-certification.md` for the full finding. **Resolved (Phase 2A):** `createVendorPaymentConnector.ts` now returns `undefined` outside `NODE_ENV=test`; `createConnectorRegistry.ts` no longer registers it unconditionally. In production, `payments:execute` fails closed with the same `"No connector registered for capability"` error every other unimplemented capability already produces — MockConnector is constructed by production bootstrap only when `NODE_ENV=test`. See `docs/architecture/phase2a-production-connectors.md`. **Addendum (2026-08-05, Phase 2A.4):** the fix above was deployed to production and independently verified (Phase 2A.2), and the historical-record question — whether any prior production Trust Record/Receipt originated from a real, non-test `payments:execute` call — was resolved directly against production evidence with a finding of zero such records (Phase 2A.3). Tracked as TD-1 in `docs/architecture/repository-certification.md`, now closed; full evidence chain in `docs/operations/td1-closure-summary.md`. **Final update (2026-08-09):** `payments:execute`/vendor-payment was never on the product roadmap as a real capability; rather than building the independent signal verification that would have been required to satisfy the strategic-positioning claim "only what you authorize should become real," it was removed outright. `createVendorPaymentConnector.ts` (the file this row cites, above) no longer exists, along with its dedicated credential provider and its production-registry entry — `payments:execute` now has no connector to resolve to in any environment, not only outside `NODE_ENV=test`. `policies/vendor-payment/2.0.0/policy.json` and the shared test fixtures that use it as generic example data were deliberately retained (found to be load-bearing for 19+ unrelated tests with no execution risk, since no connector exists to pair with it); a new, differently-named, equally NODE_ENV=test-only connector (`createTestFixtureConnector.ts`, capability `test:fixture-execute`) now serves that fixture role instead. Full account: `docs/VERIFICATION-GAPS.md` G-27. | -| `connector-runtime` subsystem | `packages/execution-gateway/src/connector-runtime/*` | Dead code, not a live execution path (§4) — not an "exception" to the single-pipeline invariant since it owns zero production execution, but named here for completeness as the one thing that still exists without being exercised | - -No other exceptions exist. No implementation class or call site required a new exception to keep the suite green. - -## 9. Repository invariants established by Phase 1E - -1. **Exactly one production execution pipeline** exists: `RuntimeEngine → ExecutionGateway.execute() → ExecutionControlService → SecureConnector → SdkConnectorExecutor → Gateway-owned Adapter → Business System`. Enforced by `tests/architecture/execution-boundary.test.ts`, which fails the build if a second path appears. -2. **Only `execution-gateway` may implement `Connector`** (excepting the one named `MockConnector` test double). Enforced generically across `packages/*/src` — a new package adding an adapter fails the build automatically, without needing this test file updated first. -3. **Only three call sites may invoke `connector.execute()`/`adapter.execute()` directly**, all named and justified. A fourth call site anywhere in `packages/*/src` fails the build. -4. **`RuntimeEngine` and `ExecutionTrustApplication` are structurally incapable of bypassing `ExecutionGateway`** — verified by absence of any import of `execution-gateway`/`connector-sdk`/`connector-hubspot` and absence of direct adapter construction or `fetch()`. -5. **API routes and bootstrap composition never execute business actions** — routes only ever call `application.execute(...)`; bootstrap only constructs. -6. **Connector packages own zero production execution** — zero `fetch()` calls in `connector-sdk`/`connector-hubspot` source, enforced per-package so a _new_ connector package is covered without modification. -7. **The dependency graph remains acyclic** — no new dependency edges were introduced this phase (Phase 1E added no new imports between packages; the enforcement tests read source text directly rather than adding package dependencies). -8. **No production behavior, authorization ordering, signal verification, replay protection, audit generation, or credential handling changed this phase** — Phase 1E added test files and this report only; zero production `src/` files outside the new `tests/architecture/` directory were modified. - -## Final verification - -✓ Exactly one production execution pipeline exists (§2, §6). -✓ Every production execution reaches `ExecutionGateway.execute()` (§2). -✓ No production execution bypasses authorization, signal verification, replay protection, audit generation, or credential handling — none of these subsystems' files were touched; the pipeline reaching them is unchanged (§2, §9.8). -✓ Connector packages no longer own production execution (§3, §6, §7). -✓ Runtime behavior is unchanged — zero production `src/` diffs this phase. -✓ Security behavior is unchanged — same reason. -✓ Dependency graph remains acyclic — no new imports introduced. diff --git a/docs/architecture/execution-trust-cryptography.md b/docs/architecture/execution-trust-cryptography.md deleted file mode 100644 index acaf0bd6..00000000 --- a/docs/architecture/execution-trust-cryptography.md +++ /dev/null @@ -1,459 +0,0 @@ -\# Execution Trust Cryptography - -\## Overview - -Execution Trust Cryptography provides the cryptographic foundation of Parmana's Execution Trust Infrastructure. - -It ensures that every authorization decision, execution, verification, and receipt can be independently validated long after execution has completed. - -Rather than securing communication channels, this subsystem secures execution evidence. - -\--- - -\# Purpose - -Execution Trust Cryptography provides: - -\* Integrity - -\* Authenticity - -\* Tamper Evidence - -\* Independent Verification - -\* Long-Term Auditability - -Its purpose is to produce cryptographic proof that an execution occurred exactly as authorized. - -\--- - -\# Position in the Architecture - -```text - - Business Transaction - - │ - - ▼ - - Execution Intent - - │ - - ▼ - - Authority Evaluation - - │ - - ▼ - - Authorization Decision - - │ - - ▼ - - Execution Trust Record - - │ - - ┌─────────┴─────────┐ - - ▼ ▼ - - Verification Receipt - - │ │ - - └─────────┬─────────┘ - - ▼ - - Cryptographic Evidence - -``` - -Execution Trust Cryptography begins after execution has been authorized and continues through verification and receipt generation. - -\--- - -\# Trust Pipeline - -The complete trust pipeline is: - -```text - -Execution Request - - │ - - ▼ - -Authority - - │ - - ▼ - -Authorization - - │ - - ▼ - -Execution - - │ - - ▼ - -Evidence Collection - - │ - - ▼ - -Execution Trust Record - - │ - - ▼ - -Canonical Serialization - - │ - - ▼ - -Hash Generation - - │ - - ▼ - -Verification - - │ - - ▼ - -Receipt Generation - - │ - - ▼ - -Digital Signature - -``` - -Every stage produces immutable evidence. - -\--- - -\# Execution Trust Record - -The Execution Trust Record is the primary cryptographic object. - -It captures: - -\* Execution Intent - -\* Authority - -\* Authorization - -\* Policy References - -\* Decisions - -\* Execution Evidence - -\* Verification History - -\* Receipts - -The record becomes the canonical representation of execution. - -\--- - -\# Canonical Serialization - -Before any cryptographic operation, Parmana converts objects into deterministic byte sequences. - -Canonical serialization guarantees: - -\* Stable property ordering - -\* UTF-8 encoding - -\* Deterministic formatting - -\* Platform-independent output - -The same Trust Record always produces the same byte representation. - -\--- - -\# Hashing - -Canonical bytes are hashed to produce immutable identifiers. - -```text - -Execution Trust Record - - │ - - ▼ - -Canonical Bytes - - │ - - ▼ - -Hash Provider - - │ - - ▼ - -Trust Record Hash - -``` - -Current implementation: - -\* SHA-256 - -Future providers may include: - -\* SHA3-512 - -\* BLAKE3 - -Hash providers are selected through configuration. - -\--- - -\# Verification - -Verification validates that the Trust Record satisfies Parmana's execution trust requirements. - -Verification includes: - -\* Structural validation - -\* Policy validation - -\* Evidence validation - -\* Authority validation - -\* Authorization validation - -\* Integrity validation - -Successful verification produces a verification result that becomes part of the immutable Trust Record. - -\--- - -\# Receipt Generation - -After successful verification, Parmana generates a cryptographic receipt. - -```text - -Verified Trust Record - - │ - - ▼ - -Receipt Builder - - │ - - ▼ - -Receipt Hash - - │ - - ▼ - -Signature Provider - - │ - - ▼ - -Execution Trust Receipt - -``` - -Receipts are cryptographically signed attestations of successful verification. - -\--- - -\# Signature Algorithms - -Receipt signatures are algorithm independent. - -Current implementations: - -| Algorithm | Status | - -| ---------------------- | --------- | - -| Ed25519 | Supported | - -| ML-DSA-65 (Dilithium3) | Supported | - -Future implementations may include: - -\* ML-DSA-87 - -\* SLH-DSA - -The active provider is selected through configuration. - -\--- - -\# Key Management - -Signature providers never manage key storage directly. - -Instead they obtain key material from the configured Key Provider. - -Current implementation: - -```text - -FileKeyProvider - -``` - -Future implementations: - -\* AWS KMS - -\* Azure Key Vault - -\* Google Cloud KMS - -\* HashiCorp Vault - -\* Hardware Security Modules (HSM) - -This separation allows cryptographic algorithms and key storage mechanisms to evolve independently. - -\--- - -\# Independent Verification - -Execution Trust Records and Receipts can be verified without access to the executing runtime. - -Independent verification requires: - -\* Canonical serialization - -\* Public verification key - -\* Supported signature algorithm - -\* Receipt - -\* Execution Trust Record - -No database access is required to validate cryptographic integrity. - -\--- - -\# Replay Compatibility - -Replay uses the same canonical cryptographic model. - -```text - -Execution Trust Record - - │ - - ▼ - -Replay Engine - - │ - - ▼ - -Canonical Serialization - - │ - - ▼ - -Hash Verification - - │ - - ▼ - -Receipt Verification - -``` - -Replay validates that historical executions remain cryptographically consistent. - -\--- - -\# Security Properties - -Execution Trust Cryptography provides: - -\* Integrity - -\* Authenticity - -\* Tamper Evidence - -\* Deterministic Verification - -\* Independent Validation - -\* Long-Term Auditability - -\* Algorithm Agility - -\* Post-Quantum Readiness - -It does not provide confidentiality or encryption. Those concerns belong to separate security layers. - -\--- - -\# Design Principles - -Execution Trust Cryptography is built on the following principles: - -\* Cryptography protects execution evidence, not execution itself. - -\* Canonical serialization precedes every cryptographic operation. - -\* All cryptographic artifacts are deterministic. - -\* Business logic is independent of cryptographic algorithms. - -\* Cryptographic providers are replaceable. - -\* Key management is independent of signature providers. - -\* Verification must be independently reproducible. - -\* Every receipt is a signed cryptographic attestation of a verified execution. - -These principles allow Parmana to evolve its cryptographic implementations while preserving the stability and verifiability of the Execution Trust Infrastructure. diff --git a/docs/architecture/execution-walkthrough.md b/docs/architecture/execution-walkthrough.md deleted file mode 100644 index b3b4e94e..00000000 --- a/docs/architecture/execution-walkthrough.md +++ /dev/null @@ -1,159 +0,0 @@ -# Execution Walkthrough - -A single business transaction, traced from the HTTP request that submits it to the signed Receipt returned to the caller. Every step below names the real class and method, verified directly against source (not inferred from a diagram). For the condensed call-graph form of the same trace, see [`execution-pipeline-report.md §2`](./execution-pipeline-report.md). For what each package is responsible for along the way, see [`system-architecture.md`](./system-architecture.md). - -The example: an AI agent decides to release a $5,000 vendor payment. The trace below shows what happens between that decision arriving at Parmana and a receipt being returned — including the two points where the request can be stopped (policy rejection, signal mismatch) before anything is ever authorized. - ---- - -## Stage 0 — HTTP request - -`POST /execute` with a `BusinessTransaction` body reaches `packages/api/src/routes/execute.ts`, which calls: - -```ts -await application.execute(transaction); -``` - -`application` is an `ExecutionTrustApplication` instance, built once at startup by `RuntimeFactory.create(...)` (`packages/runtime/src/RuntimeFactory.ts`) and threaded through `packages/api/src/application.ts`. - -## Stage 1 — Accept - -`ExecutionTrustApplication.execute()` (`packages/runtime/src/ExecutionTrustApplication.ts:64`) first calls `this.transactions.accept(transaction)` — `BusinessTransactionService` persists the transaction as received, before any policy or authorization work happens. - -## Stage 2 — RuntimeEngine: policy evaluation - -`ExecutionTrustApplication.execute()` then calls `this.runtime.execute(transaction)` (`Runtime.ts:31`), a thin façade that calls `RuntimeEngine.execute()` (`packages/runtime/src/RuntimeEngine.ts:125`). Inside it, in order: - -1. **Policy loading** — `PolicyRouter.load(transaction.policy.name, transaction.policy.version)` fetches the named `Policy`. -2. **Signal/Intent binding** — `SignalIntentBinder.findViolations(policy, signals, { target, parameters })` checks the transaction's declared signals describe the _same action_ as its Intent. A violation short-circuits straight to a REJECT decision (`matchedRuleId: "signal-intent-binding-violation"`) — `PolicyEngine.evaluate()` is never even called. -3. **Policy evaluation** — if no binding violation, `PolicyEngine.evaluate(policy, signals)` produces the provisional `PolicyDecision` (`APPROVE` or `REJECT`). - -**If REJECT here:** skip to Stage 2b (Refusal Record) — no external call is made, no authorization is ever generated. - -## Stage 3 — RuntimeEngine: signal/state verification - -Only runs if the provisional decision from Stage 2 is `APPROVE` **and** a `SignalStateVerifier` is configured (production always configures one — `CompositeSignalStateVerifier`, wired in `packages/api/src/application.ts` from `createRazorpaySignalStateVerifier()`/`createHubSpotSignalStateVerifier()`). - -`this.signalStateVerifier.findViolations({ action, businessTransactionId, intentParameters }, signals)` independently re-fetches the real state (e.g. `RazorpaySignalStateVerifier` calling `executeRazorpayCapability()`, which signs its _own_ fresh authorization and calls the same injected `ExecutionSystem`/`ExecutionGateway` — this is the one place, other than the real execution below, where a network call to the vendor happens). A mismatch between the declared signal and the independently-verified value overrides the decision to `REJECT` (`matchedRuleId: "signal-state-verification-violation"`) — again, no authorization is generated for it. - -## Stage 2b/3b — Refusal Record (only on REJECT) - -If `decision.outcome !== APPROVED` after Stages 2–3, `RuntimeEngine` calls `this.writeRefusalRecord(transaction, decision, bindingViolations)` (`RefusalRecordBuilder`, RFC-0021) — a signed, third-party-verifiable evidentiary record of the refusal, written _before_ `ExecutionGate.enforce()` runs. This is evidentiary-only: it never delays or affects the synchronous response. - -`ExecutionGate.enforce(decision)` then throws for any non-approved decision, ending the request here. - -## Stage 4 — Authorization - -Only reached for an `APPROVED` decision. `RuntimeEngine` builds the frozen `ExecutableContent` (`businessTransactionId`, `action`, `target`, `parameters`) and calls: - -```ts -this.authorizationSigner.sign( - { - decisionId, - businessTransactionId, - policyName, - policyVersion, - executableContent, - }, - authorizationTtlSeconds, -); -``` - -`RuntimeAuthorizationSigner` (`@parmana/crypto` under the hood) produces a `SignedExecutionAuthorization` — the cryptographic proof that _this specific content_ was authorized by policy `policyName@policyVersion`, with a TTL. - -## Stage 5 — RuntimePipeline: TrustChainValidationComponent, then ExecutionComponent - -`RuntimeEngine` builds `RuntimeContext` (`{ transaction, decision, authorization, execution }`) and calls `this.pipeline.execute(context)` (`RuntimePipeline.ts:30`), which runs its two configured stages in order (wired in `RuntimeFactory.create()`): - -1. **`TrustChainValidationComponent.execute()`** — validates the chain of custody from authority → authorization → intent is internally consistent before anything executes. -2. **`ExecutionComponent.execute()`** (`packages/runtime/src/components/ExecutionComponent.ts:44`) — builds the `ExecutionRequest` (`ExecutionRequestBuilder`), then calls: - -```ts -await this.executionSystem.execute(request); -``` - -`executionSystem` is the injected `ExecutionSystem` — in production, always `ExecutionGateway` (bound exclusively in `packages/api/src/bootstrap/createExecutionSystem.ts`). This is the boundary: everything before this line is Runtime's; everything from here on is Gateway's. - -## Stage 6 — ExecutionGateway: verify, then dispatch - -`ExecutionGateway.execute()` (`packages/execution-gateway/src/ExecutionGateway.ts:214`): - -1. Calls `this.verify(request)` internally — re-checks protocol version, signature, expiry, TTL-vs-policy, and business-transaction content hash (independently of anything Runtime already checked), then — only if every one of those passed — checks and consumes the nonce (`nonceUnseen`). Any failure throws, naming every failing check; a nonce-replay failure throws `NonceAlreadyConsumedError` specifically. -2. Deep-freezes the verified `ExecutableContent` (`deepFreeze`) so nothing can mutate it between here and the connector call. -3. Calls `this.executionControl.service.execute(transaction, gatewayAuthentication)` — `gatewayAuthentication` is minted fresh, per request, by `mintGatewayAuthentication` (a `GatewayAttestationSigner`, bound to _this specific_ `authorizationId`). - -## Stage 7 — ExecutionControlService: resolve, session, dispatch - -`ExecutionControlService.execute()` (`packages/execution-control/src/ExecutionControlService.ts:44`): - -1. Re-authenticates the Gateway itself: `authenticator.authenticateGateway(gatewayIdentity, gatewayAuthentication)`. -2. Resolves the target connector: `this.options.registry.resolveCapability(release.executableContent.action)` → a `SecureConnector` (production: `GatewayConnectorRegistry`, internal to `execution-gateway`, constructed via `createGatewayConnectorRegistry()` at bootstrap — never a class name `api` imports directly). -3. Creates a one-time `GatewaySession`: `this.options.sessions.create(release, connector.connectorId, ...)`. -4. Records `session.created` on the shared `ExecutionAuditSink`. -5. Calls `connector.execute(request)` — this `connector` is the `SecureConnector`, not yet the raw vendor adapter. - -## Stage 8 — SessionCredentialSecureConnector: credential issue, dispatch, destroy - -`SessionCredentialSecureConnector.execute()` (`packages/execution-control/src/SessionCredentialSecureConnector.ts:70`) is the production `SecureConnector` implementation `GatewayConnectorRegistry.register()` wires up (unless a connector opts into `legacyInsecure`, test-only): - -1. Re-checks the connector's own `ConnectorPolicy` (`GatewayCapabilityConnectorPolicy` wrapping `DefaultConnectorPolicy` — a second, defense-in-depth policy check independent of `RuntimeEngine`'s earlier `PolicyEngine.evaluate()`). -2. Issues a session-scoped, single-use credential lease from `sessionCredentials` (`InMemorySessionCredentialVault`, backed by `CredentialVaultAdapter` wrapping the connector's `CredentialProvider`). -3. Calls the injected `ConnectorExecutor` — production: `SdkConnectorExecutor.execute()` (`packages/execution-gateway/src/connector-execution/SdkConnectorExecutor.ts:41`), which calls `connector.execute(request, context)` on the _raw vendor `Connector`_ (`GatewayRazorpayAdapter` / `GatewayHubSpotAdapter` / `GatewayHttpAdapter`) — the one call in the whole trace that reaches the actual business system. -4. Destroys the credential lease immediately after (issue → single use → destroy — no credential outlives one execution). -5. Records `execution.completed` on the shared audit sink, with `connectorId`, `credentialId`, `gatewayId`, `authorizationId`. - -## Stage 9 — Business System - -The adapter's `execute()` (e.g. `GatewayRazorpayAdapter.execute()`) makes the real HTTP call to Razorpay/HubSpot/the configured HTTP target, using the resolved credential and the verified, frozen request. It returns an `ExecutionResult` (`success`, `metadata` — vendor response evidence, redacted of anything credential-shaped by `ConnectorEvidence`'s `redactSensitiveKeys`). - -The result unwinds back through Stages 8 → 7 → 6 → 5. - -## Stage 10 — BusinessTrustPipeline: Trust Record - -Back in `RuntimeEngine.execute()`, once `this.pipeline.execute(context)` returns the processed context, it calls: - -```ts -await this.trustPipeline.execute(processedContext); -``` - -`BusinessTrustPipeline.execute()` (`packages/runtime/src/BusinessTrustPipeline.ts:31`) validates the context carries a transaction and execution artifact, then assembles the immutable `ExecutionTrustRecord` (`BusinessTrustRecordBuilder`) — the canonical, signed record of everything that happened: transaction, decision, authorization, execution, and (once generated) receipts and settlement confirmations. - -`Runtime.execute()` persists it: `await this.trustRecords.create(trustRecord)`. - -## Stage 11 — Verification - -Back in `ExecutionTrustApplication.execute()`, after `runtime.execute()` returns: - -```ts -await this.verification.verify(transaction.businessTransactionId); -``` - -`VerificationService.verify()` independently re-derives and checks the Trust Record's hash and signature — proving the record wasn't tampered with between being written and being read back, using the same crypto primitives a third party could use to verify it themselves. - -## Stage 12 — Receipt - -```ts -await this.receipts.generate(transaction.businessTransactionId); -``` - -`ReceiptService.generate()` produces a signed `Receipt` referencing the Trust Record's hash — the artifact a caller holds as proof the execution happened, was authorized, and is independently verifiable. - -## Stage 13 — Response - -`ExecutionTrustApplication.execute()` loads the now-complete `ExecutionTrustRecord` (`trustRecords.findByTransactionId(...)`) and returns it. The route handler (`packages/api/src/routes/execute.ts`) serializes it as the HTTP response. - ---- - -## What can stop this trace early, and where - -| Stop point | Stage | What's produced instead | -| ----------------------------------------------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------- | -| Signal/Intent binding violation | 2 | `PolicyOutcome.REJECT` → Refusal Record, no external call | -| Ordinary policy REJECT | 2 | Refusal Record, no external call | -| Signal/State verification mismatch | 3 | `PolicyOutcome.REJECT` → Refusal Record — note this path _did_ make one external call (the state-verification fetch) before rejecting | -| Gateway envelope verification failure (signature/expiry/TTL/hash/nonce) | 6 | Thrown error (or `NonceAlreadyConsumedError`); `ExecutionComponent` marks the Execution failed | -| Gateway re-authentication failure | 7 | Thrown error — the Gateway itself wasn't who it claimed to be | -| Connector policy re-check failure | 8 | Thrown error — defense-in-depth denial independent of Stage 2's policy evaluation | -| Vendor call failure | 9 | `ExecutionResult.success: false`, still recorded in the Trust Record | - -Every one of these produces evidence (a Refusal Record, an audit event, or a Trust Record with `success: false`) — nothing fails silently. diff --git a/docs/architecture/milestones/2026-06-28-end-to-end-execution.md b/docs/architecture/milestones/2026-06-28-end-to-end-execution.md deleted file mode 100644 index 3d832ba8..00000000 --- a/docs/architecture/milestones/2026-06-28-end-to-end-execution.md +++ /dev/null @@ -1,353 +0,0 @@ -\# Parmana Trust Core - -\# Milestone 2 — End-to-End Execution Pipeline - -\*\*Date:\*\* June 28, 2026 - -\--- - -\# Objective - -Following the successful implementation of the core Execution Trust architecture, the next milestone is to validate the complete execution pipeline from Business Transaction acceptance through Receipt generation. - -This milestone shifts the focus from architecture implementation to execution correctness and deterministic behavior. - -\--- - -\# Goal - -Demonstrate that Parmana can execute an entire trust chain without architectural gaps. - -The execution pipeline shall produce deterministic artifacts that can be independently replayed and verified. - -\--- - -\# Canonical Execution Pipeline - -```text - -Authority - - │ - - ▼ - -Authorization - - │ - - ▼ - -Intent - - │ - - ▼ - -BusinessTransaction - - │ - - ▼ - -PolicyReference - - │ - - ▼ - -PolicyRouter - - │ - - ▼ - -PolicyValidator - - │ - - ▼ - -PolicyAdapter - - │ - - ▼ - -SignalValidator - - │ - - ▼ - -PolicyEngine - - │ - - ▼ - -DecisionService - - │ - - ▼ - -Decision - - │ - - ▼ - -ExecutionService - - │ - - ▼ - -Execution - - │ - - ▼ - -ExecutionTrustRecordService - - │ - - ▼ - -ExecutionTrustRecord - - │ - - ▼ - -VerificationService - - │ - - ▼ - -Verification - - │ - - ▼ - -ReceiptService - - │ - - ▼ - -Receipt - -``` - -Every stage must produce an immutable artifact. - -\--- - -\# Runtime Engine - -The next implementation is a Runtime Engine that orchestrates the entire execution pipeline. - -Suggested location: - -```text - -packages/runtime/src/RuntimeEngine.ts - -``` - -Responsibilities: - -\* Accept a BusinessTransaction. - -\* Coordinate all runtime components. - -\* Preserve immutable artifacts. - -\* Stop execution on validation failures. - -\* Return the completed ExecutionTrustRecord. - -The Runtime Engine owns orchestration only. Business logic remains within the specialized services. - -\--- - -\# End-to-End Integration Test - -Create a complete runtime integration test. - -Suggested location: - -```text - -packages/runtime/tests/runtime.integration.test.ts - -``` - -The test should execute the complete pipeline and verify: - -\* BusinessTransaction accepted. - -\* Decision created. - -\* Execution created. - -\* ExecutionTrustRecord created. - -\* Verification succeeds. - -\* Receipt generated. - -\* Pipeline completes without exceptions. - -\--- - -\# Replay Integration Test - -Suggested location: - -```text - -packages/replay/tests/replay.integration.test.ts - -``` - -Assertions: - -\* Replay executes the same policy. - -\* Recorded Decision equals replayed Decision. - -\* Outcome matches. - -\* Reason matches. - -\* Replay reports a successful match. - -Replay must follow the same deterministic execution path as the runtime. - -\--- - -\# Verification Integration Test - -Suggested location: - -```text - -packages/verification/tests/verification.integration.test.ts - -``` - -Assertions: - -\* Trust Record hash verifies successfully. - -\* Verification status is VERIFIED. - -\* Any modification of the Trust Record causes verification to fail. - -\* Verification remains deterministic across repeated executions. - -\--- - -\# Receipt Integration Test - -Suggested location: - -```text - -packages/runtime/tests/receipt.integration.test.ts - -``` - -Assertions: - -\* Receipt references the correct Trust Record. - -\* Receipt hash is deterministic. - -\* Receipt is generated only after successful verification. - -\* Receipt cannot be generated for an unverified Trust Record. - -\--- - -\# Runtime Invariants - -The following invariants must hold for every execution: - -1\. BusinessTransaction is immutable. - -2\. Decision is produced only by DecisionService. - -3\. Execution references exactly one Decision. - -4\. Every Execution belongs to exactly one BusinessTransaction. - -5\. Every BusinessTransaction produces exactly one ExecutionTrustRecord. - -6\. Verification operates only on immutable Trust Records. - -7\. Receipt generation requires successful verification. - -8\. Replay must reproduce the recorded Decision for deterministic inputs. - -Violation of any invariant constitutes an execution trust failure. - -\--- - -\# Success Criteria - -This milestone is complete when: - -\* All runtime integration tests pass. - -\* Replay reproduces recorded decisions. - -\* Verification detects tampering. - -\* Receipt generation succeeds only after verification. - -\* End-to-end execution is deterministic. - -\* The monorepo builds without errors. - -\* All execution artifacts are immutable. - -\--- - -\# Expected Deliverables - -``` - -packages/runtime/src/RuntimeEngine.ts - - - -packages/runtime/tests/runtime.integration.test.ts - - - -packages/runtime/tests/receipt.integration.test.ts - - - -packages/replay/tests/replay.integration.test.ts - - - -packages/verification/tests/verification.integration.test.ts - -``` - -\--- - -\# Outcome - -Completion of this milestone will demonstrate that Parmana is not only architecturally complete but operationally correct. - -The platform will provide a deterministic, verifiable execution pipeline that transforms organizational authority into cryptographically verifiable execution evidence, supporting replay, verification, auditing, and receipt generation through a single immutable trust chain. diff --git a/docs/architecture/milestones/2026-06-28-execution-trust-pipeline.md b/docs/architecture/milestones/2026-06-28-execution-trust-pipeline.md deleted file mode 100644 index 59f080c9..00000000 --- a/docs/architecture/milestones/2026-06-28-execution-trust-pipeline.md +++ /dev/null @@ -1,440 +0,0 @@ -\# Parmana Trust Core — Architecture Milestone - -\## End-to-End Execution Trust Pipeline Completed - -\*\*Date:\*\* June 28, 2026 - -\--- - -\# Overview - -This milestone completes the first end-to-end implementation of the Parmana Execution Trust Pipeline. - -The entire monorepo now builds successfully with zero TypeScript compilation errors, and every package has been aligned to the canonical Execution Trust architecture. - -\--- - -\# Build Status - -All packages compile successfully. - -``` - -✓ @parmana/shared - -✓ @parmana/policy - -✓ @parmana/runtime - -✓ @parmana/replay - -✓ @parmana/storage - -✓ @parmana/crypto - -✓ @parmana/verification - -✓ @parmana/api - -``` - -_(Historical snapshot, unchanged: this reflects the build as of 2026-06-28. -`@parmana/verification` compiled and existed at that date, but was never -wired into the runtime and had no real implementation or test coverage -behind its stages. It was retired in Session 5 (docs/CLAIMS.md); integrity, -signature, and authorization-binding verification now live directly in -`packages/runtime/src/services/verification-service.ts`.)_ - -\--- - -\# Canonical Trust Chain - -Parmana now implements the following deterministic execution flow. - -``` - -Authority - - │ - - ▼ - -Authorization - - │ - - ▼ - -Intent - - │ - - ▼ - -BusinessTransaction - - │ - - ▼ - -PolicyReference - - │ - - ▼ - -PolicyRouter - - │ - - ▼ - -PolicyValidator - - │ - - ▼ - -PolicyAdapter - - │ - - ▼ - -SignalValidator - - │ - - ▼ - -PolicyEngine - - │ - - ▼ - -DecisionService - - │ - - ▼ - -Decision - - │ - - ▼ - -ExecutionService - - │ - - ▼ - -Execution - - │ - - ▼ - -ExecutionTrustRecordService - - │ - - ▼ - -ExecutionTrustRecord - - │ - - ▼ - -VerificationService - - │ - - ▼ - -Verification - - │ - - ▼ - -ReceiptService - - │ - - ▼ - -Receipt - -``` - -This establishes a cryptographically verifiable chain from organizational authority through execution and verification. - -\--- - -\# Domain Model - -The domain model has been separated into immutable inputs, runtime artifacts, and trust artifacts. - -\## Immutable Inputs - -\* Authority - -\* Authorization - -\* Intent - -\* BusinessTransaction - -\* PolicyReference - -These objects describe what an organization intended before any runtime evaluation occurs. - -\--- - -\## Runtime Artifacts - -\* Decision - -\* Execution - -\* Override - -These objects are produced during deterministic execution. - -\--- - -\## Trust Artifacts - -\* ExecutionTrustRecord - -\* Verification - -\* Receipt - -These provide immutable evidence of execution and verification. - -\--- - -\# Architectural Changes - -\## BusinessTransaction - -BusinessTransaction now represents only the immutable input to policy evaluation. - -It no longer contains a Decision. - -``` - -Authority - -Authorization - -Intent - -PolicyReference - -Signals - -``` - -Decision is now produced later by the runtime. - -\--- - -\## DecisionService - -Decision construction has been extracted into its own service. - -Responsibilities: - -\* Generate Decision - -\* Assign Decision ID - -\* Bind PolicyReference - -\* Bind runtime signals - -\* Record outcome - -\* Record reason - -\* Timestamp evaluation - -DecisionService does not execute policies or persist data. - -\--- - -\## ExecutionService - -ExecutionService is now a pure orchestration service. - -Responsibilities: - -\* Accept a previously generated Decision - -\* Create immutable Execution - -\* Persist Execution - -\* Update execution status - -ExecutionService no longer evaluates policy. - -\--- - -\## ExecutionTrustRecordService - -ExecutionTrustRecordService creates the canonical aggregate for replay, verification, auditing, and receipt generation. - -The aggregate now contains: - -\* BusinessTransaction - -\* Executions - -\* Overrides - -\* Verifications - -\* Receipts - -\--- - -\## RuntimeContext - -RuntimeContext now accumulates immutable execution artifacts. - -``` - -RuntimeContext - -│ - -├── transaction - -├── decision - -├── execution - -├── override - -├── evidence - -├── verification - -├── receipt - -└── trustRecord - -``` - -BusinessTransaction remains immutable throughout execution. - -\--- - -\## Replay - -Replay has been aligned with the runtime architecture. - -Replay no longer depends on embedded policy artifacts. - -Instead it receives: - -\* BusinessTransaction - -\* ExecutionTrustRecord - -\* Resolved Policy - -Replay reconstructs execution through the same PolicyEngine used by runtime and compares the recorded Decision with the replayed Decision. - -This guarantees deterministic replay. - -\--- - -\## Storage - -The storage schema has been aligned with the new domain model. - -BusinessTransaction now persists: - -\* Authority - -\* Authorization - -\* Intent - -\* Metadata - -\* PolicyReference - -\* Signals - -Decision is no longer stored as part of BusinessTransaction. - -\--- - -\# Architectural Principles Established - -The implementation now enforces the following principles: - -\* BusinessTransaction is immutable. - -\* Decision is produced only by DecisionService. - -\* Execution records what happened after a Decision. - -\* ExecutionTrustRecord is the canonical aggregate. - -\* Replay follows the same deterministic execution path as runtime. - -\* Verification validates the immutable trust record. - -\* Receipt cryptographically attests the verified trust record. - -\--- - -\# Current Architecture Status - -\## Domain - -Complete. - -\## Runtime - -Complete. - -\## Policy Layer - -Complete. - -\## Replay - -Complete. - -\## Storage - -Complete. - -\## Verification - -Complete. - -\## Receipt Generation - -Complete. - -\--- - -\# Result - -Parmana has transitioned from a collection of independent components into a cohesive Execution Trust Infrastructure. - -The system now provides a deterministic, verifiable, and replayable execution pipeline in which organizational authority, policy evaluation, runtime execution, verification, and cryptographic receipt generation are connected through a single immutable trust chain. - -This milestone establishes the architectural foundation for future work such as distributed execution, remote verification, governance APIs, conformance testing, and enterprise integrations without requiring changes to the core trust model. diff --git a/docs/architecture/phase2a-production-connectors.md b/docs/architecture/phase2a-production-connectors.md deleted file mode 100644 index d03415e0..00000000 --- a/docs/architecture/phase2a-production-connectors.md +++ /dev/null @@ -1,219 +0,0 @@ -# Phase 2A — Eliminate MockConnector from Production - -Removes the highest-priority finding from Phase 1H's independent certification (`docs/architecture/repository-certification.md` §4.9, technical debt item TD-1): production bootstrap unconditionally registered `MockConnector` for the `vendor-payment` connector's `payments:execute` capability, letting production execution fabricate a genuinely signed, successful Trust Record and Receipt without ever contacting a real business system. - -**Certified against:** commit `c3a29b5` (`docs(architecture): certify repository architecture (Phase 1H)`), the tip of `main` at the start of this phase. Working tree was clean before this phase began (verified: `git status`, `git diff --stat`, `git log --oneline -10`). - ---- - -## Live Exposure Assessment - -Performed before any code change, per this phase's charter. - -**Deployment access was available and used, read-only, non-destructively:** - -- `flyctl auth whoami` → authenticated as `charak1987@gmail.com`. -- `flyctl status -a parmana-api` → app `parmana-api` is live, 2 machines `started`, health checks passing, running deployment version `39`. -- `flyctl releases -a parmana-api` → the latest release (`v39`) was deployed **2026-08-03 14:14 UTC**. -- `curl https://parmana-api.fly.dev/health` → `{"status":"UP"}`. -- `curl https://parmana-api.fly.dev/version` → `{"error":"authentication required"}` (expected — caller authentication is enforced on every route except health checks; this session does not hold a caller credential and did not attempt to obtain or fabricate one). -- `flyctl logs -a parmana-api` → confirmed the app is actively serving traffic (live `razorpay_settlement_poll_tick` worker events streaming). -- `flyctl ssh console -a parmana-api -C "cat ...createVendorPaymentConnector.js"` → **failed**: `websocket: failed to WebSocket dial ... connection attempt failed`. This is a network/tunnel limitation of the current sandboxed environment (Fly's SSH requires a WireGuard tunnel this environment could not establish), not a permissions denial. Not retried beyond this one attempt, per guidance against rabbit-holing on failing tool calls. - -**Repository evidence:** `git log --follow` shows `packages/api/src/bootstrap/createVendorPaymentConnector.ts` was introduced in commit `ddf4bc5` (2026-07-09) and never modified again until this phase. Every Phase 1 commit examined during Phase 1H certification (`f399ff5` through `c3a29b5`) is dated 2026-08-04 or 2026-08-05 — all after the `v39` deploy timestamp. - -**Conclusion, stated precisely per this phase's instructions:** the release timeline is strong circumstantial evidence that whatever commit is live as `v39` predates the entire Phase 1C–1H architectural refactor, and that `createVendorPaymentConnector.ts` (unchanged since 2026-07-09) was almost certainly already present and already unconditionally MockConnector-backed in that build. However, direct source- or registry-level confirmation of the exact deployed commit could not be obtained (the SSH tunnel failed). Per this phase's own instruction: **"The repository indicates this would be present if this revision were deployed, but the currently deployed environment cannot be independently verified from available evidence."** This is not classified as a confirmed active production incident, because deployment evidence did not confirm it at the source level — only the release-timeline inference did. See §9 for what redeploying this fix requires. - ---- - -## 1. Previous Production Registration Path - -``` -server.ts:35 createExecutionSystem() - → createExecutionGateway() - → createExecutionControl() - → createConnectorRegistry(authenticator, sessions, audit, gatewayAuthentication) - → createVendorPaymentConnector() // packages/api/src/bootstrap/createVendorPaymentConnector.ts - return new MockConnector({ - connectorId: "vendor-payment", - capabilities: connectorCapabilities(["payments:execute"]), - }); // UNCONDITIONAL — every environment, including production - → registrations.push({ connector, ...}) // UNCONDITIONAL — no credential/config gate, unlike Razorpay/HubSpot - → createGatewayConnectorRegistry(registrations) -``` - -A `POST /execute` transaction with `action: "payments:execute"` reached `ExecutionControlService.execute()` → `registry.resolveCapability("payments:execute")` → the `vendor-payment` entry → `SessionCredentialSecureConnector` → `SdkConnectorExecutor.execute()` → `MockConnector.execute()`, which (with no `script` configured, exactly how it was constructed) unconditionally returned `{ success: true, metadata: {} }`. This flowed back through the full pipeline into a genuinely signed `ExecutionTrustRecord` and `Receipt` — cryptographically indistinguishable, from the caller's side, from a real execution. - -## 2. Updated Production Registration Path - -``` -server.ts:35 createExecutionSystem() - → createExecutionGateway() - → createExecutionControl() - → createConnectorRegistry(authenticator, sessions, audit, gatewayAuthentication) - → createVendorPaymentConnector() // packages/api/src/bootstrap/createVendorPaymentConnector.ts - if (process.env.NODE_ENV !== "test") return undefined; - return new MockConnector({ ... }); // only reached when NODE_ENV=test - → if (connector === undefined) { - console.warn({ event: "vendor_payment_connector_unavailable", ... }); - } else { - registrations.push({ connector, ... }); - } - → createGatewayConnectorRegistry(registrations) // "vendor-payment" entry absent outside test mode -``` - -In production (`NODE_ENV=production`, per `Dockerfile:99`), `payments:execute` has no connector to resolve to. `GatewayConnectorRegistry.resolveCapability("payments:execute")` throws `"No connector registered for capability 'payments:execute'."` — the exact same fail-closed error Razorpay/HubSpot already produce when their credentials are unconfigured (`createRazorpayCredentialProvider.ts` / `createHubSpotCredentialProvider.ts`, both pre-existing). The pattern is now uniform across all three connectors: return `undefined` when no real implementation/credential exists, let the registry's existing fail-closed lookup do the rest. - -## 3. Complete MockConnector Inventory - -Every location where MockConnector is constructed, registered, imported, or reachable, found via repeated repository-wide `grep` for `MockConnector`, `new MockConnector(`, and `createVendorPaymentConnector` (both before and after the fix, to confirm the fix's effect): - -| Location | Classification | Notes | -| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `packages/connector-sdk/src/MockConnector.ts` | **Test infrastructure** | The class itself. Self-documented: _"Scripted, in-memory Connector for hermetic tests."_ | -| `packages/connector-sdk/src/index.ts` | **Test infrastructure (public SDK export)** | Exports the class so tests/tutorials outside `connector-sdk` can use it. | -| `packages/connector-sdk/tests/unit/mock-connector.test.ts` | **Test** | Unit tests for the class itself. | -| `packages/execution-gateway/tests/unit/connector-registry.test.ts` | **Test** | Uses `MockConnector` as a registry test fixture. | -| `packages/connector-sdk/src/connectors/{sap,oracle,workday,salesforce}/*.ts` (4 files) | **Test/reference infrastructure** | Each a thin `MockConnector` wrapper, self-documented as _"deterministic, in-memory, used until the real enterprise connector is implemented."_ Confirmed (grep, both before and after this fix) **not imported anywhere under `packages/api/src`** — unreachable from production bootstrap. No change needed. | -| `packages/connector-sdk/src/connectors/vendor-payment/VendorPaymentConnector.ts` | **Test/dev infrastructure** | A _different_, SDK-level `createVendorPaymentConnector()` (capability `"vendor-payment"`, not `"payments:execute"`) — self-documented _"temporary in-memory connector used by the integration tests."_ Confirmed not imported by `packages/api/src/bootstrap/createConnectorRegistry.ts` (which imports its own, differently-implemented, same-named function from `./createVendorPaymentConnector.js`). Coincidental name collision between two unrelated files; no change needed. | -| `packages/api/src/bootstrap/createVendorPaymentConnector.ts` | **Was Production — now Test/explicit-demo only** | **The fix.** Now returns `undefined` outside `NODE_ENV=test`. | -| `packages/api/src/bootstrap/createConnectorRegistry.ts` | **Was Production — now correctly fail-closed** | **The fix.** Registration guarded on `connector !== undefined`, mirroring the pre-existing Razorpay/HubSpot pattern exactly. | -| `packages/api/tests/unit/bootstrap/create-connector-registry.test.ts` | **Test** | Updated: the two tests that asserted `vendor-payment` remained resolvable in production now assert it fails closed; one new test explicitly proves NODE_ENV=test still works. | -| `packages/api/tests/unit/bootstrap/create-vendor-payment-connector.test.ts` | **Test** | New, dedicated to `createVendorPaymentConnector()` in isolation (§6). | -| `tests/architecture/execution-boundary.test.ts` | **Architecture test (comment only)** | Allowlist comment updated to describe the new, fixed behavior (no assertion logic changed — `MockConnector.ts` remains the one allowlisted non-gateway `Connector` implementor, which is still true). | -| `examples/tutorials/60-end-to-end-enterprise-execution/run.ts` | **Example/tutorial** | Constructs its own standalone `MockConnector`; confirmed no import from `packages/api/src`. Unaffected, unchanged. | -| `examples/tutorials/60-end-to-end-enterprise-execution/README.md` | **Example doc** | Describes the tutorial's own self-contained mock. Unaffected, unchanged. | -| `docs/architecture/{repository-certification,execution-pipeline-report,repository-invariants,system-architecture}.md` | **Documentation** | Prose references to the finding/fix. `execution-pipeline-report.md` updated with a "Resolved (Phase 2A)" note (§8, below); `repository-certification.md` intentionally left as a point-in-time snapshot (§8). | -| `docs/CLAIMS.md`, `docs/VERIFICATION-GAPS.md`, `04-INCIDENTS-LOG.md` | **Documentation (historical)** | Pre-existing discussion of `vendor-payment` as a demo/testing connector. Not edited — out of this phase's scope, and factually consistent with (not contradicted by) the fix. | -| `docs/site/concepts/the-gateway.mdx`, `docs/site/integrations/connector-development-guide.mdx` | **Public documentation — updated** | Both contained a `` callout with a literal code snippet showing the _old_, unconditional registration. Left unfixed, these would have become actively misleading the moment this fix merged (they explicitly told readers "the default server registers exactly one connector: vendor-payment, a MockConnector"). Updated in place — see §8. | -| `docs/site/{roadmap,glossary,architecture/execution-pipeline,guides/add-a-connector,integrations/overview,concepts/content-binding-toctou,reference/connector-sdk}.mdx`, `docs/site/llms-full.txt` | **Public documentation (unaffected)** | Passing mentions of `MockConnector`/`vendor-payment` in narrative or reference context, not describing production registration behavior specifically. Not edited — reviewed, no false claims found in these files. | -| `.gitleaks.toml` | **Config comment** | One line explaining why example/tutorial paths are secret-scan-exempt ("connectors here are MockConnector... never live credentials"). Not a code path; unaffected. | - -## 4. Classification of Every Remaining Occurrence - -After the fix, `MockConnector` is constructed by exactly one production-adjacent file (`createVendorPaymentConnector.ts`), and only when `NODE_ENV=test`. Every other occurrence is one of: - -- **Test** (unit test files, exercising the class or its factories directly) — expected and necessary; unchanged. -- **Test/reference infrastructure inside `connector-sdk`** (the four enterprise mocks, the SDK-level `vendor-payment` factory) — confirmed unreachable from `packages/api/src` both before and after this fix; no change was necessary or made. -- **Example/tutorial** (`examples/tutorials/60-.../run.ts`) — self-contained, never touches production bootstrap; unchanged. -- **Documentation** — prose describing the (now-fixed) history, or describing the fixed current behavior. - -No occurrence remains classified as **Production** or **Dead code**. - -## 5. Why Each Remaining Occurrence Is Acceptable - -- **Test files** exist specifically to exercise `MockConnector`'s scripted behavior deterministically — this is its designed purpose, stated in its own docstring, and the task's own charter explicitly preserves this use. -- **The four enterprise mocks and the SDK-level `vendor-payment` factory** live in `connector-sdk`, a package `system-architecture.md` documents as a "passive SDK" whose contents are metadata/reference code, not execution. They are unit-tested in place and were independently confirmed (repeated `grep`, not assumption) to have zero import path into `packages/api/src`. Removing or relocating them was not requested by this phase's charter ("Do NOT remove: tests, examples, demo infrastructure, local development tooling") and would be unrelated scope. -- **The tutorial** is explicitly named in this phase's Preserve list ("examples") and does not construct anything through the production bootstrap chain. -- **`createVendorPaymentConnector.ts` still returning `MockConnector` under `NODE_ENV=test`** is the deliberate mechanism by which this phase satisfies "Mock connectors remain valid for: unit tests, integration tests, local development, explicit demo environments" — setting `NODE_ENV=test` is itself the explicit, intentional act that distinguishes a demo/test run from production, mirroring the identical, pre-existing convention used by roughly a dozen other bootstrap factories in the same directory (`createCredentialProvider.ts`, `createNonceStore.ts`, `createCallerAuditSink.ts`, `assertStorageConfigured.ts`, and both vendor credential providers). - -## 6. Regression Tests Added - -**`packages/api/tests/unit/bootstrap/create-vendor-payment-connector.test.ts`** (new, 4 tests): - -1. Returns `undefined` when `NODE_ENV=production`. -2. Returns `undefined` when `NODE_ENV` is unset entirely (not just "not production" — confirms the gate is an explicit `=== "test"` allowlist, not a `!== "production"` denylist, matching the codebase's existing convention). -3. Returns a `MockConnector` (`connectorId: "vendor-payment"`, capability `payments:execute`) when `NODE_ENV=test`. -4. Confirms the test-mode connector is still the scripted `{success:true}` shape (guards against a future edit accidentally wiring in something else under the test branch). - -**`packages/api/tests/unit/bootstrap/create-connector-registry.test.ts`** (updated + 2 new): - -- Updated the existing "does not fail startup... vendor-payment remains resolvable" test (previously asserted success in production) to instead assert `resolveCapability("payments:execute")` throws `"No connector registered for capability 'payments:execute'"` under `NODE_ENV=production`. -- Added: explicit fail-closed test — production, no env vars manipulated beyond `NODE_ENV`, `payments:execute` throws. -- Added: explicit test-mode-still-works test — `NODE_ENV=test`, `payments:execute` still resolves to `vendor-payment`. - -**Mutation-testing validation performed, per this phase's instructions:** - -1. `git stash push` on the two changed production files, reverting them to their exact pre-Phase-2A (vulnerable) state. -2. Ran the new/updated tests against the reverted code: **4 tests failed**, each with the expected assertion mismatch (`expected MockConnector instance to be undefined` / `expected registry to throw but it resolved to "vendor-payment"`), proving the tests do detect the regression they're meant to catch. -3. `git stash pop`, restoring the fix. -4. Re-ran the same tests: **all pass** (11/11 across both files, run together with `--maxWorkers=1` to rule out any cross-file environment-variable interaction, since `process.env` is shared global state across test files in the same worker). - -**Existing coverage relied upon, not duplicated:** rather than adding a new end-to-end HTTP-level integration test, this phase traced the failure propagation directly in source (§7) and confirmed the exact same `GatewayConnectorRegistry.resolveCapability` throw — and the exact same "no catch, straight re-throw" behavior in `RuntimeEngine.execute()` — is already the mechanism Razorpay/HubSpot rely on and that mechanism has no special-casing per connector. Adding a duplicate, heavier integration test for the identical generic code path was judged unnecessary scope; the registry-level test proves the `payments:execute`-specific case directly. - -## 7. Repository Searches Performed - -- `grep -rn "MockConnector" .` (repo-wide, all extensions) — full inventory, run both before and after the fix. -- `grep -rn "new MockConnector(" packages/ examples/ tests/ --include="*.ts"` excluding test files — confirms exactly which non-test files construct it. -- `grep -rln "MockConnector" packages/api/src packages/execution-gateway/src` — final, narrow confirmation scoped to the two packages capable of reaching production bootstrap; returns exactly the two fixed files. -- `grep -rn "createVendorPaymentConnector"` (repo-wide) — disambiguated the two same-named, differently-implemented factories (`connector-sdk`'s vs. `api/src/bootstrap`'s) and confirmed only the latter is wired into `createConnectorRegistry.ts`. -- `grep -n "SalesforceConnector|WorkdayConnector|OracleConnector|SapConnector|create{Salesforce,Workday,Oracle,Sap}Connector" packages/api/src` — confirms zero matches; the four enterprise mocks are unreachable from production bootstrap. -- `grep -n "NODE_ENV" packages/api/src packages/shared/src` — established the existing, repo-wide test/production bootstrap convention this fix mirrors, and confirmed `Dockerfile:99` sets `NODE_ENV=production` for the actual deployed image. -- `grep -n "payments:execute"` (repo-wide) — enumerated every file referencing the capability, to check for any other test implicitly depending on it always resolving; none found beyond the files already accounted for above (confirmed by the full suite passing, §Build & Verification). - -## 8. Documentation Updates Made This Phase - -Beyond the code and tests: - -- `docs/architecture/execution-pipeline-report.md` — appended a "Resolved (Phase 2A)" sentence to the existing Phase 1H correction note (§8 of that document). The original correction is left intact as the historical record of what was found and when; the resolution is appended, not substituted. -- `tests/architecture/execution-boundary.test.ts` — updated the allowlist comment to describe the current, fixed behavior (comment only; the assertion it documents was already correct and unchanged). -- `docs/site/concepts/the-gateway.mdx` and `docs/site/integrations/connector-development-guide.mdx` — both contained a public-facing `` with prose and a literal code snippet asserting "the default server registers exactly one connector: vendor-payment, a MockConnector." Left unfixed, this would have been actively wrong the moment this fix merged. Updated in place to describe the new fail-closed behavior and the `NODE_ENV=test` gate, with a pointer to this report. -- `docs/architecture/repository-certification.md` (Phase 1H's report) was **intentionally not modified** — it is a dated, point-in-time certification snapshot, and its findings were accurate as of the date it was written. This report supersedes its TD-1 recommendation rather than rewriting the historical record. - -## Task 4 Verification — Fail-Closed Behavior, Traced End to End - -Read directly from source (not inferred), tracing what happens to a `payments:execute` transaction in production after this fix: - -1. `GatewayConnectorRegistry.resolveCapability("payments:execute")` (`packages/execution-gateway/src/connector-execution/GatewayConnectorRegistry.ts:152-168`) iterates registered connectors, finds none declaring `payments:execute` (it was never registered), and throws `Error("No connector registered for capability 'payments:execute'.")`. -2. `ExecutionControlService.execute()` (`packages/execution-control/src/ExecutionControlService.ts:68-71`) calls `resolveCapability` synchronously, with no `try`/`catch` around it — the throw propagates immediately, **before** session creation (line 77), **before** any credential is issued, **before** any audit "session.created" event is recorded. -3. `RuntimeEngine.execute()`'s only `catch` block around the pipeline (`packages/runtime/src/RuntimeEngine.ts:399-445`) fires the `onRuntimeError` hook and **re-throws** — it does not swallow the error into a `success: false` result. -4. Because `RuntimeEngine.execute()` throws rather than returning, `Runtime.execute()` never reaches `await this.trustRecords.create(trustRecord)` — **no Trust Record is ever persisted**. -5. `ExecutionTrustApplication.execute()` never reaches `VerificationService.verify()` or `ReceiptService.generate()` — **no Receipt is ever generated**. -6. The HTTP route (`packages/api/src/routes/execute.ts`) receives the thrown error and returns it as a failed request — the caller gets an explicit, named failure, not a 200 with fabricated evidence. - -**Verified, not fabricated:** ✓ no successful receipt, ✓ no successful Trust Record, ✓ no successful vendor evidence, ✓ no fabricated execution result. The failure is explicit and deterministic — a named error (`"No connector registered for capability 'payments:execute'."`), the same mechanism already relied upon and tested for Razorpay/HubSpot's fail-closed path. - -## Build & Verification - -Independently re-run against the fixed tree: - -``` -npx tsc -b → clean, 0 errors -npm test -- --maxWorkers=2 → 136 test files passed (+1 new file), 16 skipped; - 940 tests passed (+6 from this phase's new/split tests), - 40 skipped, 0 failed -``` - -Live vendor suites (`*-live.integration.test.ts`) were **not** run, per this phase's explicit instruction not to run them without approval. - -## Preserve List — Confirmed Unchanged - -`git diff --stat -- packages/*/src/` shows exactly two modified files, both in `packages/api/src/bootstrap/`: `createConnectorRegistry.ts` and `createVendorPaymentConnector.ts`. Neither `RuntimeEngine`, `ExecutionGateway`, `ExecutionControlService`, `SessionCredentialSecureConnector`, `SdkConnectorExecutor`, any vendor adapter, `envelope-verifier`, nor any credential-handling or audit-generation file was touched. Authorization, signal verification, replay protection, and the execution pipeline's call order are exactly as certified in Phase 1H — this phase only changed _which capabilities get registered_, never _how a registered capability executes_. - -## 9. Redeploy Requirement — Repository State vs. Deployed State - -**Repository state (this session):** the fix is committed to the working tree of `main` (not yet pushed or committed as of this report's writing — see final summary). `tsc -b` and the full test suite both pass against it. - -**Deployed state:** the live Fly.io app (`parmana-api`, `v39`, deployed 2026-08-03) was **not** redeployed during this session. No `fly deploy` or equivalent was run — this phase's charter was a repository-level fix plus a read-only exposure assessment, not a deployment action, and deploying to a shared production environment is exactly the kind of action this assistant's operating guidelines require explicit user confirmation for before taking. - -**This fix requires a redeploy to take effect in the live environment.** Until `parmana-api` is redeployed from a commit at or after this phase's fix: - -- If the release-timeline inference in the Live Exposure Assessment is correct, the currently-running `v39` build still unconditionally registers `MockConnector` for `payments:execute` in production. -- The repository (source of truth for what _will_ ship) is fixed; the deployed binary (what's _currently running_) is unchanged by anything done in this session. - -**Recommended follow-up, not performed in this session:** commit this phase's changes, then run `fly deploy` (or the project's normal deployment process) against `parmana-api`, and confirm post-deploy — ideally via a real `POST /execute` against `payments:execute` with a valid caller credential — that it now fails closed rather than succeeding. - ---- - -## Final Verification - -| Item | Status | -| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| MockConnector removed from production | ✓ — `createVendorPaymentConnector()` returns `undefined` outside `NODE_ENV=test`; `createConnectorRegistry.ts` no longer registers it unconditionally | -| Production cannot fabricate successful execution | ✓ — traced end to end (Task 4 section above); the request throws before any execution artifact is produced | -| Production cannot fabricate successful receipts | ✓ — `ReceiptService.generate()` is never reached | -| Production cannot fabricate vendor evidence | ✓ — no connector is ever invoked; `ConnectorEvidence` is never constructed for this path | -| Production fails closed when no real connector exists | ✓ — `"No connector registered for capability 'payments:execute'"`, identical mechanism to Razorpay/HubSpot | -| Tests remain functional | ✓ — full suite green (940 passed, 40 correctly-skipped, 0 failed) | -| Demo environments remain functional | ✓ — `NODE_ENV=test` still yields `MockConnector`, unchanged behavior, proven by regression test | -| Local development remains functional | ✓ — same `NODE_ENV=test` mechanism; local runs without it now correctly fail closed like production, consistent with every other bootstrap factory in this codebase | -| No production security property weakened | ✓ — Preserve list confirmed untouched (§ above); authorization, signal verification, replay protection, credential handling, audit generation all unchanged | -| Any production capability still depends on MockConnector? | **No** — final static audit (§3, §7) confirms zero production-reachable `MockConnector` construction outside the `NODE_ENV=test` branch | - -**Repository state:** fixed, tested, and documented. **Deployed state:** unchanged — the live `v39` deployment (2026-08-03) predates this fix and was not redeployed this session. **Deployment action required:** yes, a redeploy of `parmana-api` from a commit including this phase's changes, not performed here — deployment to shared production infrastructure requires explicit user confirmation before this assistant would take that action. - -The production trust gap identified during Phase 1H (TD-1) is eliminated **in the repository**. It remains present **in the currently deployed environment** until that environment is redeployed. - ---- - -**Addendum (2026-08-05, Phase 2A.4):** the paragraph above reflects this report's status at the time it was written. The repository state has since changed: `parmana-api` was redeployed from this exact commit and independently verified (Phase 2A.2, `docs/operations/phase2a-deployment-verification.md`), and the pre-existing historical-record question was separately resolved against production evidence with a finding of zero non-test-attributed records (Phase 2A.3, `docs/operations/phase2a-historical-integrity-verification.md`). TD-1 is now closed; canonical evidence chain in `docs/operations/td1-closure-summary.md`. This report's original findings and static-audit evidence above remain accurate and unedited. diff --git a/docs/architecture/phase2b-technical-debt-assessment.md b/docs/architecture/phase2b-technical-debt-assessment.md deleted file mode 100644 index fe59529e..00000000 --- a/docs/architecture/phase2b-technical-debt-assessment.md +++ /dev/null @@ -1,206 +0,0 @@ -# Phase 2B — Independent Technical Debt Assessment & Next Priority Selection - -An independent re-assessment of the repository's technical debt, performed as though by a reviewer with no memory of Phases 1A–2A.4's prioritization decisions. Every existing register entry is treated as a hypothesis re-checked against current source; every prior investigation (`docs/architecture/repository-certification.md`'s Technical Debt Register, and a previously-unaccounted-for `docs/investigations/GAP-AUDIT.md`) is advisory, not authoritative. This is an assessment only — no production source code was modified; `git status` at the end of this phase is confirmed clean. - -**Assessed against:** commit `482c474` (`docs(certification): close TD-1 after operational verification`), the tip of `main`. Working tree was clean before this assessment began. - ---- - -## Precondition Verification - -``` -git status → clean -git log --oneline -10: - 482c474 docs(certification): close TD-1 after operational verification (Phase 2A.4) - e6a25ab docs(operations): certify historical integrity after Phase 2A (Phase 2A.3) - 0c19f78 docs(operations): verify Phase 2A production deployment (Phase 2A.2) - 10c8064 docs(operations): independent readiness review... (Phase 2A.1) - 6eff8ec fix(production): remove MockConnector from production... (Phase 2A) - ... -``` - -**TD-1 closure confirmation:** read `docs/operations/td1-closure-summary.md` directly — `**Status: CLOSED.**`. Cross-checked every location Phase 2A.4 updated (`repository-certification.md`, `execution-pipeline-report.md`, `phase2a-production-connectors.md`, `phase2a-deployment-verification.md`, `phase2a-historical-integrity-verification.md`): all show TD-1 as Closed, with consistent, dated addenda; none show Open/Pending/Outstanding/Unresolved as a current-state claim. Internally consistent. Proceeding. - ---- - -## 1. Repository Health Summary - -| Area | Status | Evidence | -| -------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Architecture | **Healthy** | Single execution pipeline, adapter ownership, and import boundaries independently re-certified in Phase 1H and unaffected by any change since; `tsc -b` clean (re-run this phase) | -| Documentation | **Needs Attention** | Core architecture docs are accurate and cross-referenced; but a substantial, previously-unaccounted-for investigation document (`docs/investigations/GAP-AUDIT.md`) existed without being folded into the canonical debt register until this phase, and CLAIMS.md/VERIFICATION-GAPS.md/ROADMAP-v1.md remain a separate, parallel tracking lineage never reconciled with the TD-numbered one | -| Testing | **Needs Attention** | Full suite green (942 passed, 40 correctly-skipped, 0 failed, re-run this phase); but `packages/receipt` has zero test files (though now understood to be unreferenced by production, §3), the `typescript/` SDK still has 5 of 9 empty test-stub files, and one test (`execution-failure.integration.test.ts`) is skipped for a reason that no longer applies | -| CI | **Needs Attention** | Build/lint/typecheck/test steps all independently re-run clean this phase; but the terminology guard step would still fail if triggered (TD-2, confirmed again this phase, blast radius unchanged) and no coverage threshold is enforced (TD-3) | -| Operations | **Healthy** | Phase 2A.2/2A.3 established direct, repeatable production-verification technique (the public `/ready` endpoint plus timestamp-correlated `pg_stat_activity` observation); production confirmed live and healthy at last check | -| Deployment | **Healthy** | Fly release `v40` live, both machines healthy; deployment process (worktree-pinned `flyctl deploy`) is now a demonstrated, repeatable pattern | -| Security | **Healthy** | Caller authentication, credential isolation, replay protection, and fail-closed connector resolution all independently re-verified across Phases 1H–2A.3; no new security-relevant gap found this phase beyond the low-severity comment-hygiene item in §3 (TD-17) | -| Public APIs | **Needs Attention** | `packages/execution-gateway`'s barrel is confirmed clean (Phase 1H); but `@parmana/receipt` is a fully-implemented, tutorial-facing public package with zero test coverage of its own and zero production consumers (§3, TD-8 revisited) | -| Developer Experience | **Needs Attention** | `check-dist-fresh.ts` (a genuine, confirmed fix for a real historical gap, §2) and fail-closed config errors (`PARMANA_POLICY_DIR`, `DATABASE_PROVIDER`) are strong positives; but stale/misleading TODO comments in production bootstrap files (TD-17) and a duplicated field-extraction pattern with no drift-guard (TD-9) are real, if minor, friction points | - -No area is classified **High Risk**. Nothing found this phase rises to that bar — every item is either already resolved, or open at Medium severity or below (§3). - -## 2. Existing Technical Debt (Independently Re-Verified) - -### From `docs/architecture/repository-certification.md`'s Technical Debt Register (Phase 1H) - -| ID | Phase 1H severity | Verification result | Evidence | -| ---- | ----------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| TD-1 | High | **Closed** (confirmed, see Precondition Verification above) | — | -| TD-2 | High | **Still Exists** — and its blast radius has grown | Reproduced the exact CI guard `grep` from `ci.yml`: still fails on `docs/site/changelog.mdx:93`. **New this phase:** the guard now _also_ matches `repository-certification.md`'s own prose (quoting "execution governance" while describing TD-2 itself) — a second file would now trip it, not just one | -| TD-3 | Medium | **Still Exists** | `grep -n "coverage" .github/workflows/ci.yml` finds only an unrelated `--exclude-dir=coverage` in the terminology guard; `npm run coverage` is never invoked as a CI step | -| TD-4 | Low | **Still Exists** | `grep -n "WorkdayConnector\|SapConnector\|SalesforceConnector\|OracleConnector" docs/architecture/system-architecture.md` — zero matches, still undocumented | -| TD-5 | Low | **Still Exists** | `system-architecture.md`'s dependency graph still has no `storage`/`replay` entries | -| TD-6 | Low | **Still Exists** | `policies/vendor-payment/` still contains only `2.0.0`, not the `1.0.0` `EXECUTION-FLOW-AUDIT.md` cites | -| TD-7 | Low | **Still Exists** | `createVendorPaymentSecureConnector.ts` still 0 bytes | -| TD-8 | Low | **Still Exists, with new context** | `packages/receipt/package.json` still lacks the type-only dependency declarations. **New this phase:** `@parmana/receipt` has **zero consumers anywhere in `packages/*/src`** — it is used only by three tutorial scripts (`examples/tutorials/{54,55,56}-*`). Production's real receipt flow is `ReceiptService`/`ReceiptCrypto` (runtime/crypto), an entirely separate implementation. This re-contextualizes TD-8: the packaging-hygiene risk is real but its blast radius is "tutorial consumers of the standalone package," not production | - -### From `docs/investigations/GAP-AUDIT.md` (2026-07-07 snapshot, commit `4a3c2b9` — pre-dates every Phase 1/2 commit; never previously reconciled with the TD register) - -| New ID | Item | Original severity | Verification result | Evidence | -| --------- | ----------------------------------------------------------------------------------- | --------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| — | MUST-FIX-1 (stale `dist/`, no staleness prevention) | High (systemic gap) | **Resolved** | `scripts/check-dist-fresh.ts` exists and is wired as the `pretest` script (`package.json:31`); confirmed running and passing in this phase's own `npm test` invocation | -| — | MUST-FIX-2 (API has no authentication) | High | **Resolved** | `packages/api/src/auth/` now contains `CallerAuthenticator.ts`, `StaticKeyAuthenticator.ts`, `hashApiKey.ts`, `isPrincipalAllowed.ts`, `isOwnedByCaller.ts`; `packages/api/src/middleware/caller-auth.ts` wired conditionally in `app.ts:151` | -| — | MUST-FIX-3 (`PARMANA_STORAGE`/`DATABASE_PROVIDER` mismatch, silent memory fallback) | High | **Resolved, and hardened beyond the audit's suggested fix** | `ConfigValidation.ts:32-38` now explicitly **throws** if the retired `DATABASE_PROVIDER` is set at all ("DATABASE_PROVIDER is no longer read; set PARMANA_STORAGE instead"), rather than the audit's more modest "pick one, delete the other" suggestion | -| — | MUST-FIX-4 (`PARMANA_POLICY_DIR` no fallback, confusing crash) | High | **Resolved, via fail-closed error rather than the suggested fallback** | `Config.ts:97-103` now throws an explicit, named error ("PARMANA_POLICY_DIR is not set. Refusing to start...") instead of a bare non-null-assertion crash. Judgment note: this is arguably a _better_ fix than the audit's suggested repo-relative fallback — consistent with this codebase's established fail-closed convention (README: "a misconfigured process refuses to start rather than degrade silently") rather than introducing implicit magic | -| **TD-9** | SHOULD-FIX-1 (duplicated `ExecutableContent` extraction) | Low | **Still Exists** | `packages/runtime/src/ExecutionRequestBuilder.ts:24-38` still manually re-lists the four fields instead of calling `toExecutableContent()`; confirmed via direct read, `RuntimeEngine.ts` is still the only caller of the shared helper | -| — | SHOULD-FIX-2 (CLAIMS.md/`claim.md` markdown escaping) | Low | **Resolved** | `docs/CLAIMS.md`'s headers render as clean markdown (no literal `\#`); root `claim.md` no longer exists in the tree | -| **TD-10** | SHOULD-FIX-3 (`typescript/` SDK: 9 empty test stub files) | Session-sized | **Partially Resolved** | `Configuration.test.ts` (83 lines), `Errors.test.ts` (192), `HttpTransport.test.ts` (454), `NewApiMethods.test.ts` (159, new), plus a new `integration/parmana-client.integration.test.ts` (356) all now have real content. Still empty (0 lines): `HealthApi.test.ts`, `ParmanaClient.test.ts`, `PolicyApi.test.ts`, `ReplayApi.test.ts`, `VerificationApi.test.ts` | -| **TD-11** | SHOULD-FIX-4 (`ExecutionEvidenceComponent` dead no-op) | Low | **Still Exists** | Confirmed unreferenced anywhere outside its own file (`grep -rln "ExecutionEvidenceComponent" packages/*/src` → only its own file); still literally `return context;` with a `// TODO: Build ExecutionEvidence...` comment | -| **TD-12** | KNOWN-PARKED: `OverrideService`/`OverrideVerifier` unreachable | Guarded | **Still True** | Zero references from `packages/api/src/**`, confirmed by direct grep | -| **TD-13** | KNOWN-PARKED: Replay/Storage subsystem unreachable from `/replay` | Tier 1 | **Still True** | Zero references to `ReplayExecutor`/`ReplayPipeline` from `packages/api/src` | -| **TD-14** | KNOWN-PARKED: `LedgerSerializer` replacer-array bug | Documented, dead code | **Still True** | `packages/storage/src/ledger/LedgerSerializer.ts:9` still has the exact `JSON.stringify(entry, Object.keys(entry).sort())` bug; still dead code (same orphaned-subsystem finding above) | -| **TD-15** | KNOWN-PARKED: `execution-failure.integration.test.ts` permanently skipped | Tier 1 | **Still skipped, but the stated blocker is now resolved** | The skip's own comment says the blocker is "`RuntimeFactory` always creates a `DefaultExecutionSystem` internally, making it impossible to inject a failing implementation." Read `RuntimeFactory.create()` directly: `executionSystem: ExecutionSystem` is now a **required** constructor parameter (`RuntimeFactory.ts:41`) — it does not construct a `DefaultExecutionSystem` internally at all. The test's own stated reason for being skipped is stale; nobody has revisited it since the DI seam was added | - -IDs are assigned continuing from the existing TD-1–TD-8 register (`repository-certification.md`) so both lineages share one numbering scheme going forward (§8 of `td1-closure-summary.md`'s traceability precedent). Items marked "—" are resolved and are not carried into the active register. - -## 3. Newly Discovered Technical Debt (this phase) - -| ID | Description | Evidence | Severity (see §4 for scoring) | -| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -| **TD-16** | `ReceiptCrypto` (`packages/crypto/src/ReceiptCrypto.ts:27`) and `SettlementConfirmationCrypto` (self-documented as "structurally identical") each directly instantiate their own `FileKeyProvider()`, bypassing whatever key-provider selection the rest of the system's "crypto composition root" (`CryptoBootstrap`) would otherwise apply. Both are self-documented as temporary ("Will later be supplied by the crypto composition root"). Confirmed live: `packages/runtime/src/services/receipt-service.ts:22` — `ReceiptService`, the real production receipt-generation path, directly constructs `new ReceiptCrypto()`. | Not currently exploitable — `FileKeyProvider`/`KEY_PROVIDER=local` is the only implemented key provider anywhere in the repository today (confirmed: `docs/site/roadmap.mdx` "Move 1" still lists KMS/HSM providers as declared-but-unimplemented). But this is a **latent** gap: the moment a KMS/HSM provider is implemented (roadmap Move 1) and the rest of the system migrates, Receipt and Settlement Confirmation signing would silently continue using the local filesystem key with no error, no warning, and no test to catch the divergence. | Medium | -| **TD-17** | `packages/api/src/bootstrap/createGatewayIdentity.ts` and `createSessionStore.ts` carry explicit `TODO: Replace ... with your production gateway identity / production authentication mechanism` comments that read as unresolved security gaps. Traced both fully: `createGatewayIdentity()`'s hardcoded `"parmana-gateway"` string is a non-secret label whose actual security weight is zero — the real authentication is `SignedTokenConnectorAuthenticator.authenticateGateway()`'s cryptographic signature verification (`GatewayAttestationSigner`/`gatewayPrivateKey`), which the identity label merely accompanies. `createSessionStore.ts`'s `gatewaySessionIssuanceAuthentication = Object.freeze({})` is checked by **strict reference equality** (`InMemoryGatewaySessionStore.create()`, confirmed by reading the source: `issuanceAuthentication !== this.issuanceAuthentication`), not by content — meaning its _value_ (even if it were a "real" secret) is architecturally irrelevant, since Gateway and ExecutionControl run in the same process and no external actor can ever construct a matching object reference. | Both TODOs are misleading, not describing a real exploitable gap; independently traced end-to-end this phase, not inferred from the comment text | Low (comment-hygiene / developer-confusion risk, not a security gap) | -| **TD-18** | `GET /transactions` (`packages/api/src/routes/transactions.ts:58-64`) filters caller-scoped results **after** pagination is applied, not by pushing the scope into the storage query. Self-documented, honest, and explicitly scoped as "a pagination-correctness follow-up, not a security gap" (never over-discloses another caller's data, only under-fills a page). | Direct read of the route handler and its own comment | Low | - -No other TODO/FIXME/XXX, disabled test, CI exclusion, or "temporary"-flagged code was found beyond what's catalogued above and in §2 — confirmed by a repository-wide sweep (`TODO|FIXME|XXX`, `HACK`, `not yet implemented`, `[Tt]emporary`, `deferred`, `follow-up`, `\.skip\(`) across `packages/*/src` and `packages/*/tests`, cross-checked item by item. - -## 4. Risk Matrix (remaining open items only — closed/resolved items excluded) - -| ID | Security | Production | Operational | Architectural | Dev Productivity | Likelihood | Business Impact | Remediation Complexity | **Overall** | -| ---------------------------------------------------------------------------- | ------------------------ | ---------- | ------------------------------------ | --------------------------------------- | -------------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------ | -| TD-2 | None | None | High (CI gate actively broken) | None | Medium (false failure erodes CI trust) | Certain (reproduced live) | Low (terminology/branding, not security or correctness) | Trivial (1-line exclude-list edit, or move the note) | **High** (process integrity) | -| TD-3 | Low | None | Medium (silent coverage regressions) | None | Medium | Certain (absence confirmed) | Low-Medium | Low (add a CI step) | **Medium** | -| TD-13 (Replay unreachable) | None | Low | Low | Medium (claimed-but-unwired capability) | Low | Certain | Medium (CLAIMS.md 2.7 already self-caveats "untested at the live route," limiting overclaim risk) | Medium (needs a route + design decision on exposure) | **Medium** | -| TD-15 (execution-failure test) | None | Low | Low | None | Medium (missing regression coverage for failure-path evidence) | Certain (skip confirmed) | Medium (thematically central: does failure produce honest evidence?) | Low-Medium (blocker gone; needs the test re-verified against current API, not rewritten) | **Medium** | -| TD-16 (ReceiptCrypto composition root bypass) | Low today, Medium-future | Low today | Low | Medium (silent future divergence) | Low | Low today (no KMS provider exists yet to diverge from); High if/when Move 1 ships | Low today | Medium (needs the composition-root seam threaded through, or an explicit decision to defer) | **Medium** (time-bombed, not urgent) | -| TD-4, TD-5, TD-6, TD-7, TD-8, TD-9, TD-10, TD-11, TD-12, TD-14, TD-17, TD-18 | None | None | Low | Low | Low | Certain | Low | Trivial–Low | **Low** | - -No item scores **Critical**. TD-2 is the only item independently reaching **High** — not because the thing it protects (terminology consistency) is itself high-stakes, but because it is a _currently, verifiably broken automated gate_, which is a distinct and higher-order concern than any single open finding it might otherwise miss. - -## 5. Dependency Graph - -``` -Independent, parallelizable (no shared files, no ordering constraint): - TD-2 (ci.yml exclude-list) - TD-3 (ci.yml coverage step) - TD-4 (system-architecture.md prose) - TD-5 (system-architecture.md prose) - TD-6 (no action needed unless promoted) - TD-7 (delete one 0-byte file) - TD-9 (ExecutionRequestBuilder.ts) - TD-11 (ExecutionEvidenceComponent.ts — finish or delete) - TD-17 (comment text only) - TD-18 (transactions.ts route, if ever addressed) - -Sequenced (each requires investigation before implementation): - TD-15 → requires confirming the un-skipped test actually passes against current - API shape before enabling it (RuntimeFactory DI prerequisite already met) - TD-13 → requires a design decision (should /replay exist as a route at all, and - with what auth/scope) before any code is written — not a drop-in fix - TD-16 → blocked, in practice, on nothing structurally, but low-value to fix before - a KMS/HSM provider (roadmap Move 1) actually exists to diverge from; - fixing now is speculative work against an unbuilt consumer - TD-8 → best sequenced alongside any future decision on whether @parmana/receipt - is kept (and gets tests) or deprecated (tutorials 54-56 migrated to the - real ReceiptService/ReceiptCrypto path) — not urgent alone - -No item blocks any other. Nothing here has a prerequisite among Phase 2A's own -closed work — TD-1's closure introduced no new dependency on any remaining item. -``` - -## 6. Repository Readiness - -Independently re-run this phase, against commit `482c474`: - -``` -npx tsc -b → clean, 0 errors -npm run lint → clean -npm run typecheck → clean -npm test -- --maxWorkers=2 → 136 test files passed, 16 skipped; - 942 tests passed, 40 skipped, 0 failed -``` - -Repository is in a fully buildable, fully testable, green state. No blocked precondition exists for beginning new implementation work. - -## 7. Implementation Readiness (remaining open items) - -| ID | Readiness | Why | -| ------------------- | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| TD-2 | **Ready** | Exact fix already known (add `changelog.mdx` to the exclusion list, or move its historical note into an already-exempted file); zero design surface | -| TD-3 | **Ready** | Add `npm run coverage` as a CI step; threshold value is the only open question, and a conservative starting threshold (or none, just visibility) is a reasonable default | -| TD-4, TD-5 | **Ready** | Both are additive documentation edits with a clear, narrow scope | -| TD-6 | **Blocked (deliberately, not urgently)** | No action needed unless the file is promoted to living-doc status — there is nothing to implement today | -| TD-7 | **Ready** | Delete one 0-byte file | -| TD-8 | **Needs Investigation** | Fixing the `package.json` alone is trivial, but the newly-surfaced context (§2 — zero production consumers, only tutorial consumers) means the _right_ fix depends on a decision this assessment shouldn't make unilaterally: keep `@parmana/receipt` as a maintained public-facing library (add tests, fix the dependency declaration) or deprecate it in favor of the tutorials using the real `ReceiptService`/`ReceiptCrypto` path | -| TD-9 | **Ready** | One-line change: call `toExecutableContent()` from `ExecutionRequestBuilder.build()` | -| TD-11 | **Needs Investigation** | "Finish it or delete it" — either is a small change, but which one requires knowing whether `ExecutionEvidenceComponent` was meant to become live pipeline wiring (finish) or was superseded by evidence now captured elsewhere (delete); not visible from the code alone | -| TD-12, TD-13, TD-14 | **Needs Investigation** | All three are dead/orphaned-subsystem findings where the correct action (wire it in, or delete it) depends on product intent this codebase's own artifacts don't settle | -| TD-15 | **Needs Investigation** | Blocker confirmed resolved (§2), but the test itself needs to be run un-skipped and checked against the current API shape before trusting it — not a blind un-skip | -| TD-16 | **Needs Investigation** | Fixing it now means designing a "crypto composition root" seam for a KMS provider that doesn't exist yet — premature without Move 1 (roadmap) being scheduled | -| TD-17 | **Ready** | Comment-text-only correction; no behavior change | -| TD-18 | **Ready**, but explicitly low-priority | Already correctly scoped by its own comment as a known, accepted trade-off, not an active defect | - -## 8. Recommended Next Phase - -**Recommended: TD-2 — CI terminology guard failing against the current tree.** - -**Why this outranks every other remaining item, based on current evidence:** - -- **It is the only item independently scored High** (§4) — not on inherent stakes (terminology consistency is low business-impact on its own), but because it is the only finding that is _actively, verifiably broken right now_ in an automated gate every future change passes through. A broken CI check is a different class of problem than a missing feature or an untested path: it degrades confidence in every subsequent green checkmark, for every future phase, not just this one issue. -- **Zero remediation complexity, zero design surface.** The fix is fully specified already (an existing, already-solved precedent exists for `ROADMAP-v1.md`'s identical case) — add `changelog.mdx` to the guard's exclusion list, or relocate its one historical sentence. No investigation, no architectural decision, no stakeholder input needed. -- **Zero dependencies, blocks nothing, is blocked by nothing** (§5) — fully independent, fully parallelizable, immediately actionable. -- **Its blast radius has grown since it was first found** (§2) — a second file (`repository-certification.md` itself) now also trips the same guard, which is exactly the kind of "small problem that quietly gets worse while unaddressed" this assessment is meant to surface before it compounds further. - -**Why each higher-severity-looking or thematically-appealing alternative was not selected:** - -- **TD-3 (no coverage enforcement, Medium):** genuinely valuable, but requires a judgment call (what threshold, gate or just report) that TD-2 doesn't — slightly higher design surface for lower urgency (nothing is _currently broken_, coverage is simply unmeasured). -- **TD-15 (execution-failure test, Medium):** thematically the most appealing candidate — directly continuous with the evidence-integrity concern this entire TD-1 program was about — and now correctly unblocked. But "Needs Investigation" (§7): the test must be verified against the current API shape before trusting it, which is real, if modest, work TD-2 doesn't require. A strong second choice for the phase immediately after this one. -- **TD-13 (Replay unreachable, Medium):** requires a product decision (should `/replay` exist as a route, with what auth) before any code gets written — not implementation-ready today. -- **TD-16 (ReceiptCrypto composition-root bypass, Medium):** genuinely latent risk, but fixing it today means building a seam for a KMS provider that doesn't exist yet (roadmap Move 1 is unscheduled) — premature relative to its actual current exposure (none; `FileKeyProvider` is the only real provider in existence). -- **TD-8/TD-11/TD-12/TD-14 (Low, dead/orphaned code and packaging items):** each individually smaller in impact than TD-2, and several ("Needs Investigation") require a keep-or-delete product decision this assessment correctly declines to make unilaterally. - -**No item scored Critical**, so this recommendation is not driven by urgency in the security or production-safety sense — TD-2 wins on a straightforward value/effort/risk basis: highest-scoring open item, trivial cost, zero dependencies, actively regressing if left alone. - -## 9. Deferred Items - -Every item in §2/§3 not selected in §8 remains open and tracked, unchanged in priority ranking beyond what §4's fresh scoring already reflects. None are deferred due to disagreement with their original classification — each is deferred because §8's comparison found a better-justified next step. Specifically flagged as worth a dedicated, product-level decision before implementation (not just an engineering task): TD-8 (keep vs. deprecate `@parmana/receipt`), TD-11 (finish vs. delete `ExecutionEvidenceComponent`), TD-12/TD-13/TD-14 (wire in vs. delete the Override/Replay/Ledger orphaned subsystems). - -## 10. Assessment Methodology - -Every item was checked against current source directly — `grep`, targeted file reads, and (for TD-15) tracing a class's constructor signature to confirm a stated blocker no longer holds — rather than trusted from either prior document's own text. Where a prior document's severity or status was confirmed unchanged, that confirmation is evidenced above, not assumed. Where new evidence changed the understanding of an existing item (TD-8's true consumer set; TD-2's grown blast radius), that is stated explicitly rather than silently inherited. New debt was searched for via a repository-wide sweep for `TODO`/`FIXME`/`XXX`/`HACK`/"not (yet) implemented"/"temporary"/"deferred"/"follow-up" markers and `.skip(`-style test exclusions, with every hit individually triaged (several — the `BusinessTrustRecordBuilder.ts`/`RefusalRecordBuilder.ts` "Temporary Trust Record for hashing" comments — were confirmed to be ordinary local-variable naming, not debt, and excluded). Build/lint/typecheck/test were independently re-run this phase against the current commit, not assumed carried-over from Phase 2A.4. - -## 11. Evidence Summary - -- Direct source reads: `createGatewayIdentity.ts`, `createSessionStore.ts`, `InMemoryGatewaySessionStore.ts`, `SignedTokenConnectorAuthenticator.ts`, `ReceiptCrypto.ts`, `RuntimeFactory.ts`, `ExecutionRequestBuilder.ts`, `ExecutionEvidenceComponent.ts`, `StorageFactory.ts`, `ConfigValidation.ts`, `Config.ts`, `transactions.ts`, `LedgerSerializer.ts`, and others cited inline above. -- Repository-wide greps re-run fresh this phase (not reused from any prior phase's cached results): `implements Connector`-adjacent patterns were not re-run (unaffected by anything in this phase's scope); `TODO|FIXME|XXX`, `HACK`, "not (yet) implemented", `[Tt]emporary`, `deferred`, `follow-up`, `.skip(`, `@parmana/receipt` consumers, `OverrideService`/`OverrideVerifier`/`ReplayExecutor`/`ReplayPipeline` reachability, the exact CI terminology-guard command, and the `typescript/test/*.test.ts` line counts. -- `git log --follow`/`git show` were not needed this phase — no new provenance question arose that source-reading and cross-referencing `docs/investigations/GAP-AUDIT.md`'s own dated snapshot (`main @ 4a3c2b9`, 2026-07-07) didn't already resolve. -- Build/test evidence: `npx tsc -b`, `npm run lint`, `npm run typecheck`, `npm test -- --maxWorkers=2` all independently re-run this phase against commit `482c474`, all clean. - ---- - -## Final Recommendation - -**BEGIN NEXT IMPLEMENTATION PHASE.** - -**Target: TD-2 — fix the CI terminology guard's exclusion list so it stops failing against the current tree** (`docs/site/changelog.mdx`, and now also `docs/architecture/repository-certification.md`). - -This outranks every other remaining item because it is the only finding independently scored High in this phase's fresh risk matrix (§4), carries essentially zero remediation complexity or design surface (§7: Ready), has no dependencies in either direction (§5), and its blast radius has measurably grown since it was first documented — the clearest available signal that leaving it alone has a real, if small, ongoing cost. Every item that might otherwise seem more consequential (TD-15's evidence-integrity theme, TD-3's coverage visibility, TD-13's claims-accuracy angle, TD-16's future KMS-migration risk) either requires a decision or investigation step this item does not, or represents latent rather than currently-active risk. diff --git a/docs/architecture/phase2c-terminology-guard.md b/docs/architecture/phase2c-terminology-guard.md deleted file mode 100644 index d0aa2a76..00000000 --- a/docs/architecture/phase2c-terminology-guard.md +++ /dev/null @@ -1,189 +0,0 @@ -# Phase 2C — Repair the CI Terminology Guard - -Repairs TD-2 (`docs/architecture/repository-certification.md`, Technical Debt Register), the single item Phase 2B's independent reassessment (`docs/architecture/phase2b-technical-debt-assessment.md`) recommended as the next implementation target. This is an implementation phase scoped narrowly to CI configuration and its own test coverage — no production source code, runtime behavior, or execution architecture was touched. - -**Repaired against:** commit `17c4b2f` (`docs(architecture): independently reassess technical debt after Phase 2A`), the tip of `main`. Working tree was clean before this phase began. - ---- - -## Independent Verification (before touching anything) - -Reproduced the exact CI guard command from `.github/workflows/ci.yml` against the current tree, rather than trusting Phase 2B's report text: - -``` -grep -rIn --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=dist \ - --exclude-dir=coverage --exclude='ROADMAP-v1.md' --exclude='VERIFICATION-GAPS.md' \ - --exclude='how-parmana-thinks.mdx' --exclude='execution-authorization.mdx' \ - --exclude='ci.yml' -i "execution governance" . -``` - -**Confirmed TD-2 still exists, and its blast radius had grown to three files** (not the one originally documented in Phase 1H): - -| File | Line | Status before this phase | -| -------------------------------------------------------- | ---- | ---------------------------------------------- | -| `docs/site/changelog.mdx` | 93 | Original finding (Phase 1H) | -| `docs/architecture/repository-certification.md` | 172 | New — quotes the phrase describing TD-2 itself | -| `docs/architecture/phase2b-technical-debt-assessment.md` | 49 | New — quotes the phrase describing TD-2 itself | - -A full, unfiltered repo-wide search (no `--exclude` at all) for the exact phrase, case-insensitive, found **exactly 8 files total** — the 3 above, plus the 5 already correctly excluded (`ci.yml` itself, `ROADMAP-v1.md`, `VERIFICATION-GAPS.md`, `how-parmana-thinks.mdx`, `execution-authorization.mdx`). No file containing the phrase was unaccounted for; no silent false negative existed alongside the known false positives. - -## 1. Original Failure - -The guard's `if grep ...; then ... exit 1; fi` step, run against commit `17c4b2f`, exits non-zero and would fail CI, because it detects the retired phrase "execution governance" in `docs/site/changelog.mdx` (and, as of this phase's independent re-verification, two additional files) that are not in its `--exclude` list. - -## 2. Root Cause - -The guard's only exemption mechanism is a fixed, per-file `--exclude=basename` list. This has two compounding weaknesses: - -1. **Incompleteness at the time it was written** — `changelog.mdx` legitimately narrates the same terminology history `ROADMAP-v1.md`/`VERIFICATION-GAPS.md` already do, in the same past-tense, self-referential way, but was never added to the list. -2. **Structural tendency to keep growing** — any document whose job is to _describe_ this exact guard, its history, or the retired term it protects against (an audit report, a certification, a technical-debt register entry) must quote the phrase to do so accurately, and immediately becomes a new false positive the moment it's committed. This happened twice, independently, in one day (Phase 1H's certification report and Phase 2B's assessment report each rediscovered and described the same underlying bug, and each commit that added that description silently became a new violation). - -The root cause is not that the exclusion mechanism is _wrong_ — file-based exclusion is a legitimate, low-risk approach (and this phase's preferred-fix guidance names it explicitly) — but that it was maintained reactively, one omission at a time, rather than documented as a pattern maintainers should recognize and extend proactively. - -## 3. Guard Architecture - -**Entry point:** `.github/workflows/ci.yml`, step `Guard against retired terminology`, runs after `Typecheck` and before `Test` in the `build-and-test` job, on every push to `main` and every pull request. - -**Matching algorithm:** a single `grep -rIn -i "execution governance" .` over the whole repository tree (`-r` recursive, `-I` skip binary files, `-n` line numbers, `-i` case-insensitive), narrowed by `--exclude-dir` (directories skipped entirely) and `--exclude` (basenames skipped, matched anywhere in the tree). Any match causes the step to print a `::error::` annotation and exit 1, failing the build. - -**Directory exclusions (unchanged by this phase):** `node_modules`, `.git`, `dist`, `coverage` — build output and dependency trees, never hand-authored content. - -**File exclusions, after repair** (basename-matched, same mechanism as before, now complete): - -| File | Category | Reason | -| -------------------------------------------------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------- | -| `ci.yml` | Self-reference | Its own comment and grep pattern must name the phrase to check for it | -| `tests/architecture/terminology-guard.test.ts` | Self-reference | New this phase (§6) — its own matching pattern and test fixtures must name the phrase for the same reason | -| `docs/ROADMAP-v1.md` | (1) Historical self-narration | Narrates the terminology sweep itself | -| `docs/VERIFICATION-GAPS.md` | (1) Historical self-narration | Documents this repo's own terminology history | -| `docs/architecture/repository-certification.md` | (1) Historical self-narration | Phase 1H's certification report, discovered and described this exact gap | -| `docs/architecture/phase2b-technical-debt-assessment.md` | (1) Historical self-narration | Phase 2B's reassessment, independently rediscovered and described this exact gap | -| `docs/architecture/phase2c-terminology-guard.md` | (1) Historical self-narration | This document — necessarily quotes the phrase throughout to document the fix | -| `docs/site/changelog.mdx` | (1) Historical self-narration | The original false positive — this repo's public changelog, narrating the same history in past tense | -| `docs/site/how-parmana-thinks.mdx` | (2) Third-party citation | Cites an unrelated academic framework actually named "Execution Governance" (Ku, 2026) | -| `docs/site/concepts/execution-authorization.mdx` | (2) Third-party citation | Same third-party citation | - -## 4. Matching Algorithm (detail) - -Unchanged from before this phase: literal substring match (via `grep`'s basic regex mode) for the case-insensitive phrase `execution governance`, with word-boundary-free matching (so `Execution Governance`, `execution governance`, `EXECUTION GOVERNANCE`, and any embedded occurrence within a longer sentence all match identically). No stemming, no context-awareness, no distinction between quoted/unquoted usage — deliberately simple, matching this repository's own stated preference for "the cheap, high-value check" over heavyweight prose analysis (the same design philosophy `tests/architecture/documentation-references.test.ts` states explicitly for its own, unrelated check). - -## 5. Allowed Exceptions - -See the table in §3. Two, and only two, legitimate categories exist — the guard does not exempt anything for any other reason (e.g., "this file is old" or "this file is unimportant" are not exemption criteria; every exemption traces to one of the two categories above). - -## 6. Forbidden Terminology - -Exactly one phrase, case-insensitive: **"execution governance"**. The locked, correct vocabulary is **"execution authorization"** / **"AI Execution Authorization"**, per the guard's own error message. Independently confirmed this phase that neither "Execution Trust" (e.g. `ExecutionTrustRecord`, used throughout `README.md`) nor "Policy Engine" (e.g. the `PolicyEngine` class, `docs/architecture/system-architecture.md`) are retired or checked-for terms — both are current, correct architecture vocabulary, unaffected by this guard. - -## 7. CI Integration - -No change to when or how the guard runs: same step, same position in the job (`Typecheck` → **`Guard against retired terminology`** → `Test`), same trigger (`push` to `main`, every `pull_request`). The only change is the completeness of the `--exclude` list and the clarity of the comment explaining it (§3). `npm test` (run both locally and in CI) now also runs `tests/architecture/terminology-guard.test.ts` (§6), so the guard's logic is verified on every test run, not only when the CI workflow step itself executes. - -## Task 4 — False Negative Audit - -Searched, independently of the guard's own exclusion list, for the exact phrase across every category this phase's instructions name: - -``` -README.md → 0 matches -docs/architecture/system-architecture.md → 0 matches -docs/architecture/execution-walkthrough.md → 0 matches -docs/developer/extending-parmana.md → 0 matches -docs/architecture/repository-invariants.md → 0 matches -docs/architecture/execution-pipeline-report.md → 0 matches -packages/*/package.json, packages/*/README.md → 0 matches -package.json (root) → 0 matches -typescript/, python/, examples/ → 0 matches -docs/site/ (public docs site) → exactly the 2 known, classified, - legitimate third-party citations; - no other matches -``` - -**No active documentation contains the retired phrase.** README.md specifically was checked and found clean — there is no README finding to report as a separate tracked item. - -## Task 5 — Disposable Probe Validation - -Two throwaway files were created under `docs/`, checked against the repaired guard command, and deleted immediately after (never committed; `git status` before and after this step was compared to confirm no trace remained): - -1. `docs/_terminology_guard_probe.md`, containing `"This document describes our Execution Governance layer."` — the repaired guard **detected it** (`grep` exit code 0, meaning the CI step's `if` branch would trigger and fail the build), proving new, unlisted legacy terminology still fails as required. -2. `docs/_terminology_guard_probe2.md`, containing only "Execution Trust" and "Policy Engine" — the repaired guard **did not** detect it (`grep` exit code 1, no match), confirming the guard remains precisely scoped to the one forbidden phrase and was not accidentally broadened. - -Both probes were deleted with `rm` immediately after use; `git status --porcelain=v1 -uall` confirmed a clean tree with no residual files before proceeding. - -## Task 6 — Regression Tests - -`tests/architecture/terminology-guard.test.ts` (new), 6 tests, reimplementing the guard's exact matching logic in TypeScript so it runs on every `npm test`, not only when the CI workflow step executes: - -1. The repository, scanned by the reimplemented guard, currently has zero violations. -2. Active documentation (`README.md`, `system-architecture.md`, `execution-walkthrough.md`, `repository-invariants.md`, `extending-parmana.md`) contains no instance of the phrase. -3. Historical self-narration files are both (a) present in the exclusion list and (b) confirmed to actually contain the phrase (proving the exclusion is exercised, not vacuous). -4. Third-party-citation files are both (a) present in the exclusion list and (b) confirmed to actually contain the phrase. -5. A synthetic probe string (not written to disk) proves the matcher still fires on unlisted content. -6. A synthetic benign string ("Execution Trust", "Policy Engine") proves the matcher does not over-match. - -The exclusion list is duplicated (not shared) between `ci.yml` and this test file, by the same convention `documentation-references.test.ts` already uses for its own document list — the file's own comment states this explicitly and flags it as something to update in both places together. - -## Task 7 — Build & Verification - -Independently re-run against the repaired tree: - -``` -npx tsc -b → clean, 0 errors -npm run lint → clean (caught and fixed one unused import - in the new test file during this process) -npm run typecheck → clean -npm test -- --maxWorkers=2 → 137 test files passed (+1 new), 16 skipped; - 948 tests passed (+6 new), 40 skipped, 0 failed -grep ... (the guard, run directly, full updated exclude list) → clean, 0 violations -``` - -## Task 7.5 — Before/After Behavior Comparison - -| Metric | Before this phase | After this phase | -| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Total files containing the phrase (unfiltered) | 8 | 10 (the 8 original, plus this document and the new regression test, both of which necessarily quote the phrase to document/check for it) | -| Files in the exclude list | 5 | 10 | -| **Violations (guard would fail)** | **3** | **0** | -| Valid violations (genuine regressions requiring a doc fix) | 0 | 0 | -| False positives | 3 (`changelog.mdx`, `repository-certification.md`, `phase2b-technical-debt-assessment.md`) | 0 | -| Files newly excluded this phase | — | `repository-certification.md`, `phase2b-technical-debt-assessment.md`, `phase2c-terminology-guard.md`, `changelog.mdx`, `terminology-guard.test.ts` (5) | -| Files newly checked (scope narrowed) | — | **None.** `--exclude-dir` set unchanged (`node_modules`, `.git`, `dist`, `coverage`); every file outside the exclude list, before and after, is still scanned. Nothing that was protected before is unprotected now | -| New legacy terminology (disposable probe) | — | Still detected (Task 5, probe 1) | -| Legitimate current terms (Execution Trust / Policy Engine) | — | Still never flagged (Task 5, probe 2) | - -**✓ False positives eliminated (3 → 0). ✓ No legitimate violation became silently accepted (0 valid violations existed before or after — every one of the 3 originally-failing files was independently classified False Positive / Historical Documentation, never Valid Failure). ✓ No new false negative introduced (disposable probe still fails; Task 4's active-documentation sweep found nothing newly missed).** - -## 8. Validation Performed - -Summarized from Tasks 4–7.5 above: independent re-verification of the original failure before any change; a full, unfiltered repo-wide search establishing there were exactly 8 (now 10) files in the entire tree containing the phrase, with none unaccounted for; a false-negative audit across every category this phase's instructions named, all clean; two disposable, uncommitted probes proving both directions (still catches new violations, still ignores unrelated terms); a new, permanent regression test suite; and a full independent re-run of `tsc -b`, lint, typecheck, the full test suite, and the guard command itself. - -## 9. Remaining Limitations - -- **The exclusion mechanism is still a maintained list, not a self-describing rule.** This phase made the list complete and clearly documented (§3's two-category framework, plus an explicit instruction in `ci.yml`'s comment for future maintainers), but it is not structurally immune to the same omission that caused the original bug — a future document that legitimately needs to quote the retired phrase must still be manually added. The comment added this phase exists specifically to make that addition obvious and low-friction rather than to eliminate the need for it. -- **`grep --exclude` matches by basename only, repository-wide**, not by full path. If two files anywhere in the tree ever shared one of the excluded basenames (e.g., a second, unrelated `changelog.mdx`), both would be exempted together. No such collision exists today (verified: each excluded basename is unique in the tree), so this is a documented, currently-inert design property, not an active gap — not changed this phase, to avoid introducing an untested change to the matching mechanism itself for a risk that isn't currently realized. -- **The regression test's exclusion list is hand-duplicated from `ci.yml`'s**, not derived from a single shared source. This mirrors an existing, accepted precedent in this codebase (`documentation-references.test.ts`'s own document list), not a new pattern — flagged for the same reason that precedent presumably was: worth a future consolidation if either list grows unwieldy, not urgent today. - ---- - -## Final Verification - -| Item | Status | -| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| CI terminology guard repaired | ✓ | -| Original false positives eliminated | ✓ — all 3 confirmed gone (Task 7.5) | -| No false negatives introduced | ✓ — disposable probe still fails; false-negative audit clean (Task 4, 5) | -| Historical documentation preserved | ✓ — zero documentation files edited; only `.github/workflows/ci.yml` and the new test file were touched | -| Active documentation protected | ✓ — still fully in scope of the guard; explicitly re-verified clean (Task 4) | -| CI enforcement remains effective | ✓ — same scan scope, same trigger, same failure behavior; only the exclude list's completeness changed | -| No production code changed | ✓ — `git diff --stat` confirms only `.github/workflows/ci.yml` and `tests/architecture/terminology-guard.test.ts` changed; zero files under any `packages/*/src` | - -## Final Recommendation - -**TD-2 CLOSED.** - -All five required conditions are demonstrated with repository evidence, not asserted: - -- The original false positives (`changelog.mdx`, plus the two newly-discovered ones) no longer occur — confirmed by re-running the exact guard command (§ Independent Verification, Task 7.5). -- Disposable probes still fail as expected — confirmed directly (Task 5), not inferred. -- Active documentation remains protected — confirmed by an explicit sweep finding zero instances anywhere outside the classified exclusion list (Task 4). -- Archived/historical documentation remains allowed — confirmed each excluded file both belongs to a named, justified category and actually contains the phrase (Task 6's regression tests assert this continuously). -- CI behavior improved (3 false positives → 0) without weakening enforcement — scan scope, directories excluded, and trigger conditions are all unchanged; only five specific, individually-justified files were added to what was already an established, precedented exclusion mechanism. diff --git a/docs/architecture/phase2d-execution-failure-testing.md b/docs/architecture/phase2d-execution-failure-testing.md deleted file mode 100644 index 354f2bac..00000000 --- a/docs/architecture/phase2d-execution-failure-testing.md +++ /dev/null @@ -1,114 +0,0 @@ -# Phase 2D — Restore Execution Failure Integration Testing - -Restores TD-15 (`docs/architecture/repository-certification.md` Technical Debt Register, added Phase 2B), the second implementation target Phase 2B's independent reassessment identified after TD-2 (repaired in Phase 2C). This is an implementation phase scoped to one test file — no production source was modified. - -**Restored against:** commit `f953d6c` (`ci(architecture): repair terminology guard and close TD-2`), the tip of `main`. Working tree was clean before this phase began. - ---- - -## 1. Original Skipped Rationale - -`packages/api/tests/integration/execution-failure.integration.test.ts`'s own comment, verbatim before this phase: - -> "This test is intentionally skipped. The current RuntimeFactory always creates a DefaultExecutionSystem internally, making it impossible to inject a failing implementation. This test should be enabled after RuntimeFactory supports dependency injection for ExecutionSystem." - -## 2. Independent Verification - -Phase 2B's claim ("`RuntimeFactory.create()` now appears to provide the required seam") was treated as an unverified hypothesis, not inherited. Read `packages/runtime/src/RuntimeFactory.ts` directly: `create()`'s signature takes `executionSystem: ExecutionSystem` as a required (non-optional, non-defaulted) constructor argument (`RuntimeFactory.ts:41`) and passes it straight through — it does not construct a `DefaultExecutionSystem`, or any `ExecutionSystem`, internally. **The stated blocker is confirmed gone.** - -**A second, more consequential blocker was found that Phase 2B's narrower check did not surface.** The skipped test, as written, does not use `RuntimeFactory`'s DI seam at all — it imports `app` from `../test-app.js`, a module-level singleton that calls the real, production `createExecutionSystem()` (the actual `ExecutionGateway`, wired to real connector resolution). There was never a mechanism in the test's own code for making that shared app's execution system fail; the DI seam being available doesn't help a test that doesn't use it. - -**A third issue, independent of both DI questions, was found and confirmed empirically, not by inspection alone.** The test's assertions describe a response shape that does not exist anywhere in the current API: - -```ts -expect(response.body.execution.status).toBe("FAILED"); -expect(response.body.error).toContain("Execution System"); -``` - -A disposable probe (constructed with `createInspectableExecutionSystem`, run once, deleted immediately — never committed) that submitted a real transaction against a connector executor engineered to throw produced: - -``` -STATUS: 500 -BODY: {"error": "Internal Server Error"} -``` - -`packages/api/src/middleware/error-handler.ts`'s generic fallback (`error-handler.ts:151-155`) maps any unrecognized thrown error to exactly this shape — by design, so no internal failure detail is ever leaked to a caller (the same "no stack-trace leakage" property `docs/investigations/GAP-AUDIT.md` independently confirmed a month earlier, §7 "What is genuinely solid"). There is no `execution` field in this response at all, and the message will never contain the real error text, "Execution System" or otherwise. The original test's specific assertions could never have passed against this codebase's actual, intentional error-handling design — they describe a response shape that doesn't exist, not one that used to exist and drifted. - -## 3. Root Cause - -Two independent, compounding problems, neither of which is "the DI seam is missing" (that part, Phase 2B correctly found, is fixed): - -1. **The test's harness never exploited the injection capability it needed** — it used the shared, non-configurable `test-app.js` rather than the already-existing `createInspectableExecutionSystem` helper (`packages/api/tests/bootstrap/createInspectableExecutionSystem.ts`, which explicitly supports `options.executor` to "force a downstream failure" — already in use by `credential-isolation.integration.test.ts` for exactly this purpose). -2. **The test's assertions were written against a response contract that either never existed or predates the current centralized error handler**, and nobody revisited them when `error-handler.ts`'s no-leak design was established. - -## 4. Restoration Work - -`execution-failure.integration.test.ts` rewritten (the `.skip` removed, the harness and assertions replaced; nothing else in the repository touched): - -- **Harness:** `createInspectableExecutionSystem({ executor: { async execute() { throw ...; } } })`, matching the pattern `credential-isolation.integration.test.ts` already established — a connector executor that deterministically throws, running through the real `ExecutionGateway` → `ExecutionControlService` → `SessionCredentialSecureConnector` chain, not a mock of the chain itself. -- **Assertions**, all verified against actual, current behavior (not assumed): - - `response.status === 500`, `response.body` deep-equals `{ error: "Internal Server Error" }` (replacing the two assertions that could never have passed). - - The real failure message never appears in the response body (a positive assertion of the no-leak property, not just an absence check). - - The connector executor was called exactly once (proves the failure happened at the intended point, not earlier for an unrelated reason, and that nothing retried or partially executed). - - `GET /trust-records/:id` → 404, exact body `{ error: "Execution Trust Record not found." }`. - - `GET /receipt/latest/:id` → 404. - - `GET /refusal/:id` → 404, exact body `{ error: "Refusal Record not found." }` — proving this failure is _not_ a policy rejection (a materially different, already-well-tested path — see §5), but a genuine post-authorization, downstream execution failure. - - At least one `execution.rejected` audit event exists (the failure is evidenced, not silently swallowed) — a light cross-check, not a duplicate of `credential-isolation.integration.test.ts`'s own, deeper credential-lifecycle proof for this same failure shape. -- **A second test** added: the same failing setup run three times in a row, each asserting the identical `500` / exact-body result, demonstrating the failure is deterministic (a property of the connector always throwing) rather than something that happened to be observed once. - -No assertion from the original test was weakened — the two that could never have passed were replaced with assertions that check the same underlying concern (the caller is told the execution failed) against the real, verified contract, and five additional properties Task 4 required (no partial execution, no Trust Record, no Receipt, not-a-policy-rejection, audit still fires) were added, none of which the original test attempted. - -## 5. Failure Integrity Guarantees (Task 1 / Task 5 — coverage inventory) - -| Failure path | Coverage before this phase | Coverage after this phase | -| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Connector resolution failure (no connector registered — TD-1/Phase 2A) | Unit-tested (`create-connector-registry.test.ts`) | Unchanged — out of this phase's scope | -| **Connector execution failure (executor throws)** | Partially tested — `credential-isolation.integration.test.ts` proved the audit/credential-lifecycle angle only; response shape and evidentiary absence were untested anywhere | **Now fully tested** (this phase) — response contract, Trust Record/Receipt/Refusal-Record absence, determinism | -| Runtime exception (non-connector, unexpected internal error) | Untested as a distinct scenario | Still untested as a distinct scenario — shares the same generic-500 handling path as connector execution failure (§2), so the incremental risk is low; not in this phase's scope (the skipped test was specifically about connector/execution-system failure, not internal runtime bugs) | -| Authorization denial (policy REJECT) | Extensively tested (`refusal-record.integration.test.ts`, `execution-authorization-wiring.test.ts`, others) | Unchanged | -| Signal verification failure | Tested (`hubspot-deal-update.integration.test.ts`, `hubspot-deal-update-policy.test.ts`, `hubspot-deal-update-signals.test.ts`) | Unchanged | -| Replay protection failure (nonce reuse) | Tested (`execution-gateway.test.ts`, 409 `NonceAlreadyConsumedError`) | Unchanged | - -Every failure path this phase's instructions named is covered by at least one test, before and after this phase. The one gap this phase closes is specifically the connector-execution-failure path's HTTP-contract and evidentiary-absence properties — the credential-lifecycle half of that same path was already covered. - -## Regression - -Independently re-run against the restored tree: - -``` -npx tsc -b → clean, 0 errors -npm run lint → clean -npm run typecheck → clean -npm test -- --maxWorkers=2 → 138 test files passed (+1 vs. before), 15 skipped (-1); - 950 tests passed (+2), 39 skipped (-1), 0 failed -``` - -The restored file's own tests, run in isolation with verbose output, both pass: `marks execution as failed, with no fabricated success and no evidentiary artifacts, when the connector executor throws` and `fails deterministically across repeated attempts against the same failing connector (not flaky)`. - -`git diff --stat -- packages/*/src/` is empty — no production source file was modified; the only change in the repository is to `packages/api/tests/integration/execution-failure.integration.test.ts` itself. - -## 6. Remaining Limitations - -- **"Runtime exception" (a non-connector, genuinely unexpected internal error) remains untested as its own scenario.** It shares the exact same generic-500 error-handling code path as the connector-failure case this phase now covers, so the marginal risk is low, but no test deliberately triggers an internal `RuntimeEngine`-level throw unrelated to a connector. Out of this phase's scope (the skipped test was never about this case); worth a future, separate, narrowly-scoped addition if judged valuable. -- **Two stray debug `console.log` statements were observed, incidentally, in production source** during this phase's empirical verification (`packages/api/src/routes/execute.ts`: `"[ROUTE] before execute"` / `"[ROUTE] after execute"`; `packages/runtime/src/ExecutionTrustApplication.ts`: `"[APP] ..."` at what appear to be the accept/runtime stages). Neither affects correctness or the properties this phase verifies, and per this phase's explicit no-production-changes mandate, **neither was touched**. Flagged here for visibility as an unrelated, minor cleanup opportunity, not addressed in this phase. -- **The credential-lifecycle proof for this failure shape lives in a separate file** (`credential-isolation.integration.test.ts`) from the response-contract/evidentiary-absence proof this phase added (`execution-failure.integration.test.ts`). This is a deliberate choice to avoid duplicating an already-thorough, already-passing test, not an oversight — but it does mean the full picture of "what happens on a connector execution failure" requires reading both files together. - ---- - -## Final Verification - -| Item | Status | -| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Skipped integration test restored | ✓ — `.skip` removed, both tests pass | -| Original blocker independently disproven or confirmed | ✓ — disproven (DI seam confirmed present, §2); a second, more consequential blocker (stale assertions, unused DI capability) was found and independently fixed in the same pass | -| Failure integrity verified | ✓ — via assertions, not exception-presence alone (§4, §5) | -| Deterministic failures preserved | ✓ — dedicated repeated-attempt test added | -| Runtime behavior unchanged | ✓ — zero files under `packages/*/src` modified | -| Security behavior unchanged | ✓ — no-leak error-handling behavior independently confirmed, not altered | -| No production behavior changed | ✓ — `git diff --stat -- packages/*/src/` empty | - -## Final Recommendation - -**TD-15 CLOSED.** - -The skipped test is restored, executes successfully, and now verifies more than its original scope: the exact HTTP response contract for a connector execution failure (empirically confirmed, not assumed), the complete absence of Trust Record/Receipt/Refusal Record evidence, exactly-once connector invocation (no partial execution), continued audit-trail evidence, and determinism across repeated attempts — all via real assertions against a real request/response cycle, not by checking that an exception was thrown. The original blocker (no DI seam) is confirmed resolved; the additional blocker this phase found (the test never used the seam, and its assertions never matched any real response shape) is fixed in the same change. No production source was modified, and the full regression suite (950 passed, 39 skipped, 0 failed) is clean. diff --git a/docs/architecture/phase2e-engineering-debt-assessment.md b/docs/architecture/phase2e-engineering-debt-assessment.md deleted file mode 100644 index c74c9a70..00000000 --- a/docs/architecture/phase2e-engineering-debt-assessment.md +++ /dev/null @@ -1,175 +0,0 @@ -# Phase 2E — Independent Medium-Priority Engineering Debt Assessment - -An independent re-assessment of the repository's remaining engineering-quality technical debt, performed as though by a reviewer with no memory of Phases 2A–2D's own conclusions. Every item Phase 2B previously catalogued is treated as a hypothesis re-checked against current source; historical reports (`repository-certification.md`, `phase2b-technical-debt-assessment.md`, `docs/investigations/GAP-AUDIT.md`) are advisory, not authoritative. This is an assessment only — no production source code was modified; `git status` at the end of this phase is confirmed clean except for the documentation deliverables this phase produces. - -**Assessed against:** commit `752be4c` (`test(integration): restore execution failure testing and close TD-15`), the tip of `main`. Working tree was clean before this assessment began. - ---- - -## Precondition Verification - -``` -git status → clean -git log --oneline -10: - 752be4c test(integration): restore execution failure testing and close TD-15 (Phase 2D) - f953d6c ci(architecture): repair terminology guard and close TD-2 (Phase 2C) - 17c4b2f docs(architecture): independently reassess technical debt after Phase 2A (Phase 2B) - 482c474 docs(certification): close TD-1 after operational verification (Phase 2A.4) - ... -``` - -## 1. Repository Health Summary - -| Area | Status | Evidence | -| -------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Testing | **Healthy** | Full suite re-run this phase: 138 test files passed, 15 skipped (all confirmed legitimately environment-gated — Supabase config, live-vendor credentials, ML-DSA-65 runtime support; zero unconditional/stale skips remain, TD-15 was the last one), 950 tests passed, 0 failed | -| Documentation | **Needs Attention** | Core architecture docs remain accurate; but this phase found the canonical Technical Debt Register (`repository-certification.md`) had silently drifted from reality — TD-2 and TD-15 were both closed in prior phases but the register still showed TD-2 "Open" and never listed TD-15 at all; ten more items (TD-9–TD-14, TD-16–TD-18) existed only in a separate report, never merged. Corrected this phase (§9) | -| Developer Experience | **Needs Attention** | `check-dist-fresh.ts`, fail-closed config errors, and now a repaired CI guard are strong positives; but 9 unconditional debug `console.log` statements sit in the system's single most central method (`ExecutionTrustApplication.execute()`) and its HTTP entry point, newly found this phase (TD-19) | -| Build | **Healthy** | `tsc -b` clean, re-run this phase | -| CI | **Healthy** | terminology guard now passes (TD-2 closed, Phase 2C); lint, typecheck, build all clean; TD-3 (no coverage enforcement) remains the one open CI-quality gap | -| Bootstrap | **Needs Attention** | The real composition chain (`createExecutionControl.ts`/`createConnectorRegistry.ts`/`createExecutionGateway.ts`) is correct and already extensively verified (Phases 1H, 2A–2D); but a second, entirely dead composition root (`ExecutionControlComposition.ts`, `ConnectorFactory.ts`) still sits alongside it with zero references from anywhere, confirmed still present a month after `GAP-AUDIT.md` first flagged it (TD-20, newly registered this phase) | -| SDK quality | **Needs Attention** | Python SDK generation confirmed in sync in `GAP-AUDIT.md` and not re-broken; TypeScript client SDK still has 5 of 9 test files as empty stubs (TD-10), unchanged since Phase 2B | - -No area is classified **High Risk**. - -## 2. Engineering Debt Inventory (Task 1) - -Repository-wide sweep, independent of any prior report's own list: - -``` -TODO|FIXME|XXX|HACK in packages/*/src → 3 files (all previously known: TD-11, TD-17 x2) -describe.skip|it.skip|test.skip repo-wide → 24 files, every one individually verified - legitimately conditional (skipIf on Supabase - config / live-vendor credentials / ML-DSA-65 - runtime support) — zero unconditional skips -it.todo|test.todo → 0 matches anywhere -console.log( in packages/*/src → 10 matches; 1 legitimate (server.ts startup - banner), 9 debug instrumentation (TD-19, new) -Dead/unreferenced composition roots → ExecutionControlComposition.ts / ConnectorFactory.ts, - zero references (TD-20, new — matches GAP-AUDIT G-13) -Stale documentation claims → docs/CLAIMS.md:835 (TD-21, new registration — - previously verbally flagged in Phase 2A/2A.1 - work but never corrected or tracked) -``` - -## 3. Independent Verification Results (Task 2) - -Every item Phase 2B previously catalogued (TD-9–TD-14, TD-16–TD-18) was re-checked against current source, not copied: - -| Item | Verification method | Result | -| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| TD-9 (ExecutionRequestBuilder duplication) | Read the file directly | **Still Exists**, unchanged | -| TD-10 (typescript SDK stubs) | `wc -l` on all 9 files | **Still Exists**, unchanged (5/9 still 0 lines) | -| TD-11 (ExecutionEvidenceComponent dead no-op) | `grep -rln` for the class name | **Still Exists**, unchanged | -| TD-12 (OverrideService/OverrideVerifier unreachable) | `grep -rln` from `packages/api/src` | **Still Exists**, unchanged | -| TD-13 (Replay unreachable from `/replay`) | Read `routes/replay.ts` **and** `ExecutionTrustApplication.replay()` directly | **Still Exists, but Misclassified in its prior framing** — the route is not missing/orphaned (it returns 200), it implements something materially different (re-verification) than what `@parmana/replay`'s actual machinery or the route's own name imply. Corrected in the register (§9) | -| TD-14 (LedgerSerializer bug) | Read the file directly | **Still Exists**, unchanged, confirmed still dead code | -| TD-16 (ReceiptCrypto composition-root bypass) | Read `ReceiptCrypto.ts`/`SettlementConfirmationCrypto.ts`, confirmed `ReceiptService` still constructs it directly | **Still Exists**, unchanged | -| TD-17 (misleading bootstrap TODOs) | Read both files' TODO comments | **Still Exists**, unchanged | -| TD-18 (transactions.ts pagination) | Read the route's own comment | **Still Exists**, unchanged | - -No item was found **Already Resolved**, **Duplicate**, or **Obsolete** among Phase 2B's list — every one of its still-open items independently re-verified as still present. Two items Phase 2B itself closed (TD-2, TD-15) were confirmed genuinely closed (§ Precondition Verification and Task 9), but had not been reflected in the canonical register — a documentation-currency gap, not a technical one. - -## 4. Test Coverage Assessment (Task 3) - -Every skipped or placeholder test in the repository, individually classified (no restoration or deletion performed): - -| Test file(s) | Skip condition | Classification | -| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `*-live.integration.test.ts` (Razorpay, HubSpot) | `skipIf` on real vendor credentials | **Justified** — correctly excluded from CI (no secrets in CI), by design | -| `supabase-*.integration.test.ts`, `receipt-negative`, `receipt-signature`, `trust-record-get`, `trust-record-lifecycle`, `verification-negative`, `workflow-negative`, `workflow-supabase`, `transactions-api.test.ts` (13 files) | `skipIf(!databaseConfigured)` | **Justified** — correctly excluded when no real Postgres/Supabase is configured | -| `dilithium3-*.test.ts` (4 files) | `skipIf(!isMlDsa65Supported())` | **Justified** — correctly excluded on runtimes without ML-DSA-65 support | -| `execution-failure.integration.test.ts` | Was unconditional `describe.skip`, stale justification | **Already restored** (Phase 2D, TD-15) — no longer skipped, both tests pass | -| `typescript/test/{HealthApi,ParmanaClient,PolicyApi,ReplayApi,VerificationApi}.test.ts` | Not skipped — empty file bodies (0 lines), collected as "0 tests, passed" | **Should be redesigned** (written) — these aren't placeholders with a skip marker, they're literally empty files; `vitest run` reports them as trivially passing, which is a different and arguably more concealed gap than a `.skip` (no red flag anywhere signals the absence) | - -**No unconditional/stale test skip remains anywhere in the repository** — the one that existed was TD-15, closed in the immediately preceding phase. The empty TypeScript SDK stub files (TD-10) are the only remaining placeholder-test-shaped gap, and they were already correctly identified as such by Phase 2B; this phase adds no new instance. - -## 5. Bootstrap Assessment (Task 4) - -- **The real, live composition chain** (`createExecutionSystem.ts` → `createExecutionGateway.ts` → `createExecutionControl.ts` → `createConnectorRegistry.ts`) was extensively re-verified across Phases 1H and 2A–2D and is not re-litigated here. -- **A second, dead composition root exists and was confirmed still present**: `ExecutionControlComposition.ts` (64 lines) and `ConnectorFactory.ts` (36 lines), zero references from any other file in the repository (`grep -rn` for both class/file names, filtered to exclude self-matches, returns nothing). This exactly matches `docs/investigations/GAP-AUDIT.md`'s G-13 finding from 2026-07-07 ("Two composition roots for Execution Control exist... Reading the wrong one as source of truth risks real confusion for new contributors or auditors") — confirmed still true, unaddressed, and never previously added to the TD-numbered register. Registered this phase as **TD-20**. -- **The one previously-known empty bootstrap file** (`createVendorPaymentSecureConnector.ts`, TD-7) was the only other bootstrap-directory anomaly found; no new empty or broken bootstrap file exists among the 35 files in `packages/api/src/bootstrap/`. - -## 6. Documentation Assessment (Task 4) - -- **`docs/CLAIMS.md:835`** still describes `create-connector-registry.test.ts` as asserting "vendor-payment remains resolvable when razorpay is not" — accurate before Phase 2A, false since (that exact test was changed by Phase 2A to assert the opposite, fail-closed behavior, outside `NODE_ENV=test`). This was verbally noted during Phase 2A/2A.1's own investigation but never corrected or formally tracked. Registered this phase as **TD-21**. -- **The canonical Technical Debt Register itself was found stale** — the most significant documentation finding of this phase (§9): TD-2 showed "Open" despite being closed in Phase 2C; TD-15 didn't exist in it at all despite being closed in Phase 2D; ten further items (TD-9–TD-14, TD-16–TD-18) existed only in `phase2b-technical-debt-assessment.md`, a separate report, never merged into the one document meant to be authoritative. This is not new _engineering_ debt, but it is exactly the kind of "documentation inconsistency" Task 1 asked this phase to search for, and arguably the highest-value finding in this pass precisely because it undermines confidence in every other status claim in the register until corrected. -- No other stale comment, misleading TODO, outdated example, or obsolete guidance was found beyond what's already catalogued in TD-4 through TD-21. - -## 7. Risk Matrix (Task 5) - -| ID | Dev Productivity | Maintainability | Architectural Risk | Operational Risk | Regression Risk | Implementation Complexity | Business Impact | **Overall** | -| --------------------------------------------------------- | --------------------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | -------------- | -| TD-13 (`/replay` doesn't replay) | Low | Medium (misleading naming) | Low | Low | Low | Medium (needs a design decision: wire in real machinery, or rename/redocument) | Medium (a documented capability, `CLAIMS.md` 2.7, functions differently than described) | **Medium** | -| TD-16 (ReceiptCrypto bypass) | Low | Medium | Medium (silent future divergence) | Low today, would become Medium-High post-KMS-migration | Low today | Medium (real seam design work) | Low today, time-bombed | **Medium** | -| TD-3 (no CI coverage gate) | Medium | Medium | None | Medium (silent regressions) | N/A | Low | Low-Medium | **Medium** | -| TD-19 (debug console.log) | Medium (log noise complicates real debugging) | Low | None | Low (no secrets logged, just noise) | None | Trivial | Low | **Low** | -| TD-20 (dead composition root) | Medium (confusion risk for new contributors) | Medium | None (confirmed zero references, no live risk) | None | None (dead code, nothing to break) | Trivial (delete 2 files, already confirmed safe) | Low | **Low** | -| TD-9 (ExecutionRequestBuilder duplication) | Low | Medium (drift-guard absent on a trust-boundary-feeding function) | Low | Low | Low (prevents a _future_ regression class, doesn't fix a live one) | Trivial (one-line change) | Low | **Low** | -| TD-10 (SDK empty stubs) | Medium (published SDK, no safety net) | Medium | None | Low | N/A | Medium (session-sized) | Medium (external consumers) | **Low-Medium** | -| TD-21 (CLAIMS.md stale claim) | Low | Low | None | None | None | Trivial | Low | **Low** | -| TD-4, TD-5, TD-6, TD-8, TD-11, TD-12, TD-14, TD-17, TD-18 | Low | Low | None | None | None | Trivial–Low | Low | **Low** | - -No item reaches **Critical** or **High** — consistent with Phase 2B's own finding that the highest-severity items (TD-1, TD-2) are both now closed. - -## 8. Dependency Graph (Task 6) - -``` -Independent, parallelizable (no shared files, no ordering constraint): - TD-3 (ci.yml coverage step) - TD-4, TD-5, TD-6, TD-21 (documentation-only edits, different files each) - TD-7, TD-20 (delete dead files, already confirmed zero references) - TD-9 (ExecutionRequestBuilder.ts, one line) - TD-19 (execute.ts + ExecutionTrustApplication.ts, delete-only) - -Needs a decision before implementation (not blocked, just not yet scoped): - TD-13 → wire in @parmana/replay for real, or rename/redocument the route — - a product/design choice, not an engineering one - TD-16 → premature before roadmap Move 1 (KMS/HSM) is scheduled; sequencing - dependency on unscheduled future work, not on anything in this repo today - TD-11 → finish ExecutionEvidenceComponent or delete it — same "decide intent - first" shape as TD-13 - TD-12 → wire in Override machinery or leave parked — product decision - -Session-sized, independent of everything else: - TD-10 (typescript SDK test coverage) - TD-8 (before any future standalone package extraction, not urgent now) - -No item in this list blocks any other. Nothing here has a prerequisite among -Phase 2A–2D's own closed work. -``` - -## 9. Technical Debt Register Update - -Applied directly to `docs/architecture/repository-certification.md` §10 this phase (not merely recommended): - -- **TD-2**: Status corrected from "Open" to **Closed** (2026-08-05, Phase 2C), with a dated closure-note addendum. The original finding/description/rationale text is preserved unedited, per this repository's established convention (TD-1's own closure precedent). -- **TD-15**: **Added** to the canonical register for the first time (it previously existed only in `phase2b-technical-debt-assessment.md`), already marked **Closed** (2026-08-05, Phase 2D). -- **TD-9 through TD-14 and TD-16 through TD-18**: merged into the canonical register for the first time, each independently re-verified this phase (§3) rather than copied verbatim. **TD-13's description was corrected** (not just copied) to reflect the more precise finding this phase made: the route exists and returns 200, it does not silently fail — a materially different, more accurate characterization than the prior "unreachable from `/replay`" framing. -- **TD-19, TD-20, TD-21**: newly registered this phase, each with direct repository evidence (§2, §5, §6). -- **No item's severity was changed without evidence.** TD-4 through TD-8's original Low/Medium ratings, and TD-9 through TD-18's ratings as scored in Phase 2B, were independently re-derived this phase (§7) and found to still hold, except TD-13's overall risk read stayed **Medium** but its rationale was sharpened. - -## 10. Evidence Summary - -- Direct source reads: `ExecutionRequestBuilder.ts`, `ExecutionEvidenceComponent.ts`, `ExecutionTrustApplication.ts` (both the `replay()` method and the 7 debug `console.log` call sites), `routes/replay.ts`, `routes/execute.ts`, `LedgerSerializer.ts`, `ReceiptCrypto.ts`, `SettlementConfirmationCrypto.ts`, `createGatewayIdentity.ts`, `createSessionStore.ts`, `ExecutionControlComposition.ts`, `ConnectorFactory.ts`, `docs/CLAIMS.md`. -- Repository-wide greps re-run fresh this phase: `TODO|FIXME|XXX|HACK`, `describe.skip|it.skip|test.skip`, `it.todo|test.todo`, `console.log(`, class/file-name reachability for `OverrideService`/`OverrideVerifier`/`ReplayExecutor`/`ReplayPipeline`/`ExecutionControlComposition`/`ConnectorFactory`, and the exact terminology-guard command (confirming TD-2's continued closure). -- `git log`/`git blame`: confirmed TD-19's debug statements were introduced 2026-07-18 (`a6b9d8b`), independent of any Phase 1–2 work, and predate every prior audit that could have caught them. -- Build/test evidence: `npx tsc -b`, `npm run lint`, `npm test -- --maxWorkers=2` all independently re-run this phase against commit `752be4c` — clean (138 files / 950 tests passed, 15 files / 39 tests correctly skipped, 0 failed). -- Register-consistency evidence: `tests/architecture/documentation-references.test.ts` and `tests/architecture/terminology-guard.test.ts` both re-run after this phase's register edits — 54 tests, all passing, confirming the edits introduced no broken reference and no terminology-guard regression. - ---- - -## Final Recommendation - -**BEGIN NEXT IMPLEMENTATION PHASE.** - -**Target: TD-9 — `ExecutionRequestBuilder.build()` should call the shared `toExecutableContent()` helper instead of manually re-deriving the same four fields.** - -**Why this outranks every other remaining item:** - -- **Highest value-to-cost ratio among everything scored in §7.** The fix is a one-line change (call the existing, already-shared helper `RuntimeEngine.ts` already uses for the identical four-field extraction) with zero behavior change today (both derivations already produce identical values — confirmed, not assumed) and zero design surface: no product decision, no sequencing dependency, no investigation required. -- **It closes a drift-guard gap on a trust-boundary-feeding function**, not merely a cosmetic one. `ExecutionRequestBuilder`'s output feeds directly into what `ExecutionGateway` verifies and executes — two independent code paths deriving the same four fields with nothing to prevent future divergence is precisely the class of latent risk this entire multi-phase program (TD-1 onward) has been about: not "is it broken today" but "is there a structural reason it can't quietly become wrong later." TD-9 is the cheapest, most directly analogous instance of that pattern still open. -- **Every comparably-scored alternative requires either a decision this phase correctly declines to make unilaterally, or has an unscheduled prerequisite:** TD-13 and TD-11/TD-12 (all Medium-or-decision-gated) need a product call on intent (wire something in vs. rename/delete) before any code should be written; TD-16 is explicitly premature until roadmap Move 1 is scheduled; TD-10 is session-sized (writing real SDK tests), a reasonable but _larger_ undertaking than a single next-phase item should be; TD-3 (coverage gate) needs a threshold-policy decision, not just a YAML edit. -- **TD-19 and TD-20 are close seconds**, both trivial and low-risk (delete debug logs; delete dead files), but neither closes a guard against _future_ incorrectness the way TD-9 does — they clean up existing noise/confusion rather than removing a structural opportunity for drift. TD-9 is marginally the higher-value pick because its absence is a mechanism that could actively cause a future bug, not just present-tense untidiness. - -No higher-severity alternative exists: TD-13 and TD-16, the only two open items scored Medium, are both correctly identified as needing a scoping decision or an external prerequisite before implementation is appropriate, not as items this assessment is deprioritizing without cause. diff --git a/docs/architecture/phase2f-executable-content-canonicalization.md b/docs/architecture/phase2f-executable-content-canonicalization.md deleted file mode 100644 index 796dd380..00000000 --- a/docs/architecture/phase2f-executable-content-canonicalization.md +++ /dev/null @@ -1,131 +0,0 @@ -# Phase 2F — Eliminate ExecutionRequestBuilder Content Duplication (TD-9) - -Closes TD-9 (`docs/architecture/repository-certification.md`, Technical Debt Register), the item Phase 2E's independent reassessment recommended as the next implementation target. This is an implementation phase scoped to one class and its test coverage — no architectural or runtime redesign, no production behavior change. - -**Fixed against:** commit `7a309b3` (`docs(architecture): reassess engineering technical debt after Phase 2D`), the tip of `main`. Working tree was clean before this phase began. - ---- - -## 1. Original Duplication - -`packages/runtime/src/ExecutionRequestBuilder.ts`, before this phase, manually re-listed the same four fields `toExecutableContent()` (`packages/shared/src/domain/executable-content.ts`) already derives: - -```ts -return { - businessTransactionId: transaction.businessTransactionId, - action: transaction.intent.action, - target: transaction.intent.target, - parameters: transaction.intent.parameters, - authorization, -}; -``` - -## 2. Independent Verification - -Treated Phase 2E's finding as a hypothesis, not a fact. Read both files directly rather than trusting the prior report's summary ("no drift today, both derive identical values"). - -**The duplication was confirmed still present** — exactly as described. But independent verification also found the prior framing incomplete: **the two paths had already diverged in behavior, not just in code shape.** `toExecutableContent()` does two things beyond field selection that the manual code did not: - -1. **Validates** `target` is a non-empty string, throwing `"ExecutableContent.target must be a non-empty string."` if not. -2. **Freezes** the returned object and produces a **fresh copy** of `parameters` (`Object.freeze({ ...input.parameters })`) rather than passing the original object through by reference. - -Per this phase's own instruction ("If any difference exists: STOP. Document it before changing code"), both differences were traced to their actual runtime consequence before any code was touched: - -- **Validation:** `RuntimeEngine.execute()` already calls `toExecutableContent()` with the exact same `businessTransactionId`/`action`/`target`/`parameters` values (`RuntimeEngine.ts:326-332`, sourced from `transaction.businessTransactionId`/`transaction.intent.*`) _before_ `ExecutionComponent`/`ExecutionRequestBuilder` ever run — and `context.transaction` (what reaches `ExecutionRequestBuilder.build()`) is a shallow copy that never reassigns `intent` (confirmed: `RuntimeEngine.ts:380-385` only overrides `status`; `TrustChainValidationComponent` and `RuntimePipeline` were both confirmed, via direct grep, to never reassign `context.transaction`). **Any transaction that reaches `ExecutionRequestBuilder` has therefore already passed this exact validation, on these exact values, moments earlier in the same request.** Adding the same check a second time cannot newly reject anything that previously succeeded. -- **Freezing/copying:** confirmed no downstream code mutates `.parameters` on an `ExecutionRequest` (`grep` for `.parameters =` / `.parameters[` across `runtime`, `execution-gateway`, `execution-control`, `connector-sdk`, `connector-hubspot` — zero matches) and confirmed `ExecutionComponent.execute()` passes the built `request` straight to `this.executionSystem.execute(request)` with nothing in between that could depend on mutating it. Freezing earlier is also consistent with, not a new constraint against, the existing architecture — `ExecutionGateway` already `deepFreeze`s the verified content downstream regardless. - -**No consumer was found that intentionally relies on the unfrozen, reference-sharing behavior of the old manual construction.** - -## 3. Canonical Helper - -`toExecutableContent()` (`packages/shared/src/domain/executable-content.ts`) is the single, pre-existing canonical implementation — unchanged by this phase. It was already the sole implementation `RuntimeEngine.execute()` (`RuntimeEngine.ts:326`) and `ExecutionGateway.verify()` (`ExecutionGateway.ts:149`) used; `ExecutionRequestBuilder` was the one production consumer in `packages/runtime` still bypassing it. - -**Task 1 duplication audit — every executable-content-shaped construction site in the repository, classified:** - -| Location | Classification | -| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `packages/shared/src/domain/executable-content.ts` (`toExecutableContent`) | **Canonical** | -| `packages/runtime/src/RuntimeEngine.ts:326` | Canonical call site | -| `packages/execution-gateway/src/ExecutionGateway.ts:149` | Canonical call site | -| `packages/runtime/src/ExecutionRequestBuilder.ts` | **Was Duplicate — fixed this phase** | -| `packages/execution-gateway/src/connector-execution/SdkConnectorExecutor.ts` | Legacy/pass-through — forwards already-frozen, already-validated fields into a differently-shaped `ConnectorRequest`/`ExecutionResult`; does not re-derive or re-validate anything, not a duplicate of the same logic | -| `packages/connector-sdk/src/connectors/razorpay/RazorpaySignalStateVerifier.ts`, `packages/connector-hubspot/src/HubSpotSignalStateVerifier.ts`, `packages/api/src/webhooks/RazorpaySettlementProcessor.ts` | Not applicable — construct _derived_, differently-scoped request content (synthesized `businessTransactionId` suffixes, different `action`/`target` for a state-fetch or settlement-fetch capability), not a re-derivation of the original transaction's executable content | -| **`packages/connector-sdk/src/connectors/razorpay/RazorpayCapabilityExecution.ts` (`executeRazorpayCapability`)** | **Duplicate — same anti-pattern, out of this phase's scope** | -| **`packages/connector-hubspot/src/HubSpotCapabilityExecution.ts` (`executeHubSpotCapability`)** | **Duplicate — same anti-pattern, out of this phase's scope** | - -**New finding, not fixed in this phase:** `executeRazorpayCapability`/`executeHubSpotCapability` each reimplement the _same_ freeze-and-copy logic inline (`Object.freeze({ ...content, parameters: Object.freeze({ ...content.parameters }) })`) rather than calling `toExecutableContent()` — and each has an internal inconsistency of its own: the frozen, copied `executableContent` is used only for authorization signing (line ~45-48 in both files), while the actual `gateway.execute(...)` call a few lines later (line ~63-69) uses the raw, unfrozen `content.*` fields directly. This is the identical class of bug TD-9 fixed, in two different files, in a different package boundary (`connector-sdk`/`connector-hubspot`, not `runtime`). **Out of scope for this phase** — Task 3 scoped the fix specifically to `ExecutionRequestBuilder`, and touching these two files would cross into signal-state-verification and Razorpay/HubSpot-refund authorization code this phase's Preserve list and narrow Objective do not cover. Flagged here for a future, separately-scoped phase. - -## 4. Migration Performed - -`ExecutionRequestBuilder.build()` now delegates entirely: - -```ts -return { - ...toExecutableContent({ - businessTransactionId: transaction.businessTransactionId, - action: transaction.intent.action, - target: transaction.intent.target, - parameters: transaction.intent.parameters, - }), - - authorization, -}; -``` - -No logic was copied out of `toExecutableContent()` into `ExecutionRequestBuilder`, and no logic was duplicated elsewhere — the helper remains the sole implementation. `ExecutionRequest`'s four shared fields (`businessTransactionId`, `action`, `target`, `parameters`, all `readonly`) are structurally identical to `ExecutableContent`'s (confirmed by reading `packages/execution-system/src/ExecutionRequest.ts` directly), so spreading the helper's return value plus `authorization` produces a valid `ExecutionRequest` with no type-level friction — confirmed by a clean `tsc -b`. - -## 5. Regression Protection - -`packages/runtime/tests/unit/execution-request-builder.test.ts` (new, 5 tests): - -1. **Source-level delegation proof** — asserts the class's own source text contains `toExecutableContent(`, so a future revert to manual field-listing fails this test even if it happened to still produce matching values. -2. **Byte-for-byte equivalence** — builds a request and independently calls `toExecutableContent()` with the same inputs; asserts every shared field matches. -3. **Immutability inheritance** — asserts `request.parameters` is frozen and is a _different object reference_ than `transaction.intent.parameters`. Only genuine delegation produces this; the old manual pass-through could not. -4. **Validation inheritance** — asserts building a request from a transaction with an empty `target` throws the exact `toExecutableContent()` validation error, not silent acceptance. -5. **Lockstep with `RuntimeEngine`'s own derivation** — asserts the builder's output matches what `toExecutableContent()` produces for the same transaction `RuntimeEngine.execute()` would have used to build the signed `ExecutableContent` — directly encoding the property TD-9 existed to protect (what was authorized is exactly what gets executed). - -**Mutation-testing validation performed:** reverted `ExecutionRequestBuilder.ts` to its exact pre-Phase-2F content (`git stash`), re-ran the new test file — **3 of 5 tests failed** (delegation-proof, immutability-inheritance, validation-inheritance), exactly the three that depend on genuine delegation rather than coincidental value matching. Restored the fix (`git stash pop`), re-ran — all 5 pass again. This directly satisfies "duplicate mapping cannot silently reappear": if it did, these tests would catch it. - -## Static Search Confirmation (Task 5) - -Re-searched `packages/runtime/src` for every `businessTransactionId:` occurrence after the fix: `ExecutionRequestBuilder.ts` is now the only one that also constructs `action`/`target`/`parameters` together and delegates to `toExecutableContent()`. Every other match is either a function parameter type declaration or a genuinely different object (`BusinessTrustRecordBuilder`, `ExecutionBuilder`, `ExecutionEvidenceBuilder`, `RefusalRecordBuilder` build differently-shaped records; `RuntimeEngine.ts`'s other three matches build a signal-verification context, an authorization-signing payload referencing the already-built `executableContent`, and an error-log object respectively — none re-derive `ExecutableContent`). **Exactly one production implementation of executable-content construction remains reachable from the runtime package's request-building path.** - -## Regression - -Independently re-run against the fixed tree: - -``` -npx tsc -b → clean, 0 errors -npm run lint → clean -npm run typecheck → clean -npm test -- --maxWorkers=2 → 139 test files passed (+1), 15 skipped (unchanged); - 955 tests passed (+5), 39 skipped, 0 failed -``` - -Broader regression scope also independently re-run: the full `packages/runtime` suite (13 files, 37 tests) and the full `packages/api` + `packages/execution-gateway` suites (56 files, 289 tests, 33 correctly skipped) — the latter specifically because `ExecutionGateway.verify()` recomputes and compares a content hash against what was signed, which would be the first place a genuine value divergence (as opposed to a mutability/reference change) would surface as a failure. All clean. - -`git diff --stat` confirms exactly two files changed: `packages/runtime/src/ExecutionRequestBuilder.ts` (the fix) and the new test file. No other production source was touched. - -## 6. Remaining Limitations - -- **Two duplicate sites of the identical anti-pattern remain**, in a different package boundary: `executeRazorpayCapability` (`packages/connector-sdk`) and `executeHubSpotCapability` (`packages/connector-hubspot`) — see §3. Both reimplement `toExecutableContent()`'s freeze-and-copy logic inline instead of calling it, and both have an internal inconsistency where the frozen copy is used for signing but the raw, unfrozen fields are used for the actual gateway call. Not fixed here — out of this phase's explicitly scoped Objective (`ExecutionRequestBuilder` specifically) and touches a different package boundary and authorization-signing code path than this phase's Preserve list covers. Recommended as the natural next candidate for a dedicated, narrowly-scoped follow-up phase. -- **The `ExecutionRequest` object itself remains unfrozen** (only its nested `.parameters` is now frozen, inherited from `toExecutableContent()`) — spreading a frozen object into a new object literal to add the `authorization` field necessarily produces an unfrozen outer object. This is unchanged from before this phase (the old manual construction never froze the outer object either), so it is not a regression, but it means the immutability guarantee this fix adds is partial, not total, for this specific type. Not addressed here, as fully freezing `ExecutionRequest` would be a small scope expansion beyond "delegate to the existing helper" and wasn't reported as a problem by any downstream consumer. - ---- - -## Final Verification - -| Item | Status | -| ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| TD-9 independently verified | ✓ — duplication confirmed present; two additional behavioral differences (validation, freezing) found and traced beyond Phase 2E's original "values match" framing | -| Duplicate implementation removed | ✓ — `ExecutionRequestBuilder.build()` now delegates entirely | -| Exactly one executable-content implementation remains | ✓ — confirmed by static search (Task 5); two similar, out-of-scope duplicates in a different package documented, not silently ignored | -| Runtime behavior unchanged | ✓ — traced why validation can never newly reject a request that reaches this point, and why freezing/copying `.parameters` has no downstream consumer that depends on mutating it | -| Security behavior unchanged | ✓ — no authorization, signal-verification, replay-protection, audit, or credential-handling code touched; `git diff --stat` confirms | -| No production semantics changed | ✓ — full regression suite green, including the content-hash comparison in `ExecutionGateway.verify()` that would be the first place a real value divergence would surface | - -## Final Recommendation - -**TD-9 CLOSED.** - -The duplication is removed, not papered over: `ExecutionRequestBuilder` now has zero independent field-derivation logic and delegates completely to the same helper `RuntimeEngine.execute()` already uses to build the `ExecutableContent` an authorization is signed over — the two paths that were structurally free to diverge can no longer diverge, because there is only one path. Behavioral equivalence was demonstrated, not assumed: two real differences (validation, freeze/copy semantics) were found during independent verification, each traced to its actual runtime consequence and confirmed safe before any code changed, exactly as this phase's instructions required. Regression protection is demonstrated, not just asserted — the new tests were confirmed to actually fail against the prior implementation via mutation testing, not merely written to pass against the new one. Full regression suite (955 passed, 39 correctly skipped, 0 failed) and static search both confirm exactly one production implementation remains in scope for this phase's Objective. diff --git a/docs/architecture/phase2g-replay-semantics.md b/docs/architecture/phase2g-replay-semantics.md deleted file mode 100644 index e0b92455..00000000 --- a/docs/architecture/phase2g-replay-semantics.md +++ /dev/null @@ -1,224 +0,0 @@ -# Phase 2G — Resolve Replay Endpoint Semantics (TD-13) - -Independently re-verifies TD-13 (`docs/architecture/repository-certification.md`, Technical Debt Register) against current source, determines whether `POST /replay`'s current behavior is intentional or a bug, and either restores canonical replay semantics or documents why that would be a production API break. Treated Phase 2E's conclusions as hypotheses, not facts. - -**Fixed against:** commit `ef2cbab` (`refactor(runtime): canonicalize executable content construction`), the tip of `main`. Working tree had one uncommitted, unrelated whitespace-only change to `.github/workflows/ci.yml` at the start of this phase (adds leading indentation to `name: CI`); confirmed unrelated to Phase 2F or any replay work (no matching content in the diff, no stash, no history tying it to this line of work) and discarded (`git restore`) before proceeding, per user confirmation. - ---- - -## 1. Original TD-13 Wording - -From `docs/architecture/repository-certification.md`: - -> `POST /replay` exists and returns 200, but `ExecutionTrustApplication.replay()` does not invoke `@parmana/replay`'s `ReplayExecutor`/`ReplayPipeline` at all — it re-fetches the Trust Record and re-verifies its hash/signature, functionally duplicating `/verify`. `@parmana/replay`'s own "deterministic reconstruction" machinery remains entirely unreachable from any route. **Not a security or trust-fabrication issue** (nothing is misrepresented as more-verified than it is), but a claims-precision gap. **Next phase touching `/replay`: either wire in the real package, or correct the route's documented behavior/name to match what it actually does.** - -Rated **Medium**, classified **Open**. - -## 2. Independent Verification Methodology - -Read source directly rather than trusting Phase 2E's summary: - -- `packages/api/src/routes/replay.ts` (the HTTP route) -- `packages/runtime/src/ExecutionTrustApplication.ts` (`replay()`, `verify()`) -- `packages/runtime/src/services/verification-service.ts` (`VerificationService.verify()`) -- `packages/crypto/src/VerificationCrypto.ts` (`verify()`, `verifySignature()`, `hash()`) -- Every file under `packages/replay/src/` (`ReplayEngine`, `ReplayBuilder`, `ReplayVerifier`, `ReplayPipeline`, `ReplayExecutor`, `ReplayContext`) -- `examples/tutorials/06-replay/run.ts` (the only place `ReplayEngine` is actually invoked) -- Repo-wide grep for `@parmana/replay` (17 hits, all docs/package metadata/the tutorial — zero in `packages/api` or `packages/runtime`) -- Repo-wide grep for `.replay(`/`replay(` across `packages/**/*.ts` (8 call sites — see Task 6 below) -- `docs/CLAIMS.md` §2.7, §4 (Future Claims), `docs/GUARANTEES.md` G-08, `docs/VERIFICATION-GAPS.md` G-10 -- `docs/site/reference/replay.mdx`, `docs/site/replay/overview.mdx` (existing disambiguation pages) -- `schemas/requests/replay-request.schema.json`, `schemas/responses/replay-response.schema.json`, `openapi/openapi.yaml` (`/replay` path) -- Both published SDKs: `typescript/src/client/ReplayApi.ts`, `typescript/src/models/replay-result.ts`, `python/parmana/api/replay_api.py`, `python/parmana/models/replay_result.py`, and their tests (`python/tests/test_replay_api.py`; `typescript/test/ReplayApi.test.ts` — confirmed empty, a pre-existing TD-10 gap) -- `packages/api/tests/integration/replay.integration.test.ts`, `caller-scoping.integration.test.ts`, `caller-auth.integration.test.ts` -- `git log --follow` on the schema, mdx, and route files, to establish when the current documentation and contract were actually written, not assume they were backfilled by this or the prior phase - -**TD-13's factual premise was confirmed exactly as described**: the route exists, returns 200, and `ExecutionTrustApplication.replay()` performs a signature/hash recheck rather than invoking `@parmana/replay`. No part of that finding was overturned. What this phase adds is the piece TD-13 explicitly left open — **whether that behavior is intentional** — and the answer changes the recommended action from "fix it" to "leave it, for a specific, evidenced reason." - -## 3. Actual Replay Behavior - -`ExecutionTrustApplication.replay()` (`packages/runtime/src/ExecutionTrustApplication.ts:131-160`): - -```ts -async replay(businessTransactionId: string): Promise<{ - businessTransactionId: string; - trustRecordHash: string; - verified: boolean; -}> { - const trustRecord = await this.trustRecords.findByTransactionId(businessTransactionId); - if (!trustRecord) throw new VerificationFailedError("Execution Trust Record not found."); - const verified = await this.crypto.verify(trustRecord); - return { businessTransactionId, trustRecordHash: trustRecord.trustRecordHash, verified }; -} -``` - -`this.crypto` is `VerificationCrypto` (`packages/crypto/src/VerificationCrypto.ts`). Its `verify()` recomputes the canonical hash and compares it to `trustRecordHash`, then verifies the stored Ed25519/ML-DSA-65 signature against the stored public key. It does **not** run the authorization-binding check (every `APPROVED` execution must carry a non-empty `authorizationId`) that `VerificationService.verify()` runs as its third check, and it does **not** persist a `Verification` record via `appendVerification()` the way `verify()` does — confirmed by the new unit test's "does not persist a Verification record" case (§5 below). Replay is therefore not merely "the same as verify" — it is a **strict subset** of verify's checks, with no persistence side effect. - -No re-execution, no policy re-evaluation, and no interaction with `Runtime`, `RuntimeEngine`, `ExecutionGateway`, `@parmana/replay`, or any nonce/replay-protection store happens anywhere in this path. - -## 4. Repository Evidence for Intended Behavior - -This is the crux of the phase. Evidence splits into two categories that must not be conflated: what "Replay" as a _concept_ is guaranteed to mean somewhere in the repository, and what `POST /replay` as a _contracted HTTP endpoint_ is documented and consumed as meaning today. - -**What "Replay" the concept is guaranteed to mean** — `docs/GUARANTEES.md`, G-08 (Deterministic Replay): - -> Replay re-evaluates historical execution without modifying historical evidence. Replay is intended to detect meaningful differences between recorded and replayed execution. - -Its evidence citation is "Replay package" — i.e. `@parmana/replay`, not `ExecutionTrustApplication.replay()`. `packages/replay/src/ReplayEngine.ts` matches this guarantee exactly: it re-runs `PolicyEngine.evaluate()` against a trust record's recorded signals and reports `matches: recordedDecision.outcome === replayedDecision.outcome` — genuine re-evaluation, proven deterministic regardless of input order (`replay-determinism.test.ts`). `CLAIMS.md` §2.7 ("Parmana supports replay of recorded execution decisions for verification and analysis") cites the same evidence ("Replay package", "G-08") — again the package, not the route. `docs/VERIFICATION-GAPS.md` G-10 independently flags this citation as _vague_ (no test file named) but explicitly confirms **"none of these claims are false; the underlying capability is real"** — a prior, independent audit pass already checked this and did not find a misrepresentation. - -**What `POST /replay` is documented and consumed as meaning** — four independent sources, none written by this phase or by Phase 2E: - -1. **`schemas/responses/replay-response.schema.json`** (unchanged since commit `4740aee`, 2026-07-08 — predates Phase 2E by roughly a month): _"A deliberately small shape distinct from the Execution Trust Record it replays: it re-verifies the record's stored signature via VerificationCrypto and reports only whether that succeeded, alongside the hash it checked."_ This is not a post-hoc rationalization — it is the original schema description, written when the route was built. -2. **`openapi/openapi.yaml`** (`/replay` path, same provenance): _"Re-verifies the stored signature on the Execution Trust Record for businessTransactionId and returns its hash alongside the verification result. Does not re-run Policy evaluation or re-execute anything."_ -3. **`docs/site/reference/replay.mdx`** and **`docs/site/replay/overview.mdx`**: a dedicated disambiguation page, last substantively edited 2026-07-12 (commit `5690474`) — three weeks before Phase 2E's assessment (2026-08-05) and five weeks before this phase. Both pages state, in an ``/`` callout, that `POST /replay` and `@parmana/replay` are "two unconnected things," that the route is "narrower than 'replay' implies: it's a signature recheck with a `replay`-shaped response, not deterministic execution reconstruction," and that `@parmana/replay` is "not reachable through `POST /replay` or any other route today." The overview page's own text ("This corrects a previous version of this page") shows this documentation has already been through at least one prior accuracy pass, independent of this debt-cleanup arc. -4. **`docs/CLAIMS.md` §4, Future Claims (Pending Evidence)**: _"Replay semantically verifies every trust artifact."_ is listed here, explicitly **not** among Supported Claims. Wiring `@parmana/replay` into the route to make it perform real semantic re-evaluation is exactly the change that would be needed to promote this from a Future Claim to a Supported one — and the project's own claims-discipline process (`docs/site/trust-and-claims/claims-discipline.mdx`) treats that promotion as a deliberate, evidenced act, not an incidental side effect of a technical-debt cleanup phase. - -**Conclusion:** the repository's own evidence is internally consistent and unambiguous once both categories above are read together. There is no conflicting or missing evidence, no ambiguity requiring a STOP under Task 3's rubric. "Replay" as a _guaranteed capability_ (G-08) is honestly and narrowly scoped to the standalone `@parmana/replay` package. `POST /replay` as a _contracted HTTP endpoint_ is separately, correctly, and repeatedly documented as a signature recheck — and has been since before this debt-cleanup arc began. TD-13's own recommended second option — "correct the route's documented behavior/name to match what it actually does" — **was already done, independently, prior to this phase.** The remaining gap is not a documentation problem; it is that the route's _name_ ("replay") still collides with the package's _name_ for a different concept (§10, and the naming-collision note below). - -## 5. Implementation Changes - -**None to production code.** Per this phase's own gating instructions ("If any production consumer is found to depend on the current `/replay` behavior: STOP... Only proceed with Task 4 if no production consumer depends upon current semantics"), independent verification found production consumers with a hard, typed dependency on the current response shape: - -| Consumer | Dependency | Evidence | -| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -| TypeScript SDK | `ReplayApi.replay()` returns `ReplayResult` (`{businessTransactionId, trustRecordHash, verified}`); this is a published `@parmana/*` npm package's public compile-time contract | `typescript/src/client/ReplayApi.ts`, `typescript/src/models/replay-result.ts` | -| Python SDK | `ReplayApi.replay()` returns `ReplayResult` dataclass with the same three fields, and is asserted by a real, passing unit test | `python/parmana/api/replay_api.py`, `python/parmana/models/replay_result.py`, `python/tests/test_replay_api.py` (passes today) | -| OpenAPI contract | `/replay`'s `200` response is `$ref`'d to `replay-response.schema.json`, with a captured real example (`verified: true`, a real hash) | `openapi/openapi.yaml:1559-1574` | -| Server integration test | Asserts exactly today's shape and values end-to-end through `/execute` → `/replay` | `packages/api/tests/integration/replay.integration.test.ts:73-81` | - -Replacing `ExecutionTrustApplication.replay()`'s return value with `@parmana/replay`'s `ReplayResult` shape (`{recordedDecision, replayedDecision, matches, replayedAt}`) would be a breaking change to a documented, versioned, SDK-typed API response — exactly the production-API-compatibility issue this phase's instructions require stopping for, independent of whether the change would otherwise be "more correct." - -**Affected consumer(s):** the published TypeScript and Python SDKs' `ReplayApi`/`ReplayResult`, and any external caller of `POST /replay` relying on the `verified: boolean` field (the OpenAPI-documented, SDK-typed contract). - -**Observed dependency:** compile-time type contract in both SDKs; a passing Python unit test asserting the exact field names and types; a passing server integration test asserting the exact response values. - -**Behavioral impact of wiring in `@parmana/replay` instead:** the response would no longer contain `verified: boolean` or a comparable `trustRecordHash` field at the top level, breaking both SDKs' return type and any caller checking `.verified`; the semantics would change from "did the stored artifact's signature verify" to "did the policy decision reproduce," a different question with a different failure mode (e.g. a record could have `verified: true` today for cryptographic integrity but no longer have a directly analogous field once policy-outcome matching replaces it). - -**Recommended migration strategy (not performed in this phase):** introduce the real semantic replay capability under a new, additively-versioned surface — e.g. a distinct route (`POST /replay/decision` or similar) or an explicit API version — rather than mutating `POST /replay`'s existing response shape in place. This lets `@parmana/replay` be wired into the HTTP surface (closing the "genuinely unreachable" half of TD-13) without an SDK-breaking change, and gives `CLAIMS.md`'s Future Claim a concrete, evidenced promotion path. This recommendation is left for a future, explicitly-scoped phase — consistent with this phase's Preserve list and its instruction not to redesign replay or the runtime. - -Since no production consumer dependency blocks _leaving the current behavior as-is_, and repository evidence (§4) independently confirms that behavior is the documented, intentional contract, **Task 4's condition for restoration was not met.** No implementation changes were made to `ExecutionTrustApplication.replay()`, `packages/api/src/routes/replay.ts`, `VerificationCrypto`, or any file under `packages/replay/src/`. - -## 6. Replay Execution Call Graph - -``` -POST /replay - └─ packages/api/src/routes/replay.ts - ├─ isOwnedByCaller(application, businessTransactionId, req.callerId) [ownership/authorization gate] - │ └─ application.getTransaction(businessTransactionId) - └─ application.replay(businessTransactionId) - packages/runtime/src/ExecutionTrustApplication.ts:131 - ├─ this.trustRecords.findByTransactionId(businessTransactionId) [read-only] - └─ this.crypto.verify(trustRecord) - packages/crypto/src/VerificationCrypto.ts:139 - ├─ this.hash(trustRecord) → hash comparison against trustRecord.trustRecordHash - ├─ this.keys.getPublicKey(...) - └─ this.verifier.verify(canonicalRecord, signature.value, publicKey) - → { businessTransactionId, trustRecordHash, verified } [no persistence] -``` - -`@parmana/replay` (`ReplayEngine`, `ReplayPipeline`, `ReplayExecutor`, `ReplayBuilder`, `ReplayVerifier`, `ReplayContext`) does **not** appear anywhere in this graph. Its only production-code caller in the entire repository is `examples/tutorials/06-replay/run.ts`, a standalone tutorial script that builds a `Runtime` and a trust record directly and calls `new ReplayEngine().replay({trustRecord, transaction, policy})` itself — it never goes through the HTTP layer. - -## 7. Verify Execution Call Graph - -``` -POST /verify - └─ packages/api/src/routes/verify.ts - ├─ isOwnedByCaller(application, businessTransactionId, req.callerId) [ownership/authorization gate] - └─ application.verify(businessTransactionId) - packages/runtime/src/ExecutionTrustApplication.ts:109 - └─ this.verification.verify(businessTransactionId) - packages/runtime/src/services/verification-service.ts:36 - ├─ this.trustRecords.findByTransactionId(businessTransactionId) [read-only] - ├─ runChecks(trustRecord): - │ ├─ Integrity: this.crypto.hash(trustRecord) vs trustRecord.trustRecordHash - │ ├─ Signature: this.crypto.verifySignature(trustRecord) - │ └─ Authorization binding: every APPROVED execution has a non-empty authorizationId - └─ this.trustRecords.appendVerification(businessTransactionId, verification) [persists] - → Verification { verificationId, businessTransactionId, status, message, verifiedAt, trustRecordHash } -``` - -**Common execution:** both paths load the same Trust Record by id and both ultimately recompute the same canonical hash and verify the same signature (`verify`'s Integrity + Signature checks are behaviorally identical to what `replay`'s `crypto.verify()` does internally — confirmed by reading `VerificationCrypto.hash()`/`verifySignature()`/`verify()`, all three built from the same `canonicalRecord()` and the same `hasher`/`verifier` instances). - -**Divergent execution:** - -- `verify` additionally runs the authorization-binding check; `replay` does not. -- `verify` persists a `Verification` record via `appendVerification()`; `replay` performs no write of any kind (confirmed by the new unit test, §5). -- `verify`'s response is the full `Verification` domain object (with `verificationId`, `status`, `message`, `verifiedAt`); `replay`'s response is the deliberately smaller `{businessTransactionId, trustRecordHash, verified}` shape. -- `replay` is exposed as a separate, narrower boolean than `verify`'s richer `VerificationStatus` enum (`VERIFIED`/`FAILED`) with a human-readable per-check `message`. - -**Duplicated logic:** the hash-integrity and signature checks are effectively duplicated between the two paths (both call into `VerificationCrypto`'s hash/verify machinery against the same canonical record), which is the concrete basis for TD-13's "functionally duplicating /verify" finding — confirmed accurate. It is a subset-duplication, not a full one: `replay` cannot produce everything `verify` produces (no authorization-binding check, no persisted record). - -**Unreachable replay code:** `@parmana/replay`'s entire public surface (`ReplayEngine`, `ReplayPipeline`, `ReplayExecutor`, `ReplayBuilder`, `ReplayVerifier`, `ReplayContext`) — unreachable from either `/verify` or `/replay`. - -**Replay-only behavior:** none beyond the narrower response shape — every check `replay` performs, `verify` also performs. - -**Verify-only behavior:** the authorization-binding check and the persisted `Verification` audit trail. - -## 8. Regression Testing Performed - -**New:** `packages/runtime/tests/unit/execution-trust-application-replay.test.ts` (6 tests, constructs `ExecutionTrustApplication` directly with a real `VerificationService` and an in-memory repository, matching the conventions of `verification-service.test.ts`, so it runs without the Supabase gate that skips the existing integration test locally): - -1. Reports `verified: true` and the stored hash for a valid Trust Record, built via the real `BusinessTrustRecordBuilder` (genuine canonicalization/hashing/signing, not hand-rolled). -2. Deterministic — two calls against the same record produce identical results. -3. Does not persist a `Verification` record — the concrete, asserted divergence from `verify()`. -4. Cannot fabricate a valid result for a tampered hash — `verified: false`, and the response still reports the actual (tampered) stored hash rather than a substituted or omitted one. -5. Cannot fabricate a valid result for a tampered signature — `verified: false`. -6. Throws `VerificationFailedError` for an unknown transaction and creates no record as a side effect of the failure — cannot fabricate a Trust Record, and by construction (no write path anywhere in `replay()`) cannot fabricate a Receipt or vendor evidence either. - -**Existing, re-confirmed passing, unchanged:** - -- `packages/api/tests/integration/replay.integration.test.ts` — happy path and 404-for-unknown-transaction through the real HTTP route (Supabase-gated, skipped locally, unchanged). -- `packages/api/tests/integration/caller-scoping.integration.test.ts` — "blocks caller-b from POST /replay for caller-a's transaction" (authorization preserved). -- `packages/api/tests/integration/caller-auth.integration.test.ts` — `/replay` requires authentication like every other route (`it.each` list). -- `packages/runtime/tests/unit/verification-service.test.ts`, `verification-negative.test.ts` — `verify()` behavior, entirely untouched by this phase, still green. - -**Full regression run:** - -``` -npx tsc -b → clean, 0 errors -npm test -- --maxWorkers=2 → 140 test files passed (+1), 15 skipped (unchanged); - 961 tests passed (+6), 39 skipped, 0 failed -``` - -`git diff --stat` against the starting commit confirms exactly one new file (`packages/runtime/tests/unit/execution-trust-application-replay.test.ts`) and this documentation file — no production source under `packages/*/src/` was touched. - -## 9. Remaining Limitations - -- **`@parmana/replay` remains genuinely unreachable from any HTTP route.** This phase did not change that — see §5 for why (production API compatibility) and the recommended migration path (an additive new surface, not an in-place change). -- **Locally, `/replay`'s only end-to-end HTTP-level coverage (`replay.integration.test.ts`) is Supabase-gated and skipped without a configured database.** The new unit test (§8) closes the gap for `ExecutionTrustApplication.replay()`'s own logic without a database, but does not exercise the Express route, `isOwnedByCaller`, or error-handler mapping — those remain covered only by the gated integration tests and the (ungated) `caller-scoping`/`caller-auth` tests. -- **The TypeScript SDK's own test for this surface is an empty stub** (`typescript/test/ReplayApi.test.ts`, `typescript/test/fixtures/replay-result.ts` — both confirmed empty). This is the pre-existing, separately-tracked TD-10 gap ("5 of 9 test files remain empty stubs"), not something this phase closes — flagged here because it is directly relevant to how much this phase's production-consumer-dependency finding rests on the _type contract_ (real) versus a _dedicated test_ (missing on the TS side; present and passing on the Python side). -- **`CLAIMS.md` §2.7's citation remains vague** ("Replay package", "G-08", no test file named) — a pre-existing, independently-flagged (`VERIFICATION-GAPS.md` G-10) cosmetic gap, confirmed not to represent a false claim, not addressed by this phase. - -## 10. Naming Collision Note - -This repository already uses the word **"replay"** for a second, unrelated concept: the anti-replay **nonce-consumption** security mechanism in the execution-authorization pipeline (`packages/execution-gateway/src/ExecutionGateway.ts`'s `isSoleFailureNonceReplay`, `NonceAlreadyConsumedError`, `packages/envelope-verifier/src/NonceStore.ts`, `packages/storage/src/supabase/SupabaseNonceStore.ts`, `CLAIMS.md` §2.10/§2.21). That mechanism rejects a previously-consumed authorization nonce as a replayed (i.e. re-submitted) request — a completely different meaning of "replay" from either of the two examined in this phase (re-verifying a stored artifact, or re-evaluating a recorded decision). - -**The repository therefore now contains three distinct things sharing the word "replay":** - -1. Anti-replay nonce consumption (`ExecutionGateway`/`NonceStore`) — security control against request resubmission. -2. `POST /replay` (`ExecutionTrustApplication.replay()`) — a signature/hash recheck of a stored Trust Record. -3. `@parmana/replay` (`ReplayEngine`) — genuine deterministic re-evaluation of a recorded policy decision. - -None of these are confused with one another in code — each lives in its own package/module with no shared implementation — but the shared English word across three independently-real mechanisms is a legitimate terminology risk for anyone reading route names, package names, or `CLAIMS.md` prose without the full cross-reference this document (and the existing `docs/site/replay/overview.mdx`) provides. **Flagged for future consideration** (e.g. renaming `POST /replay` to something like `POST /trust-records/:id/reverify`, or renaming `@parmana/replay` to `@parmana/decision-replay`) — **no names were changed in this phase**, per its explicit scope. - ---- - -## Final Verification - -| Item | Status | -| ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| TD-13 independently re-verified | ✓ — factual premise (route exists, returns 200, duplicates a subset of `/verify`, `@parmana/replay` unreachable) confirmed exactly accurate | -| Replay semantics established from repository evidence | ✓ — two internally-consistent, non-conflicting readings found: G-08/`CLAIMS.md` scope "Replay" the guarantee to the standalone package; the schema/OpenAPI/mdx docs scope `POST /replay` the endpoint to a signature recheck, and have done so since before this debt-cleanup arc (`schemas/responses/replay-response.schema.json` since `4740aee`, 2026-07-08; `docs/site/replay/overview.mdx` since `5690474`, 2026-07-12) | -| Production consumer dependency checked | ✓ — found: TypeScript and Python published SDKs' `ReplayApi`/`ReplayResult` are typed against the current response shape; OpenAPI contract and a passing integration test assert it; STOP condition applied, no in-place breaking change made | -| Replay exercises canonical behavior, if restoration required | N/A — restoration not required; current behavior independently verified intentional and separately production-contracted | -| Verify semantics unchanged | ✓ — no file under `verification-service.ts`'s call graph touched; existing tests unchanged and green | -| Runtime behavior unchanged outside replay | ✓ — `git diff --stat` shows only a new test file and this document | -| Security behavior unchanged | ✓ — no authorization, signal-verification, replay-protection (nonce), audit, or credential-handling code touched | - -Supported by: repository searches (§2), source references (§3, §6, §7), execution call graphs (§6, §7), and regression tests (§8, 967 passing / 39 correctly skipped / 0 failed, `tsc -b` clean). - -## Final Recommendation - -**TD-13 CLOSED.** - -The repository has one clearly defined `POST /replay` behavior, supported by repository evidence predating this phase: it is a deliberately scoped signature/hash recheck of a stored Trust Record, documented as exactly that in its OpenAPI spec, its JSON Schema, its dedicated disambiguation page, and both published SDKs — not a bug masquerading as something narrower than its name implies. TD-13's own second recommended option ("correct the route's documented behavior/name to match what it actually does") was independently confirmed already satisfied, by documentation written weeks before this phase and before Phase 2E's assessment. The first recommended option ("wire in the real package") was independently evaluated and found to require a breaking change to a versioned, SDK-typed production API contract — triggering this phase's explicit STOP-and-document instruction rather than a silent implementation change. Regression coverage for the current, confirmed-intentional behavior was strengthened (6 new unit tests closing a gap the Supabase-gated integration test leaves open locally), without touching `verify()`, the runtime, or any security-relevant code path. The remaining architectural question — whether to someday expose `@parmana/replay`'s real semantic re-evaluation through a new, additively-versioned surface — is recorded as a concrete, evidenced recommendation (§5) for a future, separately-scoped phase, not left as an open ambiguity. diff --git a/docs/architecture/phase2h-debug-instrumentation.md b/docs/architecture/phase2h-debug-instrumentation.md deleted file mode 100644 index e3c8f2f5..00000000 --- a/docs/architecture/phase2h-debug-instrumentation.md +++ /dev/null @@ -1,155 +0,0 @@ -# Phase 2H — Remove Residual Debug Instrumentation (TD-19) - -Closes TD-19 (`docs/architecture/repository-certification.md`, Technical Debt Register), a Phase 2E finding. This is an implementation phase scoped to removing print-debugging statements left in the production execution path — no architectural, runtime, or observability redesign, no production behavior change. - -**Fixed against:** commit `dd4fee3` (`test(runtime): certify replay endpoint semantics and close TD-13`), the tip of `main`. Working tree was clean before this phase began. - ---- - -## 1. Original TD-19 Finding - -From `docs/architecture/repository-certification.md`: - -> 9 debug `console.log` statements sit in the core request/execution hot path, unconditional (no log-level or `NODE_ENV` gate), firing on every request: `"[ROUTE] before/after execute"` (2, `execute.ts`) and numbered `"[APP] 1"`–`"[APP] 7"` stage markers spanning the entire `ExecutionTrustApplication.execute()` method. Introduced 2026-07-18 (`a6b9d8b`, "Execution Integrity Platform v0.3"); never previously flagged by any prior audit (Phase 1H's certification, `GAP-AUDIT.md`, or Phase 2B's assessment). Debug instrumentation left behind after a development session; no functional or security impact, but noise in every production log line for the system's most central method. **Next touch to either file: delete all 9 (or replace with a real, level-gated logger if execution tracing is actually wanted).** - -Rated **Low**, classified **Open**. - -Also independently corroborated, incidentally, by `docs/architecture/phase2d-execution-failure-testing.md` (a prior phase, explicitly out of its own scope): "Two stray debug `console.log` statements were observed, incidentally, in production source during this phase's empirical verification... Neither affects correctness... Flagged here for visibility as an unrelated, minor cleanup opportunity, not addressed in this phase." - -## 2. Independent Verification - -Every finding below was re-derived from source, not copied from Phase 2E or Phase 2D. - -**Repository-wide search**, `console\.(log|debug)\(` across the full tree (excluding `node_modules`): 125 files matched. The overwhelming majority are `examples/tutorials/**/run.ts` (deliberately narrated CLI tutorials whose entire purpose is printing output to the terminal), `scripts/*.ts`/`.mjs` (CLI tooling: key generation, migrations, secret rotation — meant to print), SDK examples (`typescript/examples/**`, `python` has none since Python doesn't use `console`), and test helper files (`*-availability.ts`, integration tests logging response bodies for manual debugging of the test itself, not the product). None of these are "production execution path" in TD-19's sense — they are CLI-adjacent, example, or test-only code, correctly out of scope. - -**Narrowed to production `src/`** (`packages/*/src/**/*.ts`, `typescript/src/**/*.ts`): exactly **3 files, 10 statements** — confirmed by direct grep, not estimation: - -``` -packages/runtime/src/ExecutionTrustApplication.ts:68,73,78,83,88,94,102 (7) -packages/api/src/routes/execute.ts:108,115 (2) -packages/api/src/server.ts:84 (1) -``` - -`typescript/src/` had zero matches. - -This confirms TD-19's count of "9" exactly (7 + 2 — TD-19's own inventory correctly excluded `server.ts`'s line, which this phase also treats separately, see §3/§5). No debug statement was found beyond what TD-19 already identified, and none of TD-19's 9 turned out to be misclassified on closer reading. - -**Introduction commits** (`git log -S`, exact string search, not `git blame` on the current line — avoids attributing a later reformat as the origin): - -- `packages/runtime/src/ExecutionTrustApplication.ts`'s 7 lines and `packages/api/src/routes/execute.ts`'s 2 lines: **both introduced in the same commit**, `a6b9d8b` ("feat(platform): Execution Integrity Platform v0.3", 2026-07-18) — a large, multi-feature commit (caller auth, nonce store/replay protection, credential isolation, Supabase persistence, Dilithium3, production bootstrap/CI) whose message never mentions logging or tracing work. -- `packages/api/src/server.ts`'s 1 line: introduced separately, at the file's original creation, commit `9fe09ee` ("refactor: separate runtime library from API server", 2026-06-26) — three weeks before the other two, unrelated to the `a6b9d8b` debug-tracing pattern. - -**Direct diff inspection of `a6b9d8b`** (not just the resulting file) confirms these are genuine, unambiguous print-debugging artifacts, not a stylistic logging choice: - -- In `ExecutionTrustApplication.ts`, the commit's diff shows each `console.log("[APP] N - ...")` line **directly replacing** a pre-existing, meaningful section comment (`// Accept Business Transaction`, `// Execute Runtime`, `// Verification`, `// Receipt`, `// Load completed Trust Record`) at the exact same position. This is the signature of someone stepping through `execute()` to trace where a bug occurred, substituting comments for print statements, and never reverting before the commit landed. -- In `execute.ts`, the same commit's diff shows the two `console.log` lines inserted with **column-0 indentation**, breaking the surrounding block's consistent indentation — a strong, independent signal of an ad hoc, unreviewed edit rather than a deliberate code-style decision. - -**`server.ts`'s line, by contrast**, was present from the file's first commit as a single, one-time "the server is listening" message (`console.log("API running on http://localhost:3000")`, later parameterized to `${HOST}:${PORT}`) — the conventional pattern for an HTTP server announcing its bound address at boot. Nothing about its origin, shape, or frequency (once per process start, not once per request) resembles the other 9. - -**Whether any is intentional production observability:** the 9 `[APP]`/`[ROUTE]` lines have no log level, no structured fields, no request-correlation id, fire unconditionally on every single request through the system's most central method, and their own introducing commit message never describes them as an observability feature. `server.ts`'s line is a conventional, one-time startup diagnostic. These are treated as two structurally different things, not one class of 10. - -**Whether any automated test depends on it:** repository-wide search for the literal strings `[APP]` and `[ROUTE]` found exactly 5 files: the 2 production source files themselves, `docs/architecture/repository-certification.md` and `docs/architecture/phase2d-execution-failure-testing.md` (both prose describing the finding, not asserting behavior), and `docs/guides/e2e/03-credential-enforcement.md` (see §4 below). **Zero test files reference either string.** No test spies on, parses, or asserts against `console.log` output anywhere in `packages/runtime/tests` or `packages/api/tests` for the `execute` path. - -## 3. Logging Inventory - -| File | Class/Method | Line(s) | API | Message | Introduced | Classification | -| --------------------------------------------------- | ------------------------------------- | ------- | ------------- | --------------------------------------------- | ---------------------- | --------------------------------------------- | -| `packages/runtime/src/ExecutionTrustApplication.ts` | `ExecutionTrustApplication.execute()` | 68 | `console.log` | `"[APP] 1 - accept"` | `a6b9d8b` (2026-07-18) | Temporary debug / Development artifact | -| same | same | 73 | `console.log` | `"[APP] 2 - runtime"` | `a6b9d8b` | Temporary debug / Development artifact | -| same | same | 78 | `console.log` | `"[APP] 3 - verification"` | `a6b9d8b` | Temporary debug / Development artifact | -| same | same | 83 | `console.log` | `"[APP] 4 - receipt"` | `a6b9d8b` | Temporary debug / Development artifact | -| same | same | 88 | `console.log` | `"[APP] 5 - load trust record"` | `a6b9d8b` | Temporary debug / Development artifact | -| same | same | 94 | `console.log` | `"[APP] 6 - found trust record"` | `a6b9d8b` | Temporary debug / Development artifact | -| same | same | 102 | `console.log` | `"[APP] 7 - returning"` | `a6b9d8b` | Temporary debug / Development artifact | -| `packages/api/src/routes/execute.ts` | `createExecuteRouter()` route handler | 108 | `console.log` | `"[ROUTE] before execute"` | `a6b9d8b` (2026-07-18) | Temporary debug / Development artifact | -| same | same | 115 | `console.log` | `"[ROUTE] after execute"` | `a6b9d8b` | Temporary debug / Development artifact | -| `packages/api/src/server.ts` | module-level, server bootstrap | 84 | `console.log` | `` `API running on http://${HOST}:${PORT}` `` | `9fe09ee` (2026-06-26) | Production observability / Startup diagnostic | - -No statement in this inventory was classified as Structured operational logging, Test-only, or Legacy — the repository has no other logging in these files to classify. No statement was found outside these two commits or these three files within production `src/`. - -## 4. Sensitive Data Exposure Findings (Task 1.5) - -**None found.** Every one of the 10 statements was inspected for interpolated content: - -- The 7 `ExecutionTrustApplication.ts` lines and 2 `execute.ts` lines are all **static string literals** — no template literals, no interpolated variables, no reference to `transaction`, `trustRecord`, headers, credentials, or any request/response data. They print only a fixed label and a step number. -- `server.ts`'s line interpolates only `HOST` and `PORT`, both non-secret server bind configuration (and `HOST` defaults to `0.0.0.0`), never a credential, token, or user-supplied value. - -No credentials, API keys, tokens, signed authorization envelopes, signature material, customer PII, or vendor request/response bodies were ever logged by any of these 10 statements, at any point in their history (confirmed by reading the full diff of both introducing commits, not just the current file state). There is no historical exposure to disclose, no log-aggregation retention concern, and no credential rotation warranted from this phase's findings. This is stated explicitly, as a distinct check, per this phase's Task 1.5 — not folded silently into the removed/retained classification. - -## 5. Classification Rationale - -**Temporary debug / Development artifact (9 statements, `ExecutionTrustApplication.ts` ×7, `execute.ts` ×2):** - -- Introduced in the same commit, with no stated logging intent in that commit's message. -- Direct diff evidence shows they replaced meaningful section comments at the exact same code positions (`ExecutionTrustApplication.ts`) and were inserted with broken indentation (`execute.ts`) — both independent signals of an unreviewed, ad hoc edit rather than a deliberate logging feature. -- Unconditional (no `NODE_ENV`/log-level gate), firing on every request through the system's most central method — the profile of a bug trace left in, not a designed observability signal. -- No log level, no structured fields, no correlation id — nothing distinguishes them from `print` statements. -- Zero test dependency (§2). -- Not documented as supported observability in `CLAIMS.md`, `GUARANTEES.md`, `docs/architecture/**`, or any deployment/operations guide (`docs/ROADMAP-v1.md`, `docs/architecture/repository-invariants.md`, `docs/guides/e2e/19-deployment-guide.md`, `docs/guides/e2e/20-production-operations.md`, `docs/guides/e2e/21-troubleshooting.md` were all checked; none mention `[APP]` or `[ROUTE]`). - -**Production observability / Startup diagnostic (1 statement, `server.ts`):** - -- Fires exactly once per process start, not once per request — a fundamentally different frequency and purpose than the 9 above. -- Conventional pattern for an HTTP server (announcing its bound host/port), present since the file's creation, not introduced alongside the debug-tracing commit. -- Explicitly protected by this phase's own Preserve/Task 3 instructions ("Do NOT remove: ... startup diagnostics"). -- No sensitive content (§4). - -No statement required the STOP-condition path: none is consumed by production monitoring, parsed by operational tooling, required by deployment verification, referenced by a test, or documented as supported observability (the one documentation reference found, `docs/guides/e2e/03-credential-enforcement.md`, is addressed separately in §7 as it does not meet that bar — see rationale there). - -## 6. Removed Statements - -All 9 statements classified Temporary debug / Development artifact were removed: - -- `packages/runtime/src/ExecutionTrustApplication.ts`: all 7 `console.log("[APP] N - ...")` calls deleted. The section comments they had overwritten (`// Accept Business Transaction`, `// Execute Runtime`, `// Verification`, `// Receipt`, `// Load completed Trust Record`) were restored in their place — a faithful reversal of what the introducing commit's own diff shows was removed, not a new addition. No statement, branch, ordering, or awaited call was touched; every `await this.X.Y(...)` call and its arguments are byte-for-byte unchanged. -- `packages/api/src/routes/execute.ts`: both `console.log("[ROUTE] before/after execute")` calls deleted, and the surrounding block's indentation (broken by the original ad hoc edit, see §2) restored to match the rest of the function. The `application.execute(transaction)` call and `res.json(result)` line are otherwise unchanged. - -`git diff --stat` confirms only these two files changed in production source, plus this documentation file. - -## 7. Retained Statements - -- `packages/api/src/server.ts:84` — the startup diagnostic (`API running on http://${HOST}:${PORT}`) — retained unchanged, per §5's classification and this phase's explicit Preserve instruction. -- **`docs/guides/e2e/03-credential-enforcement.md`** quotes the exact text `[ROUTE] before execute` / `[APP] 1 - accept` / `[APP] 2 - runtime` in a "Runtime Progress" section, presented as captured terminal output from a manual end-to-end test session, with the narrative "This confirms the transaction entered the Runtime Engine and progressed into the execution pipeline." This is the one documentation reference this phase's Task 2 usage audit found. It does not meet any STOP condition: it is not a claim of supported observability (no `GUARANTEES.md`/`CLAIMS.md` entry references it), not parsed by any tooling, and not asserted by any test — it is a point-in-time captured transcript in an internal walkthrough series (`docs/guides/e2e/`, a manual step-by-step session log, distinct from the published `docs/site/` documentation), the same category of artifact TD-19's own register entry recommended deleting regardless. **Not edited in this phase** — updating narrative documentation to match new log output is outside this phase's explicitly non-observability, non-documentation-redesign scope, and doing so wasn't requested. Recorded here, and again in §9, as a known, harmless side effect: that guide's captured console-output block is now stale (it will no longer literally reproduce against current code) and is a candidate for a future, separately-scoped documentation pass. - -## 8. Regression Validation - -**New tests:** none required. Task 5's usage audit found no test anywhere asserting on `console.log` output for `/execute` or `ExecutionTrustApplication.execute()` (confirmed by search across `packages/runtime/tests` and `packages/api/tests` for `console.log`/`console.debug`/`spyOn(console`/the literal strings `[APP]`/`[ROUTE]` — the only hits were tests' own unrelated diagnostic `console.log`/`console.dir` calls for manual triage of test failures, not assertions against product output). No test needed updating or weakening. - -**Full regression run, independently re-executed against the fixed tree:** - -``` -npx tsc -b → clean, 0 errors -npm test -- --maxWorkers=2 → 140 test files passed, 15 skipped (unchanged from before this phase); - 961 tests passed, 39 skipped, 0 failed (identical counts to before this phase) -``` - -Identical pass/skip counts before and after this phase's changes is itself evidence of no behavioral change: removing print statements that only ever wrote to stdout, with no branch, ordering, return-value, or error-handling changes, could not plausibly alter any test's outcome, and the run confirms it did not. - -`git status`/`git diff --stat` confirm exactly two production files changed (`packages/api/src/routes/execute.ts`, `packages/runtime/src/ExecutionTrustApplication.ts`) plus this new documentation file. No test file, schema, OpenAPI spec, SDK, or any other production source was touched. - -## 9. Remaining Limitations - -- **`docs/guides/e2e/03-credential-enforcement.md`'s captured "Runtime Progress" transcript is now stale** — it quotes exact debug output that no longer exists in the running system (§7). Not fixed here (documentation-narrative update is outside this phase's scope); flagged as a candidate for a future documentation pass. -- **No level-gated tracing was added in place of the removed statements.** TD-19's own recommendation offered two options ("delete all 9... or replace with a real, level-gated logger if execution tracing is actually wanted"); this phase took the deletion option, since no repository evidence was found that execution tracing at this granularity is an actual product requirement (no `GUARANTEES.md`/`CLAIMS.md` entry, no operations guide, no monitoring dashboard reference names or parses this output — see §5). If request-level execution tracing is wanted going forward, it remains an open, separately-scoped feature request, not something this phase inferred a need for. -- **`server.ts`'s single `console.log` remains unstructured** (plain string, not JSON, no log level) — unchanged from before this phase, consistent with its classification as a conventional one-time startup message and out of this phase's non-observability-redesign scope. - ---- - -## Final Verification - -| Item | Status | -| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| TD-19 independently verified | ✓ — all 9 flagged statements confirmed present, correctly classified, and correctly attributed to their introducing commit (`a6b9d8b`); no additional debug statements found beyond TD-19's original count | -| Every debug statement checked for sensitive data exposure | ✓ — all 10 (including the retained `server.ts` line) inspected for interpolated content; none found (§4) | -| Temporary debug instrumentation removed | ✓ — all 9 `[APP]`/`[ROUTE]` statements deleted from `ExecutionTrustApplication.ts` and `execute.ts`; meaningful section comments restored where the debug lines had overwritten them | -| Production observability preserved | ✓ — `server.ts`'s startup diagnostic untouched | -| Runtime behavior unchanged | ✓ — no branch, ordering, awaited call, or return value changed; only `console.log` calls and comments touched | -| Security behavior unchanged | ✓ — no authorization, signal-verification, replay-protection, audit, or credential-handling code touched | -| Execution architecture unchanged | ✓ — no class, method signature, or call graph changed | - -Supported by: repository searches (§2), source references and diff inspection of both introducing commits (§2, §3), and regression tests (§8: `tsc -b` clean; 961 tests passed, 39 correctly skipped, 0 failed — identical counts before and after). - -## Final Recommendation - -**TD-19 CLOSED.** - -No sensitive data exposure was found (§4) — there is no separate, higher-priority item to escalate. All 9 statements independently confirmed as temporary debug instrumentation (unconditional, structurally identical to print-debugging, traced to a single development commit whose own diff shows them overwriting pre-existing meaningful comments) were removed, with the overwritten comments restored. The 1 statement independently confirmed as intentional production observability (`server.ts`'s startup diagnostic) was preserved untouched, exactly as this phase's Preserve list required. Zero tests depended on the removed output; none needed updating. Full regression suite is unchanged in every count (961 passed, 39 skipped, 0 failed, `tsc -b` clean) before and after, demonstrating removal of these statements had no effect beyond eliminating unconditional per-request console noise from the system's central execution path. diff --git a/docs/architecture/phase2i-composition-root-cleanup.md b/docs/architecture/phase2i-composition-root-cleanup.md deleted file mode 100644 index 69322f23..00000000 --- a/docs/architecture/phase2i-composition-root-cleanup.md +++ /dev/null @@ -1,186 +0,0 @@ -# Phase 2I — Remove Dead Composition Root (TD-20) - -Closes TD-20 (`docs/architecture/repository-certification.md`, Technical Debt Register), a Phase 2E finding. This is an implementation phase scoped to removing an unreachable, abandoned composition root — no architectural, runtime, or dependency-injection redesign, no production behavior change. - -**Fixed against:** commit `4d45385` (`fix(runtime): remove residual debug instrumentation and close TD-19`), the tip of `main`. Working tree was not clean at the start of this phase — Phase 2H's changes were still uncommitted; per user confirmation, they were committed first (`4d45385`) before this phase's own gate check was re-run and passed. - ---- - -## 1. Original TD-20 Finding - -From `docs/architecture/repository-certification.md`: - -> A second, entirely unused "composition root" for Execution Control exists alongside the real one: `ExecutionControlComposition.ts` and `ConnectorFactory.ts` (100 lines combined) have zero references from any other file in the repository. Matches `docs/investigations/GAP-AUDIT.md`'s G-13 finding (2026-07-07) precisely, still unaddressed a month later and never previously added to this register. Confusion risk for a new contributor or auditor who reads the wrong file as source of truth (the real chain is `createExecutionControl.ts`/`createConnectorRegistry.ts`) — zero functional risk since nothing constructs or imports either dead class. **Any future cleanup pass: delete both (already confirmed zero references; low-risk removal).** - -Rated **Low**, classified **Open**. - -## 2. Independent Verification Methodology - -Read source and history directly rather than trusting Phase 2E's summary: - -- Both target files: `packages/api/src/bootstrap/ExecutionControlComposition.ts`, `packages/api/src/bootstrap/ConnectorFactory.ts` -- Every other file in `packages/api/src/bootstrap/` (30 files total), to place the two targets in context against the real composition chain -- The real chain, read end to end: `packages/api/src/server.ts` → `createExecutionSystem.ts` → `createExecutionGateway.ts` → `createExecutionControl.ts` → `createConnectorRegistry.ts`/`createSessionStore.ts`/`createExecutionAuditSink.ts`/`createGatewayIdentity.ts` -- `packages/api/src/application.ts`, `packages/api/src/app.ts`, `packages/api/tests/test-app.ts`, `packages/api/tests/bootstrap/createInspectableExecutionSystem.ts` -- `packages/connector-sdk/src/ConnectorFactory.ts` and `packages/connector-sdk/src/index.ts` — the same-named but unrelated interface, to confirm it is never conflated with the dead class -- Repo-wide grep for `ExecutionControlComposition`, `ConnectorFactory`, `new ExecutionControlComposition`, `new ConnectorFactory(`, `composition root`, `factory`, across all `.ts`, `.md`, `.mdx`, `.json` files (excluding `node_modules`, `dist`, `coverage`) -- `git log -S`/`git log --follow`/`git show` on both target files, `createExecutionControl.ts`, `docs/ROADMAP-v1.md`, and `docs/investigations/GAP-AUDIT.md`, to establish actual introduction order and rule out an assumed "pre-refactor path" narrative not supported by the commit history -- `packages/api/package.json` (exports/files fields), `packages/api/src` for any `index.ts` barrel, and every other package's `package.json` for a dependency on `@parmana/api` -- `docs/CLAIMS.md`, `docs/GUARANTEES.md`, `docs/site/reference/api.mdx`, `docs/site/integrations/overview.mdx`, `docs/site/reference/connector-sdk.mdx` -- `tests/architecture/execution-boundary.test.ts` (the Phase 1E/1F architecture-enforcement suite), read in full and re-run before and after removal - -**TD-20's factual premise was confirmed exactly as described**: both files exist, both are entirely unreferenced by anything else in the repository, and the real chain is `createExecutionControl.ts`/`createConnectorRegistry.ts`. No part of that finding was overturned. This phase's independent verification found one citation-provenance error worth correcting (below), confirmed the "zero references" claim through git history (not just current-tree grep), and confirmed no STOP condition applies. - -**Citation-provenance correction, found independently:** TD-20's register entry and Phase 2E's own assessment both attribute this finding to _"`docs/investigations/GAP-AUDIT.md`'s G-13 finding (2026-07-07)."_ Direct inspection of `docs/investigations/GAP-AUDIT.md` (both its current content and its original commit, `82b4db8`, dated 2026-07-07) shows it **never mentions `ExecutionControlComposition`, `ConnectorFactory`, "composition root," "G-13," or anything resembling this finding**, at any point in its history. The actual source of "G-13" is `docs/ROADMAP-v1.md` §4 ("Technical debt, cross-referenced against VERIFICATION-GAPS.md and CLAIMS.md"), which explicitly numbers its own new findings "continuing from G-11" and lists **G-13** there: _"Two composition roots for Execution Control exist (`ExecutionControlComposition`/`ConnectorFactory`, dead, vs. `createExecutionControl.ts`, real)."_ `docs/ROADMAP-v1.md` was itself introduced in commit `503d48f`, dated **2026-07-13** — six days after the date TD-20 attributes the finding to. This is a citation-trail error (Phase 2E's assessment appears to have inherited an incorrect attribution rather than checking `GAP-AUDIT.md` directly), not a factual error about the composition root itself: the underlying finding (two composition roots, one dead) is independently confirmed true regardless of which document first raised it. Noted here per this phase's instruction not to rely on Phase 2E's own citations without fresh verification. - -## 3. Historical Audit - -**When introduced:** `git log -S "class ExecutionControlComposition"`/`--follow` on both target files resolves to a single commit, `ddf4bc5` ("Stabilize runtime, verification, storage, and replay pipeline", **2026-07-09**). The same commit's diff (`git show ddf4bc5 --stat`) shows it introduced **both** competing approaches side by side in one changeset: `ExecutionControlComposition.ts`, `ConnectorFactory.ts`, and `ConnectorCatalog.ts` (the class-based approach) alongside `createConnectorAuthenticator.ts`, `createConnectorRegistry.ts`, `createConnectorRoute.ts`, `createCredentialProvider.ts`, `createExecutionAuditSink.ts`, `createExecutionControl.ts` (an early version), `createGatewayIdentity.ts`, `createSessionStore.ts`, and others (the functional `create*.ts` approach that became canonical). - -**This is a more precise finding than "a pre-refactor path later replaced by a refactor."** The two approaches were not sequential — they were committed together, in the same changeset, as apparent parallel exploration of the same responsibility. What happened next, confirmed independently: - -- **Two days later**, commit `806e9e6` ("feat: implement credential isolation and secure enterprise connectors", **2026-07-11**) extended `createExecutionControl.ts` specifically — adding `GatewayAttestationSigner`, `createGatewayKeyPair()`, registration-time attestation, and wrapping the result in `SessionCredentialExecutionControl` (confirmed by reading the current file, `packages/api/src/bootstrap/createExecutionControl.ts:1-88`). This is materially more sophisticated than `ExecutionControlComposition.build()`, which constructs a bare `ExecutionControlService` with no session-credential wrapping, no attestation signer, and no gateway key pair. -- **`ExecutionControlComposition.ts` and `ConnectorFactory.ts` were never touched again in any subsequent commit** (`git log --follow` on both returns exactly one commit each — the introducing one). -- **`new ExecutionControlComposition` and `new ConnectorFactory(` have never appeared anywhere in the entire git history of the repository** (`git log --all -S` for both strings, across all files, all commits — zero results). This is stronger evidence than a current-tree grep: it confirms these classes were never instantiated even transiently, in any commit that was later reverted or refactored away. They were dead on arrival. - -**Whether a later phase intentionally superseded them:** no. "Supersession" implies the dead path was once live and was deliberately replaced. That did not happen here — the functional path was chosen and developed within the same week the class-based one was introduced, and the class-based one simply stopped receiving commits. The correct characterization is **abandoned-at-introduction**, not **superseded-after-use**. - -**Relationship to Phases 1A–1E — direct answer to this task's cross-reference question:** `ExecutionControlComposition.ts`/`ConnectorFactory.ts` do **not** represent "the pre-refactor connector construction path" in the sense of a path that once ran in production and was later replaced by a refactor (that would require evidence of live use followed by supersession, which the history above shows never happened). What they do represent is a **parallel, never-activated construction path for the same responsibility** that the canonical `ExecutionGateway`-based architecture (`createExecutionControl.ts` → `createExecutionGateway.ts` → `ExecutionGateway`) fulfills instead. **Their removal is independent evidence supporting the Phase 1E single-execution-pipeline invariant, not merely dead-code reduction**: `tests/architecture/execution-boundary.test.ts`'s own stated purpose is to verify "there is exactly one production execution pipeline... and nothing outside execution-gateway owns production execution" (file header, lines 7-10). That two composition roots for the same responsibility have coexisted in this repository since 2026-07-09 without the invariant ever being violated — because the second one was never wired to anything, confirmed across the _entire_ commit history, not just today's tree — is direct, historical, non-trivial confirmation that the single-pipeline architecture has held continuously, not just as of the current snapshot. Removing the dead root converts "the invariant held because the alternative was never activated" into "the invariant holds because the alternative no longer exists to be activated" — a strictly stronger guarantee. - -## 4. Composition Root Inventory - -| File(s) | Package | Responsibility | Runtime entry | Classification | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -| `server.ts` → `createExecutionSystem.ts` → `createExecutionGateway.ts` → `createExecutionControl.ts` → `createConnectorRegistry.ts`/`createSessionStore.ts`/`createExecutionAuditSink.ts`/`createGatewayIdentity.ts`/`createGatewayKeyPair.ts`/`createConnectorRoute.ts` | `@parmana/api` | Production wiring of the entire `ExecutionSystem`/`ExecutionControl`/`ExecutionGateway` subsystem | `node dist/server.js` / `npm run dev` (`tsx src/server.ts`) | **Canonical composition root** | -| `packages/runtime/src/RuntimeFactory.ts` | `@parmana/runtime` | Constructs `ExecutionTrustApplication` from repositories, policy repository, and an already-built `ExecutionSystem` | called by `packages/api/src/application.ts`'s `createApplication()` | **Canonical composition root** | -| `packages/api/tests/test-app.ts` | `@parmana/api` (test) | Mirrors the production chain above (`createExecutionSystem()` → `createApplication()` → `createApp()`) for supertest-driven integration tests | `packages/api/tests/integration/*.test.ts` | **Test composition root** | -| `packages/api/tests/bootstrap/createInspectableExecutionSystem.ts` | `@parmana/api` (test) | A DI-seam variant of `createExecutionSystem()` for white-box test assertions (restored/used in Phase 2D's `execution-failure.integration.test.ts`) | `packages/api/tests/integration/execution-failure.integration.test.ts` | **Test composition root** | -| `packages/runtime/src/RuntimeBuilder.ts` + each `examples/tutorials/*/run.ts` | `@parmana/runtime` + `examples` | Standalone, no-HTTP-layer wiring for tutorial scripts | `npx tsx examples/tutorials/*/run.ts` | **Demo composition root** | -| `packages/api/src/bootstrap/ExecutionControlComposition.ts` + `ConnectorFactory.ts` | `@parmana/api` | An alternate, class-based composition for the identical `ExecutionControl` responsibility `createExecutionControl.ts` fulfills | none — zero callers in the current tree and zero callers anywhere in git history | **Dead composition root — removed this phase** | -| `packages/api/src/bootstrap/ConnectorCatalog.ts` | `@parmana/api` | A standalone connector-enablement list, self-contained, no dependency on either the dead or canonical root | none — zero callers anywhere (confirmed by grep), already flagged by `docs/site/reference/api.mdx`'s existing Warning | **Dead, but out of TD-20's named scope — not removed this phase** (see §9) | - -## 5. Reachability Analysis - -Every check this phase's Task 2 required, run directly against current source and history: - -- **Imported:** no — repo-wide grep for `ExecutionControlComposition` and `ConnectorFactory` (the `packages/api/src/bootstrap/` one specifically, distinguished from `packages/connector-sdk/src/ConnectorFactory.ts`'s same-named interface) found the two target files importing only each other, and nothing else in the repository importing either. -- **Instantiated:** no — `git log --all -S "new ExecutionControlComposition"` and `-S "new ConnectorFactory("` both return zero commits across the entire repository history. -- **Referenced:** only by each other, and by prose in `docs/architecture/phase2e-engineering-debt-assessment.md`, `docs/architecture/repository-certification.md`, and `docs/ROADMAP-v1.md` — all describing them as dead, none presenting them as live. -- **Exported:** no. `packages/api/package.json` has no `"exports"` field, no `"main"`, and `"private": true` — this package is never published to npm regardless of its contents. No `index.ts` barrel exists anywhere in `packages/api/src` or `packages/api/src/bootstrap`. No other package's `package.json` lists `@parmana/api` as a dependency (confirmed by grep across every `packages/*/package.json`). -- **Dynamically resolved:** no reflection, no dynamic `import()`, no configuration-driven class lookup found anywhere referencing either class name. -- **Reachable through runtime:** no — traced end to end from `server.ts` through every `create*.ts` call in the real chain; neither dead file appears. -- **Reachable through tests:** no — `packages/api/tests/test-app.ts` and `packages/api/tests/bootstrap/createInspectableExecutionSystem.ts` both use `createExecutionSystem()`, not either dead file. Repo-wide grep for both class names restricted to `*.test.ts` files returned zero matches. -- **Reachable through examples:** no — `examples/tutorials/**/run.ts` construct `Runtime` via `RuntimeBuilder`, never the API package's bootstrap files at all. -- **Reachable through documentation:** referenced only as a documented dead-code finding (see below), never as supported architecture. -- **Reachable through CI:** `.github/workflows/ci.yml` runs `tsc -b`/`npm test`, neither of which references either file by name; both ran clean after removal (§8). -- **Generated declarations:** `packages/api/dist/bootstrap/{ExecutionControlComposition,ConnectorFactory}.d.ts` existed as stale, gitignored build output (confirmed `packages/api/dist/` is listed in `.gitignore:11`, never tracked by git) and have been removed as part of this phase's cleanup (§8); nothing referenced them. - -**Public package surface — explicitly checked, no STOP triggered.** Neither file appears in `package.json` `exports`, any `index.ts` barrel, or any published npm surface. `@parmana/api` is `"private": true` with no `main`/`exports` field, and no other workspace package depends on it. This is categorically different from `packages/connector-sdk/src/ConnectorFactory.ts` (the unrelated same-named interface), which **is** re-exported through `packages/connector-sdk/src/index.ts`'s public barrel — confirmed untouched by this phase, and confirmed structurally distinct by import path, file location, and kind (`export interface ConnectorFactory` vs. `export class ConnectorFactory`). - -**Phase 1E/1F architecture enforcement tests — checked for negative-fixture reliance.** `tests/architecture/execution-boundary.test.ts`'s `"bootstrap composes but never executes business actions"` block (lines 230-240) scans **every** file under `packages/api/src/bootstrap/` generically via `it.each(bootstrapFiles)`, not a fixed file list — both dead files were incidentally swept into this scan before removal (and passed trivially, since neither calls `.execute(`). This is not a negative fixture the test relies on; it is a directory walk that happens to include whatever files exist. Confirmed by re-running the suite after removal (§8): the test still passes, with two fewer generated cases, and the invariant it enforces ("no bootstrap file calls `.execute(`") remains fully checked against every file that actually exists. No test needed strengthening — the scan-based design is inherently robust to files disappearing. - -**Documentation presenting these as supported architecture — none found.** `docs/site/reference/api.mdx`'s "Structure" table (the page's canonical file-by-file description of `@parmana/api`) already lists only the real chain (`createExecutionSystem`, `createExecutionGateway`, `createExecutionControl`, `createConnectorRegistry`, etc.) — neither dead file ever appeared there. The same page's existing `` block already called `ConnectorFactory.ts` "dead code, not wired into anything" (`ConnectorCatalog.ts` alongside it) — accurate, pre-existing, unprompted by this phase. It did not previously mention `ExecutionControlComposition.ts` at all. Since the warning described real, still-existing dead files, not "supported architecture," Task 2's update-or-justify branch did not strictly require a change — but leaving a public docs page describing a file that number longer exists as "dead code... lists connectors" would itself become stale and inaccurate the moment this phase's removal lands. **Updated** (§9) to remove the now-deleted `ConnectorFactory.ts` from that warning and added a short note pointing to this phase's documentation, while leaving `ConnectorCatalog.ts`'s entry (still true, still out of scope) untouched. - -## 6. Dependency Graph - -``` -ExecutionControlComposition.ts - ├─ createGatewayIdentity() → also used by createExecutionControl.ts, createExecutionGateway.ts (Independently reachable) - ├─ createConnectorAuthenticator() → also used by createExecutionControl.ts (Independently reachable) - ├─ InMemoryGatewaySessionStore → @parmana/execution-control; also constructed by createSessionStore.ts's own path (Independently reachable) - ├─ MemoryExecutionAuditSink → @parmana/execution-control; also constructed by createExecutionAuditSink.ts (Independently reachable) - ├─ InMemoryConnectorRegistry → @parmana/execution-control; also used by createConnectorRegistry.ts and execution-control/execution-gateway's own test suites (Independently reachable) - └─ ExecutionControlService → @parmana/execution-control; also constructed by createExecutionControl.ts, with a different (session-credential-wrapped) configuration (Independently reachable) - -ConnectorFactory.ts - └─ ExecutionControlComposition → the sibling dead file above; no other consumer - (createAll() returns [] — constructs nothing itself) -``` - -Every class and function these two files reference is **Still reachable** through the canonical `createExecutionControl.ts`/`createConnectorRegistry.ts`/`createSessionStore.ts`/`createExecutionAuditSink.ts`/`createGatewayIdentity.ts` chain, confirmed by grep showing each imported and used by files other than the two being removed (`packages/api/src/bootstrap/createConnectorRegistry.ts`, `createConnectorAuthenticator.ts`, `createSessionStore.ts`, `createGatewayIdentity.ts`, `createExecutionAuditSink.ts`, plus `execution-control`'s and `execution-gateway`'s own unit tests). **Zero files become Newly orphaned.** `ConnectorCatalog.ts` is **Independently reachable-or-not** on its own terms — it was already unreferenced before this phase and does not depend on, or get depended on by, either removed file, so its dead status is not a consequence of this removal and its removability was not "independently demonstrated" as required before touching it (Task 3) — left untouched, flagged in §9. - -## 7. Relationship to Phases 1A–1E - -Covered in full in §3. Summary: the dead composition root was never a Phase 1A–1E-era production path later replaced by refactor — it was an abandoned parallel approach from the moment of its introduction (`ddf4bc5`, 2026-07-09), alongside the functional approach that Phase 1A–1E's architecture-enforcement tests (`tests/architecture/execution-boundary.test.ts`) verify as the sole production pipeline today. Its removal is additional, historically-grounded evidence that the single-execution-pipeline invariant has held continuously since that commit, not merely dead-code reduction. - -## 8. Files Removed - -- `packages/api/src/bootstrap/ExecutionControlComposition.ts` (65 lines) -- `packages/api/src/bootstrap/ConnectorFactory.ts` (37 lines) -- Stale, gitignored build output for both (`packages/api/dist/bootstrap/{ExecutionControlComposition,ConnectorFactory}.{js,js.map,d.ts,d.ts.map}`) — never git-tracked, removed for local cleanliness only; `tsc -b --clean` followed by a fresh `tsc -b` did not automatically prune these (a known TypeScript composite-build limitation for deleted inputs), so they were deleted directly. No git-tracked file was affected by this step. - -No other file was removed. `ConnectorCatalog.ts` — independently dead but not a dependency of either removed file, and not named in TD-20 — was left in place (§9). - -## 9. Public API Verification - -- `packages/api/package.json`: no `exports` field, no `main` field, `"private": true`. Re-confirmed after removal: unchanged, still no exports of any kind. -- No `index.ts` barrel exists in `packages/api/src` or `packages/api/src/bootstrap`, before or after this phase. -- No other workspace package's `package.json` depends on `@parmana/api` (grep across every `packages/*/package.json`, before and after removal: only self-matches). -- `packages/connector-sdk`'s public barrel (`packages/connector-sdk/src/index.ts:16`, `export * from "./ConnectorFactory.js"`) is **unchanged** — confirmed by `git diff --stat`, this phase touched nothing under `packages/connector-sdk/`. -- **Conclusion: no public API or package export was affected by this removal**, consistent with `docs/ROADMAP-v1.md`'s own prior assessment of the same recommendation ("Impact: internal only, not exported from the package's public surface. Breaking: no."). - -## 10. Documentation Verification - -- `docs/site/reference/api.mdx`: updated. The existing `` block, which already correctly named `ConnectorFactory.ts` as dead code (pre-existing, accurate, unprompted by this phase), now reflects that both `ExecutionControlComposition.ts` and `ConnectorFactory.ts` have been removed as of this phase, with an explicit note that this `ConnectorFactory` is not the same thing as `@parmana/connector-sdk`'s `ConnectorFactory` interface described on the connector-sdk reference page — preserving the disambiguation `docs/ROADMAP-v1.md` had already established. `ConnectorCatalog.ts`'s entry in the same warning (still dead, out of scope) was left exactly as it was. -- `docs/CLAIMS.md`, `docs/site/integrations/overview.mdx`, `docs/site/reference/connector-sdk.mdx`: all three mention "`ConnectorFactory`" but every occurrence is `@parmana/connector-sdk`'s unrelated interface, not the removed class — confirmed by reading each occurrence in context. None required a change. -- `docs/architecture/repository-certification.md`, `docs/architecture/phase2e-engineering-debt-assessment.md`, `docs/ROADMAP-v1.md`: describe the now-removed files only as a historical finding (TD-20/G-13), consistent with the project's existing pattern of not editing a prior phase's or audit's own report after the fact (matching how TD-9's register row remained "Open" after Phase 2F closed it in its own doc — that update is left to a future reassessment phase, not this one). -- No architecture diagram, sequence diagram, or repository map was found referencing either removed file anywhere in the repository. - -## 11. Generated Artifact Verification - -- `npx tsc -b` (incremental) and `npx tsc -b --clean` followed by a fresh `npx tsc -b`: both clean, 0 errors, both before and after removal. -- `openapi/openapi.yaml`, `openapi/openapi.bundled.yaml`, `docs/site/openapi.bundled.yaml`: `git status`/`git diff --stat` after the full regression run (§ below) shows none of these changed — expected, since neither removed file was a route, schema, or OpenAPI-relevant construct. -- No other codegen output (`python/parmana/models/*` generated models, `typescript/src/models/*`) changed — expected, since neither removed file fed any generator. -- `packages/api/dist/bootstrap/`'s stale `.d.ts`/`.js` output for the two removed files (gitignored, never tracked) was manually deleted for local cleanliness; this is expected, harmless, and does not affect any tracked artifact. - -## 12. Regression Validation - -``` -npx tsc -b → clean, 0 errors (both before and after removal) -npm run typecheck → clean, 0 errors -npx vitest run tests/architecture/execution-boundary.test.ts - → 137 passed (was 139 before removal — the 2 fewer are exactly - the "ConnectorFactory.ts never calls .execute(" / "ExecutionControlComposition.ts - never calls .execute(" cases that no longer exist because the - files no longer exist; every remaining bootstrap file is still - checked) -npm test -- --maxWorkers=2 → 140 test files passed, 15 skipped (unchanged); - 959 tests passed (-2, exactly matching the architecture-test - reduction above), 39 skipped, 0 failed -``` - -`git status`/`git diff --stat` against the starting commit confirms exactly two files deleted (`ExecutionControlComposition.ts`, `ConnectorFactory.ts`) and one file modified (`docs/site/reference/api.mdx`) — no other production source, test, schema, or generated artifact was touched. - -**No test depended on either removed file.** The only count change (961 → 959) is fully accounted for by the architecture-enforcement suite's generic directory scan losing exactly the two files that no longer exist — not a loss of coverage for the invariant itself (§5), and not a hidden dependency elsewhere. - -## 13. Remaining Limitations - -- **`ConnectorCatalog.ts` remains dead**, independently of this phase's removal (it does not depend on, or get depended on by, either removed file) and is not named in TD-20. Already flagged by `docs/site/reference/api.mdx`'s pre-existing Warning. Left untouched per this phase's scope — a candidate for a future, separately-scoped cleanup pass, consistent with how prior phases (2F, 2H) have flagged adjacent-but-out-of-scope findings rather than folding them in. -- **Two other files `docs/site/reference/api.mdx` already flags as empty, 0-byte stubs** — `scripts/generate-keys.ts` (repo root) and `packages/api/src/bootstrap/createVendorPaymentSecureConnector.ts` — are not named in TD-20 and were not touched. -- **The `docs/investigations/GAP-AUDIT.md`/`docs/ROADMAP-v1.md` citation-provenance error (§2)** was documented here but not corrected in either of those two documents themselves — consistent with this phase's and prior phases' pattern of not rewriting earlier audit reports after the fact; a future documentation-accuracy pass could correct `docs/architecture/repository-certification.md`'s TD-20 row and Phase 2E's own doc to cite `docs/ROADMAP-v1.md` G-13 rather than `GAP-AUDIT.md`, but that register-row edit is left to a future reassessment phase, matching how TD-9's "Open" status in the register was left for Phase 2E (not Phase 2F itself) to update. - ---- - -## Final Verification - -| Item | Status | -| -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| TD-20 independently verified | ✓ — factual premise (two files, zero references) confirmed exactly accurate; a citation-provenance error in its "GAP-AUDIT.md G-13" attribution found and documented (actual source: `docs/ROADMAP-v1.md`) | -| Historical rationale verified | ✓ — both files introduced in the same commit as the functional path that became canonical (`ddf4bc5`, 2026-07-09), never touched again, never instantiated anywhere in git history (`git log --all -S`, zero results) | -| Dead composition root removed | ✓ — `ExecutionControlComposition.ts`, `ConnectorFactory.ts` deleted | -| Canonical composition root unchanged | ✓ — `createExecutionSystem.ts`, `createExecutionGateway.ts`, `createExecutionControl.ts`, `RuntimeFactory.ts` all untouched, confirmed by `git diff --stat` | -| Runtime behavior unchanged | ✓ — nothing imported or instantiated the removed files; `tsc -b` and full test suite clean | -| Security behavior unchanged | ✓ — no authorization, signal-verification, replay-protection, audit, or credential-handling code touched | -| Public APIs unchanged | ✓ — `@parmana/api` has no exports/barrel regardless; `@parmana/connector-sdk`'s unrelated `ConnectorFactory` interface and its public barrel confirmed untouched | -| Package exports unchanged | ✓ — confirmed via every workspace `package.json`, before and after | -| Documentation updated where necessary | ✓ — `docs/site/reference/api.mdx`'s warning updated to reflect the removal; documents that only describe this as a historical finding (register/assessment docs) left as-is, per established project pattern | -| Generated artifacts unchanged (or expected changes documented) | ✓ — OpenAPI bundles and codegen outputs unchanged; stale gitignored `dist/` output for the two removed files deleted, expected and harmless | -| Phase 1 architecture invariants remain valid | ✓ — `tests/architecture/execution-boundary.test.ts` re-run before and after, 137/137 passing after removal, invariant scope unreduced | - -Supported by: repository searches (§2, §5), source references and full git history search (§3, §6), dependency graph (§6), build output (§11, §12: `tsc -b` clean, `npm run typecheck` clean), and test output (§12: 959/959 non-skipped tests passing, 39 correctly skipped, 0 failed). - -## Final Recommendation - -**TD-20 CLOSED.** - -Both files were independently confirmed dead through the strongest available evidence: not just a current-tree grep (which TD-20's original finding already had) but a full git-history search (`git log --all -S`) proving `new ExecutionControlComposition` and `new ConnectorFactory(` have never appeared in any commit, ever — these classes were dead on arrival, introduced alongside the functional composition approach that was chosen and developed instead within the same week. No STOP condition applied: neither file is dynamically loaded, configuration-referenced, generated-code-referenced, documented as supported architecture, externally tooled, retained for compatibility, or exported as part of any published package surface — `@parmana/api` has no public exports at all, and the unrelated, same-named `@parmana/connector-sdk` interface was confirmed structurally distinct and left untouched. Removal was scoped exactly as required: the two dead files and nothing else — `ConnectorCatalog.ts`'s separate, pre-existing dead status was correctly identified as not a consequence of this removal and left out of scope. The removal is additional evidence for, not just a consequence of, the Phase 1E single-execution-pipeline invariant: two composition roots for the same responsibility coexisted since 2026-07-09 without the invariant ever being violated, because the second one was never activated even once. Full regression suite confirms zero behavioral change (959/959 passing, the only count delta fully explained by two fewer generated architecture-test cases for files that no longer exist), `tsc -b`/`typecheck` clean, and no public API, package export, or generated artifact affected. diff --git a/docs/architecture/phase2j-claims-verification.md b/docs/architecture/phase2j-claims-verification.md deleted file mode 100644 index 30179521..00000000 --- a/docs/architecture/phase2j-claims-verification.md +++ /dev/null @@ -1,184 +0,0 @@ -# Phase 2J — Correct Stale Repository Claims (TD-21) - -Closes TD-21 (`docs/architecture/repository-certification.md`, Technical Debt Register), a Phase 2E finding. This is a documentation and certification phase — no production source code, runtime behavior, public API, or architecture changed. Only `docs/CLAIMS.md` was edited. - -**Fixed against:** commit `c87629a` (`refactor(api): remove dead composition root and close TD-20`), the tip of `main`. Working tree was not clean at the start of this phase — Phase 2I's changes were still uncommitted; per user confirmation, they were committed first (`c87629a`) before this phase's own gate check was re-run and passed. - ---- - -## 1. Original TD-21 Finding - -From `docs/architecture/repository-certification.md`: - -> `docs/CLAIMS.md:835` still describes `create-connector-registry.test.ts` as asserting "vendor-payment remains resolvable when razorpay is not" — true before Phase 2A, false since: that exact test was changed to assert the opposite (fails closed) outside `NODE_ENV=test`. Flagged during Phase 2A/2A.1 but never corrected. Stale claim about current test behavior in an actively-referenced claims document (not a dated historical snapshot like `EXECUTION-FLOW-AUDIT.md`/TD-6). **Next `CLAIMS.md` touch: correct the one clause; out of scope for a dedicated phase on its own.** - -Rated **Low**, classified **Open**. - -## 2. Historical Provenance Audit - -**When the claim was introduced and whether it was accurate then:** `docs/CLAIMS.md`'s current §3.4 content (including the line-835 clause) traces to the same commit lineage as the Razorpay refund connector work. At the time it was written, `packages/api/tests/unit/bootstrap/create-connector-registry.test.ts` genuinely asserted that `vendor-payment` remained resolvable in production regardless of Razorpay's configuration — this was TD-1's very condition (vendor-payment unconditionally registered, unlike credential-gated Razorpay/HubSpot). The claim was **accurate when written**. - -**Which phase superseded it:** Phase 2A, commit `6eff8ec` (`fix(production): remove MockConnector from production bootstrap`), 2026-08-05. Direct evidence, read from `docs/architecture/phase2a-production-connectors.md`: - -> `packages/api/tests/unit/bootstrap/create-connector-registry.test.ts` | Test | Updated: the two tests that asserted `vendor-payment` remained resolvable in production now assert it fails closed; one new test explicitly proves `NODE_ENV=test` still works. - -Independently re-confirmed against current source, not just this citation: `packages/api/tests/unit/bootstrap/create-connector-registry.test.ts` today contains a test literally titled `"(Phase 2A, fail-closed) does not register vendor-payment, and resolveCapability throws, outside test mode — MockConnector never backs production execution"` (line 74) and a second, `"does not fail startup of the whole registry when razorpay credentials are missing — vendor-payment resolution still fails closed, independently"` (line 62) — the exact opposite of what `CLAIMS.md:835` described. - -**Why it was never corrected:** `docs/architecture/repository-certification.md`'s own provenance note states this was "flagged during Phase 2A/2A.1 but never corrected" — Phase 2A's charter was implementation, and it correctly updated the two `docs/site/` pages that would have gone stale (`docs/site/concepts/the-gateway.mdx`, `docs/site/integrations/connector-development-guide.mdx` — verified: both already read Phase-2A-accurate today, §8) but did not sweep `docs/CLAIMS.md` itself. This is exactly the situation TD-21 was registered to close. - -**Should it remain as historical documentation rather than be rewritten:** No. `CLAIMS.md`'s own stated purpose (`docs/CLAIMS.md:81`, "Purpose") is to define **current** public technical claims, not a point-in-time snapshot — unlike `docs/architecture/EXECUTION-FLOW-AUDIT.md` (TD-6, a dated historical audit report this repository's own conventions preserve unedited). TD-21's own text draws this exact distinction. Correcting the claim, not preserving it, is the right action — independently re-confirmed, not assumed from TD-21's wording alone. - -## 3. Claims Inventory - -`docs/CLAIMS.md` was read in full (1428 lines): the Key Compromise Notice, Core Positioning (§1), 21 Supported Claims (§2.1–2.21), 10 Conditional Claims (§3.1–3.10), the TRL Maturity Assessment, 24 Future Claims (§4), 9 claims intentionally not made (§5), the Claim Lifecycle, and the Engineering Principle. - -Full classification summary (every claim; detail on claims requiring action follows in §4–§6): - -| Section | Claim (summary) | Classification | -| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| §1 Core Positioning | Category/Mission/Value Proposition | Not a checkable technical claim — positioning language, no verification applicable | -| §2.1–2.5, 2.15, 2.17–2.21 | Trusted Business Transactions, Deterministic Policy Selection/Evaluation, Authorized Execution, Verifiable Execution Evidence, Authorization-Binding Verification, Fail-Closed Startup Config, Key Provider Input Validation, Fail-Closed Audit Writes, Atomic Duplicate Rejection, Distinguishable HTTP Status | **Verified** — each traced to real, existing evidence (source files and/or tests exist and match the claim as worded); no known FUTURE item, open TD, or documented gap narrows any of these | -| §2.6 Independent Verification | VerificationCrypto/verification-service | **Verified** | -| §2.7 Replay Support | "Parmana supports replay... for verification and analysis" | **Verified But Incomplete → corrected this phase** (§5) | -| §2.8–2.14 | Signed authorization, envelope verification, key/algorithm binding, PQ signing | **Verified** — evidence paths cited use a stale directory convention (`test/` singular instead of `tests/unit/`); files themselves exist and are real (§6); not reclassified, see rationale there | -| §2.16 Caller Authentication at the API Boundary | "Every route except /health requires a valid caller credential" | **Verified But Incomplete → corrected this phase** (§5) | -| §3.1–3.7, 3.9, 3.10 | Envelope non-bypassability, fleet-wide nonce, connector SDK foundation, Razorpay refund/webhook/settlement milestones (3.4–3.7), HubSpot connector, live-mode | **Verified** — each is explicitly, narrowly scoped by its own text (this document's dominant style) and cites real, checked evidence | -| §3.4 (specific clause) | "vendor-payment remains resolvable when razorpay is not" | **Stale → corrected this phase** (TD-21's named target, §4) | -| §3.8 Deployed Environment (Test Mode) | "caller authentication at every route except `/health`" | **Verified But Incomplete → corrected this phase** (same fix as §2.16, §5) | -| TRL Maturity Assessment | TRL 7 assessment | **Verified** — explicitly labeled as interpretation, not a new evidentiary claim; scope-limited in its own text | -| §4 Future Claims (24 items) | Unimplemented connectors, cloud credential providers, algorithm migration, etc. | **Future** — each explicitly framed as `[FUTURE]`/withheld, consistent with actual absence of implementation (spot-checked: no Stripe/GitHub/Salesforce/SAP/ServiceNow/Workday/Slack/Jira/Database connector exists in `packages/connector-sdk/src/connectors/`, confirmed by directory listing) | -| §5 Claims Not Made (9 items) | Non-bypassability, mathematical proof, compliance guarantees, etc. | **Historical/positioning** — these are deliberate non-claims, not subject to staleness | - -No claim was found **Unable to Certify**. Every Verified claim had locatable repository evidence (source file, test file, or both); no claim required inventing evidence or guessing at intent. - -## 4. Repository Verification — TD-21's Named Claim - -`packages/api/tests/unit/bootstrap/create-connector-registry.test.ts` read in full. Confirmed independently (not copied from Phase 2A's own report): - -- Line 50–60: `"(fail-closed) does not register razorpay, and resolveCapability throws, when credentials are unconfigured outside test mode"` — Razorpay's existing fail-closed behavior, unchanged by Phase 2A. -- Line 62–72: `"does not fail startup of the whole registry when razorpay credentials are missing — vendor-payment resolution still fails closed, independently"` — asserts `resolveCapability("payments:execute")` **throws** in production even when only Razorpay's credentials are missing (vendor-payment's own absence is independent, not "reachable as a fallback"). -- Line 74–82: `"(Phase 2A, fail-closed) does not register vendor-payment, and resolveCapability throws, outside test mode — MockConnector never backs production execution"` — the direct contradiction of `CLAIMS.md:835`'s old wording. -- Line 84–90: `"(Phase 2A) registers vendor-payment (MockConnector) when NODE_ENV=test"` — confirms the claim is now conditioned on test mode, not "always resolvable." - -This is the exact evidence TD-21 cited, independently re-run and re-read rather than trusted. - -## 5. Stale Claim Analysis - -**Why it became stale:** Phase 2A's fail-closed fix for TD-1 changed the very test `CLAIMS.md:835` cites, from asserting production-availability to asserting production-unavailability (§2, §4). - -**Which phase superseded it:** Phase 2A (implementation, commit `6eff8ec`) — see §2 for the full four-stage evidence chain this phase's Task 4 required cross-referencing. - -**Was the wording inaccurate:** Yes, directly — the cited test's current assertions are the literal opposite of what the clause described. - -**Was correction appropriate:** Yes (§2's "should it remain historical" analysis) — `CLAIMS.md` is an actively-referenced current-state document, not a dated snapshot. - -## 6. Verified But Incomplete Findings - -Three findings, all corrected in this phase (none left open): - -**§2.7 Replay Support.** "Parmana supports replay of recorded execution decisions for verification and analysis" is literally true — `@parmana/replay`'s `ReplayEngine` is real, tested, and does exactly this. But this phase's own prior work (Phase 2G, `docs/architecture/phase2g-replay-semantics.md`) independently established that `POST /replay`, the HTTP-reachable surface a reader would naturally assume this claim describes, does **not** exercise this package at all — it performs a signature/hash recheck, a materially different and narrower operation. A reader relying on this claim's plain wording, without already knowing Phase 2G's findings, would reasonably but wrongly conclude the HTTP API exposes semantic replay. **Corrected**: added an explicit scope note distinguishing the package-level capability from the HTTP route, pointing to the existing `docs/site/replay/overview.mdx` disambiguation and this repository's own `phase2g-replay-semantics.md` for the full evidence. - -**§2.16 Caller Authentication at the API Boundary.** "Every route except /health requires a valid caller credential" is incomplete, not false — independently verified by reading `packages/api/src/app.ts` directly: **six** routes are mounted before the caller-auth middleware and exempt, not one: `/health`, `/ready`, `/openapi.yaml`, `/documentation`, `POST /refusal/verify`, and `POST /audit/verify`. The claim's own cited evidence (`packages/api/tests/integration/caller-auth.integration.test.ts`) only tests `/health` and `/ready` as exempt and its file-header comment itself says "every route except /health" — also incomplete relative to `app.ts`'s actual code (not corrected in this phase; it is a test file, out of this phase's documentation-only scope, see §9). **Corrected**: named all six exempt routes explicitly, with the reason the last two are intentionally unauthenticated (independently third-party-verifiable signature checks, not caller-data-exposing routes). - -**§3.8 Deployed Environment (Test Mode).** The identical "except `/health`" phrase appears a second time, describing the same underlying fact in a different claim. **Corrected** identically, for consistency, with a pointer back to 2.16. - -No claim remains classified **Verified But Incomplete** after this phase (§10 lists related, out-of-scope incompleteness found in _other_ files, which does not block this classification for `CLAIMS.md` itself). - -## 7. Documentation Changes - -`docs/CLAIMS.md` only, three edits: - -1. **§3.4, evidence bullet (the named TD-21 target):** replaced "vendor-payment remains resolvable when razorpay is not" with "vendor-payment and razorpay each independently fail closed outside `NODE_ENV=test`," plus a pointer to `docs/operations/td1-closure-summary.md` naming the complete Implementation (Phase 2A) → Deployment Verification (Phase 2A.2) → Historical Integrity Verification (Phase 2A.3) → Technical Debt Closure (Phase 2A.4) evidence chain, per this phase's Task 4 instruction. -2. **§2.7 Replay Support:** added the package-vs-route scope note (§6). -3. **§2.16 and §3.8:** named all six caller-auth-exempt routes explicitly, in both places the incomplete four-word phrase appeared (§6). - -No other claim, section, or unrelated wording was touched. `git diff --stat`: one file changed, `docs/CLAIMS.md`, 8 insertions / 8 deletions (three edits, each a same-line-count replacement). - -## 8. Cross-Reference Verification (Phase 2A / 2A.1 / 2A.2 / 2A.3 / 2A.4) - -Full chain read directly from `docs/operations/td1-closure-summary.md` (the canonical evidence record) and cross-checked against each underlying report: - -| Phase | Commit | Report | Outcome | -| ------------------------------ | --------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -| 2A (implementation) | `6eff8ec` | `docs/architecture/phase2a-production-connectors.md` | `createVendorPaymentConnector.ts` returns `undefined` outside `NODE_ENV=test`; `createConnectorRegistry.ts` guards registration accordingly | -| 2A.1 (readiness review) | `10c8064` | `docs/operations/phase2a-deployment-readiness.md` | READY WITH CONDITIONS | -| 2A.2 (deployment verification) | `0c19f78` | `docs/operations/phase2a-deployment-verification.md` | DEPLOYMENT VERIFIED WITH FOLLOW-UP | -| 2A.3 (historical integrity) | `e6a25ab` | `docs/operations/phase2a-historical-integrity-verification.md` | HISTORICAL INTEGRITY CONFIRMED | -| 2A.4 (closure) | — | `docs/operations/td1-closure-summary.md` | TD-1 CLOSED | - -**Confirmed this stale claim relates directly to the MockConnector removal**: `create-connector-registry.test.ts` (the file `CLAIMS.md:835` cites) is the exact file Phase 2A modified to implement and prove the TD-1 fix. The corrected wording in §7 now points to this complete chain rather than restating it inline, matching this phase's Task 4 instruction and this document's own established style of pointing to canonical reports rather than duplicating their evidence (e.g., "see 3.2's sibling claim," "see Future Claims"). - -## 9. Repository Consistency Audit - -Searched `docs/CLAIMS.md`, `docs/GUARANTEES.md`, `README.md`, every `docs/architecture/*.md`, every `docs/operations/*.md`, and every `docs/site/**` file for equivalent wording. - -**The exact stale clause ("vendor-payment remains resolvable when razorpay is not") appears nowhere else** — confirmed by repo-wide grep both before and after this phase's edit. `docs/site/concepts/the-gateway.mdx` and `docs/site/integrations/connector-development-guide.mdx` already describe the post-Phase-2A fail-closed reality accurately (Phase 2A updated them itself, confirmed by direct reading: both contain an explicit "Update (Phase 2A)" / "used to register... unconditionally... that was a trust gap" callout). Every other `docs/site` page mentioning `vendor-payment`/`MockConnector` (roadmap.mdx, quickstart.mdx, deployment/local.mdx, guides/deploy-patterns.mdx, integrations/overview.mdx) describes it as "the one connector registered" or "a reference mock" without claiming unconditional production availability — none are stale in this specific way. - -**A significant, separate consistency finding, documented per this phase's explicit "do not silently broaden" instruction, not fixed:** the "every route except `/health`" incompleteness found in `CLAIMS.md` §2.16/§3.8 (§6) recurs across **`docs/site`**, inconsistently: - -- **Already complete** (list all four probe/doc routes, though still missing `/refusal/verify`/`/audit/verify`, which postdate these pages): `docs/site/api-reference/authentication.mdx`, `docs/site/faq.mdx`, `docs/site/llms.txt`, `docs/site/llms-full.txt` (three occurrences). `docs/site/llms.txt` line 22 is notable: it already contains its own prior self-correction — _"Note: the OpenAPI spec's own top-level description names only GET /health as exempt; the other three are real exemptions in packages/api/src/app.ts that the spec text omits. This page states the verified, complete list."_ — evidence a previous session already fought this exact class of drift once, for a subset of the exempt-route list. -- **Still incomplete** (say only "except `GET /health`"): `docs/site/api-reference/introduction.mdx`, `docs/site/guides/deploy-patterns.mdx` (line 122), `docs/site/reference/api.mdx` (line 26), `docs/site/security/overview.mdx`, `docs/site/trust-and-claims/what-we-dont-claim.mdx`, and `docs/site/openapi.bundled.yaml` (four occurrences, a generated file — would need the fix applied to `openapi/openapi.yaml`'s source and re-bundled, not edited directly). -- **Provenance**: `git log` shows `docs/site/faq.mdx` and `docs/site/api-reference/authentication.mdx` were both last corrected for this exact issue on 2026-07-21 (`8a3551a`, `c91a580`) — before the `POST /refusal/verify` (RFC-0021) and `POST /audit/verify` (caller/webhook audit signing) routes existed. The "complete" pages are complete relative to 2026-07-21's route set, not today's. - -This is a materially larger documentation-consistency issue than TD-21's own named scope (one clause in one file) and spans roughly a dozen files across the published docs site plus a generated OpenAPI bundle. Fixing it is out of this phase's scope — doing so here would silently broaden a "correct one clause" phase into a site-wide documentation sweep. **Recommended as a dedicated future phase**, scoped specifically to reconciling the caller-auth exempt-route list across every `docs/site` page and regenerating `openapi.bundled.yaml` from a corrected `openapi/openapi.yaml`. - -**Citation-path hygiene, documented, not fixed:** claims §2.6, §2.8, §2.9, §2.11–2.14 cite evidence paths using `packages/{runtime,crypto,envelope-verifier}/test/` (singular, no subdirectory) and PascalCase filenames (e.g., `SignatureProvider.test.ts`, `Dilithium3SignatureProvider.test.ts`). The actual files exist and are real, but live at `packages/{runtime,crypto,envelope-verifier}/tests/unit/` (plural, with a subdirectory) using kebab-case filenames (e.g., `signature-provider.test.ts`, `dilithium3-signature-provider.test.ts`) — confirmed by direct `find`. This does not narrow any claim's plain-language meaning (the tests exist and prove what's claimed) — it is a citation-accuracy defect, the same class `docs/VERIFICATION-GAPS.md`'s G-10 already tracks for a different set of citations ("vague or indirect... none of these claims are false"). Not reclassified as Verified But Incomplete (that definition is about omitted caveats/limitations, not path accuracy) and not fixed here, since correcting roughly eight citation paths across five claims is a distinct, mechanical documentation-hygiene task beyond TD-21's one-clause scope — flagged for a future pass, ideally alongside G-10's existing, similar finding. - -**GUARANTEES.md consistency**: G-08's own wording ("Replay re-evaluates historical execution... Replay is intended to detect meaningful differences between recorded and replayed execution") is fully consistent with, and reinforces, this phase's corrected §2.7 wording. No change needed there. - -## 10. Remaining Limitations - -- The docs/site caller-auth exempt-route inconsistency (§9) — a dozen-plus files, one generated bundle — remains unfixed, by design, per this phase's explicit scope. -- The citation-path hygiene issue across §2.6/§2.8/§2.9/§2.11–2.14 (§9) remains unfixed, by design. -- `docs/architecture/repository-certification.md`'s TD-21 register row remains "Open" — consistent with this repository's established pattern (confirmed across Phase 2F/TD-9, Phase 2H/TD-19, Phase 2I/TD-20): the closing phase's own report is the canonical closure evidence; updating the register row itself is left to a future reassessment phase (as Phase 2E did retroactively for TD-15), not the closing phase. -- This phase's Claims Inventory (§3) verified every claim's existence and evidence but did not re-run every cited test file individually beyond what §8's full regression suite already covers; claims whose cited tests are part of the always-running suite (the large majority) are covered by §11's regression run, Supabase/live-gated citations (e.g., 3.4, 3.6–3.9) were not re-run live in this phase (no destructive or credentialed action was taken; this is a documentation phase). - -## 11. Evidence Summary - -``` -Repository searches: grep -n "vendor-payment\|MockConnector" docs/CLAIMS.md (single stale - occurrence found and fixed); grep -rn "except /health" across docs/CLAIMS.md and docs/site/ - (found and cross-referenced, §9); git log -S/--follow on CLAIMS.md's stale clause's source - test file and on docs/ROADMAP-v1.md/GAP-AUDIT.md (citation-provenance check inherited from - Phase 2I's own methodology, applied here to TD-21's chain) - -Source references: packages/api/tests/unit/bootstrap/create-connector-registry.test.ts (read - in full, lines 1-100), packages/api/src/app.ts (read in full, confirming 6 exempt routes), - packages/replay/src/ReplayEngine.ts (Phase 2G's own prior finding, re-confirmed still - accurate against current source) - -Documentation references: docs/architecture/phase2a-production-connectors.md, - docs/operations/{phase2a-deployment-readiness,phase2a-deployment-verification, - phase2a-historical-integrity-verification,td1-closure-summary}.md, - docs/architecture/repository-certification.md (TD-1/TD-21 rows), docs/GUARANTEES.md (G-08), - docs/site/replay/overview.mdx, docs/architecture/phase2g-replay-semantics.md - -Build output: npx tsc -b → clean, 0 errors -Test output: npm test -- --maxWorkers=2 → 140 test files passed, 15 skipped; - 959 tests passed, 39 skipped, 0 failed — identical to the pre-phase count, confirming zero - production behavior change (expected: no test, schema, or source file was touched) -``` - ---- - -## Final Verification - -| Item | Status | -| ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| TD-21 independently verified | ✓ — factual premise (stale test-behavior description) confirmed exactly accurate against current source, not assumed from the register entry | -| Historical provenance verified for every modified claim | ✓ — §3.4's clause (§2, §4, §8: Phase 2A commit `6eff8ec`, full 2A→2A.4 chain); §2.7 (Phase 2G, this repository's own prior independent verification); §2.16/§3.8 (`packages/api/src/app.ts`, read directly) | -| Every modified claim supported by repository evidence | ✓ — §4 (test file re-read), §6 (`app.ts` re-read), §8 (full evidence chain re-read) | -| Every Verified claim checked for completeness against known FUTURE items, TD, gaps, caveats | ✓ — §3 full inventory; two additional Verified But Incomplete findings surfaced and corrected (§6) beyond TD-21's named target | -| Historically accurate claims preserved where appropriate | ✓ — no historical report was rewritten; `docs/architecture/EXECUTION-FLOW-AUDIT.md`-style dated snapshots untouched (none required this phase) | -| Historical reports preserved | ✓ — `git status`/`git diff --stat` confirms only `docs/CLAIMS.md` changed | -| Repository claims internally consistent | ✓, with one documented, out-of-scope exception (§9's docs/site exempt-route inconsistency) | -| Runtime unchanged | ✓ — no source file touched | -| Production source code unchanged | ✓ — `git diff --stat`: `docs/CLAIMS.md` only | - -Supported by: repository searches, source references, documentation references, git history, and build/test output, all listed in §11. - -## Final Recommendation - -**TD-21 CLOSED.** - -The named claim (`docs/CLAIMS.md:835`) was independently re-verified stale, traced to the exact commit that superseded it (Phase 2A, `6eff8ec`), and corrected with a pointer to the complete four-stage evidence chain this phase's Task 4 required (`docs/operations/td1-closure-summary.md`), rather than merely restating "implementation changed." Two additional Verified But Incomplete claims were found during the mandatory full-document completeness check (§2.7 Replay Support's package-vs-route scope, §2.16/§3.8's caller-auth exempt-route list) and were also corrected in this same pass, narrowly and with direct repository evidence — per this phase's own rule, TD-21 could not be marked CLOSED with either left unaddressed, and neither was. A significant, separate consistency finding (the same exempt-route incompleteness recurring across roughly a dozen `docs/site` pages and a generated OpenAPI bundle) was documented in full, with provenance, but deliberately not fixed, since doing so would have silently broadened a one-clause correction into a site-wide documentation sweep — flagged instead as a recommended future phase. No claim was found Unable to Certify. No historical report was altered. `tsc -b` and the full test suite (959 passed, 39 skipped, 0 failed — identical to the pre-phase count) confirm zero production behavior change. diff --git a/docs/architecture/phase2k-capability-policy-binding.md b/docs/architecture/phase2k-capability-policy-binding.md deleted file mode 100644 index b2fb125d..00000000 --- a/docs/architecture/phase2k-capability-policy-binding.md +++ /dev/null @@ -1,226 +0,0 @@ -# Phase 2K — Establish Canonical Capability-to-Policy Binding (TD-22) - -Closes TD-22, raised by an independent repository verification of the public claim _"Even if AI has valid credentials, it still cannot execute anything your business hasn't authorized. No exceptions."_ That verification confirmed Property A (credential isolation) held, but found — and this phase independently re-confirms from scratch — that no mechanism bound a production capability to the one policy that authorizes it, letting a caller pair a real, fund-moving or record-mutating capability with an unrelated, unprotected policy and bypass that capability's own protections entirely. - -This is an implementation phase: the gap was independently reconfirmed, so per this phase's own charter, establishing the canonical mapping is required. It is not an authorization, policy, or runtime redesign — one new, small, additive module and a minimal, backward-compatible wiring change. - -**Fixed against:** commit `cb3c3fc` (`docs(claims): correct stale vendor-payment/caller-auth wording and close TD-21`), the tip of `main`. Working tree was not clean at the start — Phase 2J's changes were still uncommitted; per user confirmation, committed first (`cb3c3fc`) before this phase's gate check was re-run and passed. - ---- - -## 1. Original Verification Finding - -From the prior verification task's report (not itself a numbered phase document, but the direct predecessor to this one): - -> **Property B (Independent Authorization): DOES NOT HOLD**, for a structural, undisclosed reason: no mechanism in this codebase binds a Business Transaction's declared `policy.name`/`policy.version` to the `intent.action`/capability actually being executed. [...] This is independently, concretely reproducible from source alone for both fund-moving (`razorpay:refund-create`) and business-record-mutating (`hubspot:deal-update`) capabilities. - -## 2. Independent Verification Methodology - -Every prior finding was treated as a hypothesis and re-derived from current source, not copied: - -- Traced the complete lifecycle fresh: `BusinessTransactionMapper.fromRequest()` → `RuntimeEngine.execute()` → `PolicyRouter.load()` → `FilePolicyRepository.load()` → `PolicyValidator.validate()` → `SignalIntentBinder.findViolations()` → `PolicyEngine.evaluate()` → `SignalStateVerifier.findViolations()` → `ExecutionGate.enforce()` → `RuntimeAuthorizationSigner.sign()`. -- Read `packages/policy/src/PolicyRouter.ts`, `PolicyValidator.ts`, `PolicyRegistry.ts`, `PolicyEngine.ts`, `SignalIntentBinder.ts`, `FilePolicyRepository.ts` in full. -- **New finding this phase, not in the prior report:** discovered that `packages/runtime/src/policy/PolicyRouter.ts` and `packages/runtime/src/policy/PolicyValidator.ts` are same-named but entirely different, **unused** classes — a naming collision with the real `@parmana/policy` classes `RuntimeEngine` actually imports (confirmed: `RuntimeEngine.ts:22` imports `PolicyRouter` from `@parmana/policy`; `RuntimeBuilder.ts:8` constructs it from the same import; grep confirms zero importers of the `packages/runtime/src/policy/` versions anywhere in the repo). `@parmana/policy`'s own `PolicyRegistry` class is similarly unused in production (confirmed by grep across `packages/api/src`, `packages/runtime/src/RuntimeBuilder.ts`, `RuntimeFactory.ts`) and, by its own doc comment, explicitly "does NOT choose policies" — it was never a candidate binding mechanism. These are pre-existing dead-code artifacts, not fixed in this phase (out of TD-22's scope; noted for a future cleanup, matching this project's established pattern of flagging adjacent-but-out-of-scope findings rather than folding them in). -- Re-ran `git log -S`-style searches were not needed here (no historical-provenance question this time); instead, exhaustively re-verified **every currently-loadable policy file** (`policies/*/*/policy.json`, 11 files) for `boundSignals` presence, and re-read `Dockerfile:104` to reconfirm `PARMANA_POLICY_DIR=./policies` — the full directory, not a curated subset — is what production actually loads from. -- Manually re-traced the exact "wrong policy" exploit end to end against current source (not assumed from the prior report): `razorpay:refund-create` paired with `customer-refund/1.0.0` (a real, loadable policy with no `boundSignals`), confirmed the request would reach `PolicyEngine.evaluate()` fully approved on caller-declared signals alone, with `intent.parameters.amountPaise` (the real, unrelated amount that executes) never checked against anything. - -## 3. Policy Selection Inventory - -| Component | Responsibility | Influences policy selection? | Classification | -| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------- | -| `BusinessTransactionMapper.fromRequest()` (`packages/api/src/mappers/`) | Structural mapping of the HTTP request body into a `BusinessTransaction` | Passes `request.policy` through verbatim — **yes, this is where caller control originates** | Canonical (production entry point) | -| `BusinessTransaction.policy` / `PolicyReference` (`packages/shared/src/domain/`) | Type shape: `{name, version, schemaVersion}` | Carries the caller-declared value | Canonical (data shape, unchanged) | -| `Intent.action` (`packages/shared/src/domain/intent.ts`) | The capability actually being executed | Was previously invisible to policy selection entirely | Canonical (unchanged) | -| `@parmana/policy`'s `PolicyRouter` (`packages/policy/src/PolicyRouter.ts`) | Loads exactly one policy by `(name, version)`, via an injected `PolicyRepository` | Executes the caller's choice, does not validate it | Canonical (the real, used implementation) | -| `packages/runtime/src/policy/PolicyRouter.ts` | Same name, file-reading implementation | N/A — never imported by anything | **Unused/Legacy** (naming collision, not fixed this phase) | -| `@parmana/policy`'s `PolicyValidator` (`packages/policy/src/PolicyValidator.ts`) | Validates a loaded policy's own internal structural integrity (non-empty `policyId`, well-formed rules, etc.) | No — does not compare the loaded policy against the caller's reference or the action at all | Validation (canonical, used) | -| `packages/runtime/src/policy/PolicyValidator.ts` | Same name, compares loaded policy to a reference | N/A — never imported by anything | **Unused/Legacy** (naming collision, not fixed this phase) | -| `FilePolicyRepository` (`packages/policy/src/FilePolicyRepository.ts`) | Reads `///policy.json`, regex-guards `name`/`version` against path traversal | Executes the caller's choice of file path segments | Canonical (used) | -| `@parmana/policy`'s `PolicyRegistry` (and its `packages/runtime/src/policy/` namesake) | In-memory `name:version → metadata` catalog | No — by its own doc comment, explicitly does not choose policies | **Unused** in production (both copies) | -| `SignalIntentBinder` | Enforces a policy's own opt-in `boundSignals` map | No — operates on whatever policy was already selected | Helper (canonical, used, unchanged) | -| `SignalStateVerifier` (Razorpay/HubSpot implementations) | Independently re-verifies specific signals via a real vendor fetch, gated on `action`, not policy identity | No — assumes the correct policy already ran `SignalIntentBinder` | Helper (canonical, used, unchanged) | -| `DefaultConnectorPolicy.assertAllowed()` | Checks connector capability inclusion, gateway/connector identity, session validity | No — never inspects policy identity | Validation (canonical, unchanged) | -| **`CapabilityPolicyBinder` / `CANONICAL_CAPABILITY_POLICY_BINDINGS`** (`packages/policy/src/CapabilityPolicyBinding.ts`) | **New this phase.** The single authoritative `action → PolicyReference` mapping | **Yes — enforces that the caller's declared policy must equal the canonical one for any bound action** | **Canonical (new)** | - -**Conclusion, independently re-confirmed:** before this phase, no component anywhere in the inventory determined policy selection from capability identity. `transaction.policy.name`/`.version` was the sole, unchecked, caller-derived input driving which policy governed every execution. - -## 4. Capability-to-Policy Call Graph - -**Before this phase** (every production capability, identical shape): - -``` -POST /execute - → BusinessTransactionMapper.fromRequest(req.body) - transaction.policy = req.body.policy [caller-derived, verbatim] - transaction.intent.action = req.body.intent.action [caller-derived, verbatim] - → RuntimeEngine.execute(transaction) - → PolicyRouter.load(transaction.policy.name, transaction.policy.version) - → FilePolicyRepository.load(name, version) [reads whatever file the caller's name/version selects] - → PolicyValidator.validate(policy) [structural only -- no action/capability awareness] - → SignalIntentBinder.findViolations(policy, signals, intent) [only as strong as the SELECTED policy's own boundSignals] - → PolicyEngine.evaluate(policy, signals) [no action parameter exists in this call at all] - → SignalStateVerifier.findViolations(...) [gated on action, assumes SignalIntentBinder already ran correctly for the RIGHT policy] - → ExecutionGate.enforce(decision) - → RuntimeAuthorizationSigner.sign({policyName: transaction.policy.name, ...}) - → connector.execute() against intent.parameters [never cross-checked against signals for an unbound field] -``` - -Nothing in this graph derives the policy from the capability. Everything derives the capability's protections from _whichever_ policy the caller happened to name. - -**After this phase** (for the 6 canonically-bound capabilities; unbound actions are byte-for-byte unchanged): - -``` -POST /execute - → BusinessTransactionMapper.fromRequest(req.body) [unchanged] - → RuntimeEngine.execute(transaction) - → PolicyRouter.load(transaction.policy.name, transaction.policy.version) [unchanged -- still loads whatever the caller named] - → CapabilityPolicyBinder.findViolation(transaction.intent.action, transaction.policy) [NEW] - if transaction.intent.action has a canonical entry AND transaction.policy != canonical: - → ordinary PolicyDecision REJECT, matchedRuleId "capability-policy-binding-violation", - no rule ever evaluated, no authorization ever generated - else: proceed exactly as before (SignalIntentBinder → PolicyEngine.evaluate → SignalStateVerifier → ...) -``` - -The check runs immediately after policy load (so the rejection decision can still reference `policy.policyId`/`policy.policyVersion`, matching the existing `SignalIntentBinder`-violation shape exactly) and before `SignalIntentBinder`, since checking a narrower guarantee against a policy already confirmed wrong is meaningless. - -## 5. Canonical Mapping Design - -`packages/policy/src/CapabilityPolicyBinding.ts` (new file, exported from `packages/policy/src/index.ts`): - -```ts -export const CANONICAL_CAPABILITY_POLICY_BINDINGS: ReadonlyMap = new Map([ - ["payments:execute", { name: "vendor-payment", version: "2.0.0", schemaVersion: "1.0.0" }], - ["razorpay:payment-fetch", { name: "razorpay-refund", version: "1.0.0", schemaVersion: "1.0.0" }], - ["razorpay:refund-create", { name: "razorpay-refund", version: "1.0.0", schemaVersion: "1.0.0" }], - ["razorpay:refund-fetch", { name: "razorpay-refund", version: "1.0.0", schemaVersion: "1.0.0" }], - ["hubspot:deal-fetch", { name: "hubspot-deal-update",version: "1.0.0", schemaVersion: "1.0.0" }], - ["hubspot:deal-update", { name: "hubspot-deal-update",version: "1.0.0", schemaVersion: "1.0.0" }], -]); - -export class CapabilityPolicyBinder { - public findViolation(action: string, declared: PolicyReference): CapabilityPolicyBindingViolation | undefined { ... } -} -``` - -**Design decisions, each independently justified:** - -- **Scope: exactly the capabilities registered in production bootstrap** (`createConnectorRegistry.ts`), each bound to the one policy already purpose-built for it (`razorpay-refund/1.0.0` covers all three Razorpay capabilities including the two read-only fetches, since `RazorpaySignalStateVerifier` already internally uses that same policy name/version for its own verification fetches; `hubspot-deal-update/1.0.0` covers both HubSpot capabilities identically; `vendor-payment/2.0.0` is included for `payments:execute` even though that capability is not currently registered in production at all (TD-1), so the binding is ready the moment a real connector replaces the test-only `MockConnector`). **Every other action — every test, tutorial, and example fixture — has no canonical entry and is completely unaffected**, confirmed by design (the check is a no-op whenever `CANONICAL_CAPABILITY_POLICY_BINDINGS.get(action)` returns `undefined`) and by the full regression run (§8). -- **Reject-on-mismatch, not silent override.** A caller whose declared policy doesn't match the canonical one for a bound action is rejected outright (`POLICY_DENIED`, 403), never silently redirected to the canonical policy. This was chosen over silent substitution for two reasons: (1) it matches this codebase's dominant, established philosophy of explicit, named fail-closed rejections over silent correction (every credential provider, connector registry, and prior `SignalIntentBinder`/`SignalStateVerifier` addition rejects rather than substitutes); (2) it keeps the signed authorization's recorded `policyName`/`policyVersion` — still `transaction.policy.name`/`.version`, unchanged code — honest: since a mismatch is rejected before evaluation, by the time an authorization is ever signed for a bound capability, the caller's declared reference is _already_ guaranteed to equal the canonical one, so no new logic was needed to keep the audit trail accurate. -- **Optional, trailing constructor parameter on `RuntimeEngine`**, exactly matching the established precedent for both prior additions to this same class (`RefusalRecordBuilder`/`Repository` for RFC-0021, `SignalStateVerifier` for RFC-0022/G-24) — every pre-existing direct constructor call site (e.g. `packages/runtime/tests/e2e/runtime.e2e.test.ts`, which constructs `RuntimeEngine` directly) keeps compiling and behaving identically when it omits the new argument. The one production composition root, `RuntimeBuilder.build()`, now **always** constructs and passes a `CapabilityPolicyBinder` — unconditionally, with no builder method to disable it — making the binding an actual structural invariant for all real API traffic, while remaining non-breaking for every other existing call site. - -## 6. Implementation Changes - -Five files changed, one new: - -- **New:** `packages/policy/src/CapabilityPolicyBinding.ts` — the mapping and binder class (§5). -- `packages/policy/src/index.ts` — exports `CANONICAL_CAPABILITY_POLICY_BINDINGS`, `CapabilityPolicyBinder`, `CapabilityPolicyBindingViolation`. -- `packages/runtime/src/RuntimeEngine.ts` — new optional trailing constructor parameter `capabilityPolicyBinder?: CapabilityPolicyBinder`; in `execute()`, a new check runs after policy load and before `SignalIntentBinder`, producing an ordinary `PolicyDecision` REJECT (`matchedRuleId: "capability-policy-binding-violation"`) on a mismatch, with `bindingViolations` (the `SignalIntentBinder` check) skipped entirely in that case (no point checking signal/intent binding against a policy already confirmed wrong). -- `packages/runtime/src/RuntimeBuilder.ts` — constructs `new CapabilityPolicyBinder()` unconditionally in `build()` and passes it as the new trailing argument to `RuntimeEngine`'s constructor. - -**Not changed:** `PolicyRouter`, `PolicyValidator`, `PolicyEngine`, `SignalIntentBinder`, `SignalStateVerifier`, `ExecutionGate`, `ExecutionGateway`, any connector, any credential provider, any policy JSON file, `RuntimeFactory.ts`, `application.ts`, any route file, any public HTTP request/response schema. - -## 7. Policy Substitution Analysis - -Re-ran the exact substitution audit Task 3 required, against the fixed tree: - -- **`razorpay:refund-create` + `customer-refund/1.0.0`** (the concrete exploit found during verification): now rejected — proven end to end through the real `POST /execute` route against the real production bootstrap chain (`packages/api/tests/integration/razorpay-refund.integration.test.ts`, new case, §8). -- **`hubspot:deal-update` + `customer-refund/1.0.0`** (the analogous exploit for the other mutating capability): now rejected — same proof, `hubspot-deal-update.integration.test.ts`, new case. -- **Version-only mismatch** (correct policy name, wrong version) is also rejected — `CapabilityPolicyBinder.test.ts`'s dedicated case, since the binder compares both `name` and `version`, not name alone. -- **Renaming/arbitrary policy references**: any `policy.name`/`.version` naming a file that doesn't exist at all still fails earlier, at `FilePolicyRepository.load()` (`PolicyNotFoundError`, pre-existing, unchanged) — the new check only matters for the case that previously succeeded: a real, loadable, _wrong_ policy. -- **Bypassing canonical routing entirely**: confirmed impossible for production traffic — `new RuntimeEngine(` occurs in exactly two places repo-wide (`RuntimeBuilder.ts`, and one direct-construction test file that predates and is unaffected by this change), and `RuntimeBuilder`/`RuntimeFactory` is the sole production composition root (Phase 1E's own architecture-boundary test, `tests/architecture/execution-boundary.test.ts`, independently re-confirms "exactly one ExecutionSystem binding in production composition"; re-run clean in §8). -- **Every policy identifier's provenance**: `transaction.policy.name`/`.version` remains **caller-derived** for every action without a canonical entry (unchanged, by design) and is now **caller-derived-but-validated** (must equal a **system-derived** canonical value or the request is rejected) for the 6 bound capabilities. No identifier is silently system-substituted. - -## 8. Regression Validation - -**New tests:** - -- `packages/policy/tests/unit/CapabilityPolicyBinder.test.ts` (7 tests): no violation for unbound actions; no violation when declared matches canonical; the exact live-shaped `razorpay:refund-create`/`customer-refund` exploit blocked; the same shape for `hubspot:deal-update`; a version-only mismatch blocked; exactly one canonical entry per bound action (no duplicates); the complete bound-action set matches what Phase 2K's own capability inventory (§1 above, and the prior verification's Step 1) found in production bootstrap. -- `packages/api/tests/integration/razorpay-refund.integration.test.ts`, new case: the exploit rejected end to end through the real HTTP route and real production bootstrap chain, zero refund reaches the mock Razorpay server. -- `packages/api/tests/integration/hubspot-deal-update.integration.test.ts`, new case: the same, for HubSpot, with a `fetch` spy additionally confirming zero network calls reached the mock server. - -**Full regression run, independently re-executed against the fixed tree:** - -``` -npx tsc -b → clean, 0 errors -npm test -- --maxWorkers=2 → 141 test files passed (+1), 15 skipped (unchanged); - 968 tests passed (+9: 7 new binder unit tests, 1 new - Razorpay integration case, 1 new HubSpot integration - case), 39 skipped, 0 failed -``` - -**Every pre-existing test that pairs a bound action with a policy reference already used the canonical value** — confirmed by direct inspection before writing any code, not discovered by trial and error: `packages/api/tests/fixtures/business-transaction.ts`, `runtime.e2e.test.ts`, `runtime.integration.test.ts`, `razorpay-refund.integration.test.ts`, `hubspot-deal-update.integration.test.ts` all already pair `payments:execute`/`razorpay:refund-create`/`hubspot:deal-update` with `vendor-payment@2.0.0`/`razorpay-refund@1.0.0`/`hubspot-deal-update@1.0.0` respectively. The handful of fixtures that pair these same action strings with a _different_ policy (`execution-request-builder.test.ts`'s `test-policy`, `execution-trust-record.test.ts`'s `vendor-payment@1.0.0`, `multi-item-trust-record.ts`'s `vendor-payment@1.0.0`) were individually confirmed, before implementation, to never invoke `RuntimeEngine.execute()` at all — each unit-tests an isolated builder or storage-repository class directly. The unchanged pass/fail/skip counts (only the expected +9 for genuinely new tests) confirm this held. - -**Authorization, replay protection, and audit generation unchanged:** no file under `execution-gateway`, `execution-control`, `envelope-verifier`, or any audit sink was touched; every test covering those areas (already part of the full suite above) passed unmodified. - -## 9. Marketing Claim Re-verification - -Re-ran the verification for _"Even if AI has valid credentials, it still cannot execute anything your business hasn't authorized. No exceptions,"_ against the fixed tree, not assuming success: - -**Property A — Credential Isolation: HOLDS, unchanged.** No file under `packages/execution-control`, `packages/execution-gateway`, or any credential provider was touched by this phase. Re-confirmed by the full regression run passing identically. - -**Property B — Independent Authorization: the specific, undisclosed finding this phase targeted is now CLOSED.** The policy-selection bypass — pairing `razorpay:refund-create`/`hubspot:deal-update` with an unrelated, unprotected policy — is proven blocked end to end (§7, §8). **Two previously-identified, separately-disclosed gaps remain, unaddressed, and were never in TD-22's scope:** - -- `razorpay-refund/1.0.0`'s `dailyCumulativeAfterThisRefundPaise` signal remains unverified against Parmana's own cumulative-refund ledger (no such ledger is wired to production `POST /execute` at all) — disclosed in `docs/VERIFICATION-GAPS.md` G-24 and `packages/connector-sdk/src/connectors/razorpay/RazorpaySignalStateVerifier.ts`'s own comment, unchanged by this phase. -- `hubspot-deal-update/1.0.0`'s `preAuthorizedForAmountChange` remains an unverified caller-declared boolean — disclosed in `docs/CLAIMS.md` §3.10/§4 (Future Claims), unchanged by this phase. - -These are real, but _disclosed_, limitations distinct in kind from TD-22's undisclosed structural gap: both require independent verification _within_ the correct policy (a ledger fetch; a signed approval artifact) — a materially different, larger piece of work than binding a capability to its policy, and out of this phase's explicit non-goals ("Do NOT redesign authorization... Do NOT redesign policies... Only establish structural binding"). - -**Property C — Structural Enforcement: HOLDS for every production-registered capability.** Policy selection is now a system-derived invariant, not a caller-controlled choice, for all 6 capabilities identified in production bootstrap. This is independently demonstrable: `CANONICAL_CAPABILITY_POLICY_BINDINGS` is a fixed, code-defined constant with no runtime configuration surface, `RuntimeBuilder.build()` wires the enforcing `CapabilityPolicyBinder` unconditionally with no opt-out, and the full regression suite (§8) proves both that legitimate matching requests still succeed and that mismatched ones are now rejected. - -**Overall claim status, honestly stated:** the claim is more supported than before this phase, but still not **fully** supported in the unqualified sense — the two disclosed gaps above mean a caller can still influence two specific, named facts (the daily cumulative total; the pre-authorization flag) without independent verification, even though the caller can no longer achieve this by picking a different policy. The claim's "No exceptions" wording remains stronger than what the repository currently proves for those two disclosed items. - -## 10. Remaining Limitations - -- The two disclosed Property B gaps (§9) remain open — out of TD-22's scope by design, not overlooked. -- `packages/runtime/src/policy/{PolicyRouter,PolicyValidator}.ts` and both copies of `PolicyRegistry` remain dead code, confirmed unused (§2) — a naming-collision hazard for future readers, flagged here, not cleaned up (out of scope; a candidate for a future, dedicated cleanup phase in the style of Phase 2I's TD-20 closure). -- The canonical mapping is a static, code-defined table — adding a new production capability (e.g., a real `vendor-payment` connector, or a sixth vendor integration) requires a corresponding code change to `CANONICAL_CAPABILITY_POLICY_BINDINGS`, not a configuration change. This is intentional (a canonical invariant should not be externally reconfigurable) but means the binding must be kept in sync by hand as new capabilities are added — no automated check currently enforces "every registered capability has a canonical entry" (the reverse direction — every canonical entry corresponds to a real registered capability — is asserted by `CapabilityPolicyBinder.test.ts`'s inventory-matching case, but nothing yet fails if a _new_ capability is registered without adding a binding for it). -- No STOP condition was triggered (§ Final Verification), but the underlying tension the STOP-conditions section anticipated is real: `docs/CLAIMS.md` §2.2 describes policy selection as caller-driven ("loads the policy identified by the Business Transaction") — this phase narrows that generic description for exactly 6 capabilities, without amending §2.2's wording. `CLAIMS.md` itself was not touched in this phase (out of scope; a future documentation phase should reconcile §2.2's general framing with this phase's capability-specific enforcement). - -## 11. Evidence Summary - -``` -Repository searches: grep for "new RuntimeEngine(" (2 occurrences, both accounted for), - "PolicyRouter"/"PolicyValidator"/"PolicyRegistry" importers (confirmed dead-code - duplicates), boundSignals presence across all 11 policies/*/*/policy.json files, - every action:"payments:execute"/"razorpay:refund-create"/"hubspot:deal-update" fixture - repo-wide (12 files individually checked for RuntimeEngine reachability before - implementation) - -Source references: packages/api/src/mappers/BusinessTransactionMapper.ts, - packages/policy/src/{PolicyRouter,PolicyValidator,PolicyRegistry,PolicyEngine, - SignalIntentBinder,FilePolicyRepository,CapabilityPolicyBinding}.ts, - packages/runtime/src/{RuntimeEngine,RuntimeBuilder}.ts, Dockerfile:104 - -Call graphs: before/after (§4) - -Regression tests: packages/policy/tests/unit/CapabilityPolicyBinder.test.ts (7 new), - packages/api/tests/integration/razorpay-refund.integration.test.ts (+1), - packages/api/tests/integration/hubspot-deal-update.integration.test.ts (+1) - -Build output: npx tsc -b → clean, 0 errors -Test output: npm test -- --maxWorkers=2 → 968 passed (+9), 39 skipped, 0 failed -``` - ---- - -## Final Verification - -| Item | Status | -| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Policy selection independently verified | ✓ — fresh trace, §2, §3, §4; one new finding (dead-code `PolicyRouter`/`PolicyValidator`/`PolicyRegistry` duplicates) not in the prior report | -| Canonical capability-to-policy binding established | ✓ — `CapabilityPolicyBinding.ts`, wired unconditionally into the sole production composition root | -| Caller policy substitution impossible | ✓, for the 6 bound capabilities — proven end to end (§7, §8); unbound actions unaffected by design | -| Authorization remains independent | ✓ — `PolicyEngine`, `SignalIntentBinder`, `SignalStateVerifier`, `ExecutionGate` all unchanged | -| Credential isolation unchanged | ✓ — no file under `execution-control`/`execution-gateway`/credential providers touched | -| Replay protection unchanged | ✓ — no nonce/envelope-verifier file touched; full suite passes | -| Audit generation unchanged | ✓ — no audit sink file touched | -| Runtime behavior unchanged except verified structural enforcement | ✓ — 959→968 test delta fully accounted for by 9 new tests; 0 pre-existing test broke | -| Public API compatibility preserved or explicitly documented | ✓ documented — HTTP contract (routes, request/response shapes, field names) unchanged; the _set_ of previously-succeeding requests narrows for exactly 2 capabilities that had no documented multi-policy flexibility (§9's residual-tension note, §10) | - -Supported by: repository searches, source references, call graphs, regression tests, and build output, all in §11. - -## Final Recommendation - -**TD-22 CLOSED.** - -Canonical binding did not exist (Objective B applied, not A) — independently reconfirmed from scratch, including one new finding (dead, same-named `PolicyRouter`/`PolicyValidator`/`PolicyRegistry` duplicates) the prior verification hadn't surfaced. It was introduced with the minimum change necessary: one new, small, dependency-free module in `@parmana/policy`, and a two-line wiring change in `RuntimeEngine`/`RuntimeBuilder` following this codebase's own established, backward-compatible pattern for exactly this kind of addition (matching RFC-0021/RFC-0022's precedent). No STOP condition was triggered — no existing canonical binding, no documented external dependency on caller-selected policies for these specific capabilities, and the HTTP contract itself is unchanged; the one residual tension (§10, §CLAIMS.md §2.2's general framing) is documented rather than silently papered over. Caller policy substitution for the exact exploit found during verification — and its `hubspot:deal-update` analog — is now proven impossible end to end through the real production HTTP route. Full regression (968 passed, +9 new, 0 failed) confirms zero unintended behavior change. The marketing claim's undisclosed Property B gap is closed; two separately-disclosed, differently-scoped gaps remain open, correctly out of this phase's charter, and are named explicitly rather than left implicit. diff --git a/docs/architecture/phase2l-authorization-exceptions.md b/docs/architecture/phase2l-authorization-exceptions.md deleted file mode 100644 index 94a05039..00000000 --- a/docs/architecture/phase2l-authorization-exceptions.md +++ /dev/null @@ -1,241 +0,0 @@ -# Phase 2L — Resolve Remaining Authorization Exceptions (TD-23) - -**Update, later session: both findings below are now closed.** This document's own -"TD-23 NOT RESOLVED" verdict (§ Final Recommendation) was accurate as of the commit this -phase was fixed against and is preserved below unedited as the historical record of that -phase's findings — but it no longer describes the current repository. The Razorpay daily -cumulative cap is now independently derived from `RazorpayDailyRefundLedger` -(`packages/connector-sdk/src/connectors/razorpay/RazorpayDailyRefundLedger.ts`), supplied -unconditionally to `RazorpaySignalStateVerifier` in production (TD-23 Phase 3B). HubSpot's -`preAuthorizedForAmountChange` is now independently verified against a real, signed -Approval Artifact via `ApprovalVerifier` (TD-23 Phase 3C). See `docs/CLAIMS.md` 3.4 and -3.10's own "RFC-0022 + TD-23" update paragraphs and `docs/VERIFICATION-GAPS.md` G-24's -matching update for the full mechanism and evidence — both independently re-verified -against the actual repository, not transcribed from a prior report, in the same session -that added this note. §10's flagged `RazorpayRefundService`/tutorial-61 citation staleness -was also corrected in that same later pass. - -Independently re-verifies the two authorization limitations Phase 2K's marketing-claim re-verification identified as still open: the Razorpay daily cumulative refund cap, and HubSpot's `preAuthorizedForAmountChange`. This phase was explicitly instructed not to assume either was a genuine gap, or that the prior report was correct. Both were re-derived from current source, fresh. **No production code was changed in this phase** — both findings are confirmed genuine, but each independently triggers this phase's own STOP conditions (intentional, disclosed, deliberately-scoped-out design decisions that would require a materially larger project — not a minimum-necessary fix — to close). - -**Fixed against:** commit `76d6b35` (`fix(policy): establish canonical capability-to-policy binding and close TD-22`), the tip of `main`. Working tree was not clean at the start — Phase 2K's changes were still uncommitted; per user confirmation, committed first (`76d6b35`) before this phase's own gate check was re-run and passed. - ---- - -## 1. Independent Verification Methodology - -Every finding below was re-derived from current source, treating Phase 2K's summary as a hypothesis, not a fact: - -- Re-read `packages/connector-sdk/src/connectors/razorpay/RazorpaySignalStateVerifier.ts` and `RazorpayRefundSignals.ts` in full. -- Re-read `packages/connector-hubspot/src/HubSpotDealUpdateSignals.ts` and `HubSpotSignalStateVerifier.ts` in full. -- Searched repository-wide for any cumulative-refund ledger implementation: `grep` for `CumulativeRefundLedger`, `cumulative`, `dailyTotal`, `sumRefunds`, `aggregateRefund` across `packages/storage/src`, `packages/api/src`, and the full `packages/*/src` tree — found only the two files already identified as the site of the _unverified signal_, never a computing/aggregating implementation anywhere. -- **New finding this phase, not in Phase 2K's report:** `RazorpayRefundService` and `RazorpayRefundHarness` — classes `docs/CLAIMS.md` §3.4 and several source comments still reference by name — **no longer exist as files anywhere in the repository** (confirmed by `find`; every remaining reference is prose in a comment, e.g. `RazorpayCapabilityExecution.ts:32`, `"Extracted from RazorpayRefundService, which was the first and, until [now], only consumer"`). Likewise, `examples/tutorials/61-razorpay-refund` — cited as evidence in `docs/CLAIMS.md` §3.4 ("four outcomes: approved and executed, denied by policy, replay returning the recorded result, tamper rejected") — **does not exist** as a directory. This is a documentation-staleness finding, not an authorization gap, and is out of TD-23's scope (which is about authorization behavior, not `CLAIMS.md` citation accuracy); flagged here and in §9 rather than silently noticed and dropped. -- Repository-wide grep (Task 6, §6 below) for every term the phase specified, to confirm no other instance of this pattern exists beyond the two already known. -- Re-ran the full test suite unmodified to confirm the two findings' behavior is exactly as currently tested (not stale test expectations). - -## 2. Razorpay Authorization Assessment - -**Independently confirmed: the daily cumulative cap is enforced by `PolicyEngine` against a signal value that is never independently computed or verified anywhere.** - -- `policies/razorpay-refund/1.0.0/policy.json`'s `reject-exceeds-daily-cumulative-cap` rule rejects when `dailyCumulativeAfterThisRefundPaise > 2000000`. This is a real, evaluated rule — confirmed, not assumed. -- `RazorpayRefundSignals.ts`'s `buildRazorpayRefundSignals()` computes `dailyCumulativeAfterThisRefundPaise: input.dailyCumulativeSoFarPaise + input.requestedRefundAmountPaise` — but `dailyCumulativeSoFarPaise` is a **caller-independent input parameter to this function**, not derived from anywhere inside it. -- `RazorpaySignalStateVerifier.ts`'s `VERIFIED_SIGNAL_KEYS` (`paymentStatus`, `paymentCurrency`, `refundableRemainingPaise`, `requestedExceedsRemainder`) **excludes `dailyCumulativeAfterThisRefundPaise` by name**, with its own comment: _"that fact depends on Parmana's own cumulative-refund ledger, not on anything Razorpay reports, and no ledger is wired to the production `POST /execute` path at all."_ -- No ledger, aggregation query, or any code that sums historical refund amounts exists anywhere in the repository (§1's grep). The signal reaching `PolicyEngine.evaluate()` for this fact is **exactly** what the caller declared in the HTTP request body's `signals.dailyCumulativeAfterThisRefundPaise` field, unmodified, unverified. - -**Where cumulative state originates:** nowhere authoritative. It originates as a plain field in the caller-submitted JSON request body. - -**Whether independent business state is consulted:** no, for this one fact specifically — `RazorpaySignalStateVerifier` independently consults real business state (a live Razorpay payment fetch) for four _other_ facts, but explicitly, by name, does not do so for this one. - -**Whether the previously reported limitation still exists:** yes, confirmed, unchanged by Phase 2K (which addressed policy _selection_, not this specific signal's _truth_). - -## 3. HubSpot Authorization Assessment - -**Independently confirmed: `preAuthorizedForAmountChange` is a caller-declared boolean with no independent verification anywhere, gating whether an over-threshold amount change is approved.** - -- `policies/hubspot-deal-update/1.0.0/policy.json`'s `reject-amount-exceeds-threshold-without-preauth` rule rejects when `amountChangeExceedsThreshold` is true and `preAuthorizedForAmountChange` is false — meaning `preAuthorizedForAmountChange: true` is the _sole_ thing that can turn an otherwise-rejected, over-threshold amount change into an approval. -- `HubSpotDealUpdateSignals.ts`'s `buildHubSpotDealUpdateSignals()`: `preAuthorizedForAmountChange: input.preAuthorizedForAmountChange ?? false` — passed straight through from the caller-supplied input, computed from nothing. -- `HubSpotSignalStateVerifier.ts`'s `VERIFIED_SIGNAL_KEYS` (`currentDealStage`, `dealStageChangeRequested`, `dealStageTransitionAllowed`, `amountChangeRequested`, `amountDeltaAbs`, `amountChangeExceedsThreshold`) **excludes `preAuthorizedForAmountChange` by name**, with its own comment: _"an out-of-band claim with no HubSpot-side equivalent to fetch and compare against."_ -- HubSpot's own Deal API has no property representing "this change was pre-authorized" — there is no external, authoritative source this signal could even be fetched from and compared against, unlike `dealStageTransitionAllowed` (computed from the real, fetched `dealstage`). - -**Whether the previously reported limitation still exists:** yes, confirmed, unchanged by Phase 2K. - -## 4. Authorization Traces - -**`razorpay:refund-create`:** - -``` -Request (signals + intent.parameters) - ↓ -Intent { action: "razorpay:refund-create", parameters: { paymentId, amountPaise, ... } } - ↓ -Policy Evaluation: policies/razorpay-refund/1.0.0 (now canonically bound to this action, TD-22) - ↓ -Signal Verification: RazorpaySignalStateVerifier — fetches real payment via parameters.paymentId - ↓ -Authorization Decision → Execution -``` - -| Authorization input | Classification | Evidence | -| ---------------------------------------------------------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `intent.parameters.paymentId` (the target payment) | **Independently Verified** | `RazorpaySignalStateVerifier` fetches this exact payment id from Razorpay's real API before approval | -| `signals.paymentStatus` | **Independently Verified** | compared against the real fetch's `status` | -| `signals.paymentCurrency` | **Independently Verified** | compared against the real fetch's `currency` | -| `signals.refundableRemainingPaise` | **Independently Verified** | computed from the real fetch's `amount`/`amount_refunded` | -| `signals.requestedExceedsRemainder` | **Independently Verified** | recomputed from the real fetch and the bound amount | -| `signals.requestedRefundAmountPaise` / `intent.parameters.amountPaise` | **Derived** | bound to each other by `SignalIntentBinder` (TD-22's own prerequisite, unchanged) — not independently fetched, but structurally guaranteed to equal what executes | -| `policy.name` / `policy.version` | **Independently Verified** (as of TD-22) | `CapabilityPolicyBinder` — caller-declared value must equal the canonical `razorpay-refund/1.0.0` binding or the request is rejected before evaluation | -| **`signals.dailyCumulativeAfterThisRefundPaise`** | **Caller Asserted** | no ledger, no fetch, no cross-check anywhere (§2) | - -**`hubspot:deal-update`:** - -``` -Request (signals + intent.parameters) - ↓ -Intent { action: "hubspot:deal-update", parameters: { dealId, dealstage?, amount? } } - ↓ -Policy Evaluation: policies/hubspot-deal-update/1.0.0 (canonically bound, TD-22) - ↓ -Signal Verification: HubSpotSignalStateVerifier — fetches real deal via parameters.dealId - ↓ -Authorization Decision → Execution -``` - -| Authorization input | Classification | Evidence | -| ----------------------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------- | -| `intent.parameters.dealId` | **Independently Verified** | fetched from HubSpot's real API | -| `signals.currentDealStage` | **Independently Verified** | compared against the real fetch | -| `signals.dealStageTransitionAllowed` | **Independently Verified** | recomputed from the real fetched stage and the bound proposed stage | -| `signals.amountChangeExceedsThreshold` / `amountDeltaAbs` | **Independently Verified** | recomputed from the real fetched amount and the bound proposed amount | -| `signals.proposedDealStage` / `intent.parameters.dealstage` | **Derived** | bound via `SignalIntentBinder` | -| `signals.proposedAmount` / `intent.parameters.amount` | **Derived** | bound via `SignalIntentBinder` | -| `policy.name` / `policy.version` | **Independently Verified** (as of TD-22) | `CapabilityPolicyBinder` | -| **`signals.preAuthorizedForAmountChange`** | **Caller Asserted** | no HubSpot-side equivalent exists to verify against (§3) | - -No input in either trace was classified **Unable to Certify** — every field's provenance was directly traceable to source. - -## 5. Adversarial Analysis - -| Adversarial attempt | Prevented? | Evidence | -| ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Split a refund into many small requests to individually pass the per-refund cap while exceeding the intended daily total | **Not prevented** | `dailyCumulativeAfterThisRefundPaise` is caller-declared and never independently summed across requests (§2) — direct consequence of the confirmed gap | -| Fabricate `preAuthorizedForAmountChange: true` to approve an over-threshold HubSpot amount change | **Not prevented** | §3 — no verification exists for this field at all | -| Fabricate `dailyCumulativeAfterThisRefundPaise: 0` regardless of real history | **Not prevented** | §2 | -| Replay a previously-signed authorization | **Prevented, unaffected by this phase's findings** | `ExecutionGateway`'s nonce store (single-use, consume-exactly-once) — not touched by either finding; re-confirmed unchanged (no file under `execution-gateway`/`envelope-verifier` referenced by either gap) | -| Resubmit the same `businessTransactionId` to reuse a prior approval | **Prevented, unaffected** | `BusinessTransactionService.accept()`'s uniqueness guard, throwing `DuplicateBusinessTransactionError` — confirmed still present, unchanged (`packages/runtime/src/services/business-transaction-service.ts:41`) | -| Modify the refund amount or deal stage/amount between approval and execution | **Prevented, unaffected** | `SignalIntentBinder` binds the relevant signals to `intent.parameters`; the amount/stage that executes is exactly the amount/stage the policy evaluated (unchanged, TD-22 and earlier RFC-0022 work) | -| Substitute execution parameters by pairing the capability with an unrelated policy | **Prevented, unaffected** | `CapabilityPolicyBinder` (TD-22, previous phase) — re-confirmed still wired and enforced by the unmodified full regression run (§7) | -| Modify a HubSpot deal's stage via a fabricated `dealStageTransitionAllowed` claim | **Prevented** | independently recomputed from a real fetch, cannot be fabricated (§4) | - -The two genuinely successful adversarial paths (splitting refunds; fabricating pre-authorization) are exactly, and only, the two findings this phase set out to verify — no new, additional exception was discovered beyond what Phase 2K's summary named. - -## 6. Static Audit - -Repository-wide search (`packages/*/src`, excluding tests/dist) for every term specified: - -| Term | Occurrences (non-test production source) | -| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `preAuthorized` | `HubSpotDealUpdateSignals.ts`, `HubSpotSignalStateVerifier.ts` — exactly the known finding, no others | -| `cumulative` | `RazorpayRefundSignals.ts`, `RazorpaySignalStateVerifier.ts` — exactly the known finding, no others | -| `authorizationAmount` | none | -| `approvedAmount` | none | -| `dailyLimit` | none | -| `amountChange` | `HubSpotDealUpdateSignals.ts`, `HubSpotSignalStateVerifier.ts` — same known finding (`amountChangeRequested`/`amountChangeExceedsThreshold`, both independently verified — only `preAuthorizedForAmountChange` itself is not) | -| `remainingAuthorization` | none | - -**Conclusion:** no third, previously-undiscovered instance of a caller-controlled authorization decision exists anywhere in production source. The two findings this phase set out to verify are the complete set. - -## 7. Implementation Changes - -**None.** Both findings were independently confirmed genuine (§2, §3), but each triggers this phase's own STOP conditions: - -- **Both are intentional, self-documented design decisions, not oversights.** `HubSpotDealUpdateSignals.ts`'s own doc comment: _"Absent, this defaults to false, which is the safe default: an over-threshold amount change is denied unless explicitly declared pre-authorized."_ `RazorpaySignalStateVerifier.ts`'s own comment names the exclusion explicitly and by design. Neither reads as an accidental gap; both read as a deliberately scoped boundary with a documented, safe default. -- **Both are explicitly disclosed, not hidden.** `docs/CLAIMS.md` §3.10 and §4 (Future Claims) name `preAuthorizedForAmountChange` as unverified, unimplemented, future work — _"Verifying it independently (e.g. a separately signed approval artifact, analogous to how an Execution Authorization itself is signed) is not implemented."_ `docs/VERIFICATION-GAPS.md` G-24's update explicitly names the daily cumulative cap as _"the same daily-cap TOCTOU/ledger race already identified as unaddressed accepted risk"_ — the phrase "accepted risk" is the repository's own characterization, not this phase's. -- **Closing either would require a materially larger project than "minimum necessary change," which this phase's Objective and Preserve list explicitly forbid attempting.** Independently verifying `preAuthorizedForAmountChange` would mean designing and implementing a new signed-approval-artifact primitive — a new authorization mechanism parallel to Execution Authorization itself, squarely "authorization redesign," explicitly out of scope ("Do NOT redesign authorization... Do not broaden this phase"). Independently verifying the daily cumulative cap would mean building and wiring a real, production-reachable ledger — deciding its scope (per-payment? per-account? global?), its storage, and its concurrency semantics (the TOCTOU race the repository's own docs flag as a _separate_, still-open risk even _with_ a ledger) — again a materially larger, multi-decision project, not a structural, additive fix like TD-22's `CapabilityPolicyBinder`. -- **A behavior change here would be a production compatibility change to documented, intentional functionality**, not a bug fix: removing the `preAuthorizedForAmountChange` escape hatch entirely (the only way to close it without building the larger verification mechanism) would break the one documented path for a legitimate, human-approved over-threshold change — this phase's STOP conditions instruct exactly this: _"If changing behavior would constitute a production compatibility change: STOP. Document the dependency. Recommend a versioned migration rather than changing behavior in place."_ - -**Recommended path (not performed in this phase), for each, matching the repository's own stated future-work framing:** - -- **Razorpay:** a dedicated future phase to design and wire a real cumulative-refund ledger into the production `POST /execute` path (scope decision: per-payment vs. per-account vs. global daily total; storage: likely a new Supabase table, mirroring the `razorpay_webhook_events`/`consumed_nonces` atomicity pattern already used elsewhere; concurrency: the TOCTOU race must be explicitly designed against, not assumed away). -- **HubSpot:** a dedicated future phase to design a signed pre-authorization artifact (as `docs/CLAIMS.md` itself already proposes) — a separately signed approval, verifiable independently of the caller's own assertion, analogous to how `RuntimeAuthorizationSigner` already signs the Execution Authorization itself. - -Neither recommendation was scoped, designed, or implemented here — both are explicitly future, separately-chartered work. - -## 8. Regression Validation - -No new tests were added (no code changed). Full regression re-run to confirm the tree's behavior is unchanged and matches exactly what Phase 2K left it as: - -``` -npx tsc -b → clean, 0 errors -npm test -- --maxWorkers=2 → 141 test files passed, 15 skipped (identical to Phase 2K); - 968 tests passed, 39 skipped, 0 failed (identical to Phase 2K) -``` - -`git status`/`git diff --stat` (implicit — no files were edited this phase) confirms zero production source changed. `CapabilityPolicyBinder`, replay protection, and audit generation were re-confirmed unaffected by re-running the full suite that already exercises them, not merely by inspection. - -## 9. Marketing Claim Re-verification - -Re-ran the verification for _"Even if AI has valid credentials, it still cannot execute anything your business hasn't authorized. No exceptions,"_ not assuming success: - -**Property A — Credential Isolation: HOLDS, unchanged.** No file under `execution-control`/`execution-gateway`/credential providers was touched by this or the prior phase. - -**Property B — Independent Authorization: two confirmed, disclosed exceptions remain.** The undisclosed structural gap (TD-22, policy selection) is closed. The two facts this phase investigated — the Razorpay daily cumulative total and HubSpot's `preAuthorizedForAmountChange` — remain caller-asserted with no independent verification, exactly as before this phase, by deliberate decision (§7), not oversight. - -**Property C — Structural Enforcement: HOLDS for policy selection** (unchanged from Phase 2K) but **does not extend to these two specific signal values** — enforcing _which_ policy governs a capability is now structural; enforcing that _every fact that policy evaluates_ is true is not, and was never this phase's charter to build. - -**Overall claim status, honestly stated:** the claim remains **not fully supported** in the unqualified "No exceptions" sense. Two named, real exceptions exist, both disclosed in the repository's own documentation, both requiring genuinely new capability (not a fix) to close. This is a narrower, better-understood gap than before Phase 2K (one severe, undisclosed structural bypass is gone; what remains is exactly what the repository's own `CLAIMS.md`/`VERIFICATION-GAPS.md` already say remains) — but "No exceptions" is still stronger than what current implementation proves for these two facts. - -## 10. Remaining Limitations - -- The two confirmed exceptions (§2, §3) remain open, by deliberate decision, not oversight — recommended future-phase paths given in §7. -- **New finding, out of TD-23's scope, not corrected here:** `docs/CLAIMS.md` §3.4 cites `examples/tutorials/61-razorpay-refund` and the classes `RazorpayRefundService`/`RazorpayRefundHarness` as evidence — none of these exist in the current repository (§1). This is a documentation-accuracy gap in the same family as TD-21 (Phase 2J), not an authorization gap, and is flagged here for a future documentation-verification pass rather than corrected in this authorization-scoped phase. -- No new static-audit exception was found beyond the two already known (§6) — this phase's search was exhaustive for the specified terms, not a guarantee no other differently-named pattern exists; a future phase could usefully extend this search to less obviously-named caller-declared attestations (e.g., `vendorVerified`/`invoiceVerified`/`paymentApproved`/`sufficientFunds`/`riskScore` on the `vendor-payment` policy, already disclosed in `VERIFICATION-GAPS.md` G-24 as unverified and out of scope there too, and unaffected by this phase since `payments:execute` is not currently reachable in production at all, TD-1). - -## 11. Evidence Summary - -``` -Repository searches: grep for CumulativeRefundLedger/cumulative/dailyTotal/sumRefunds/ - aggregateRefund (packages/storage/src, packages/api/src, full packages/*/src — no ledger - found); grep for preAuthorized/cumulative/authorizationAmount/approvedAmount/dailyLimit/ - amountChange/remainingAuthorization (§6, exhaustive); find for RazorpayRefundService.ts/ - RazorpayRefundHarness.ts/examples/tutorials/61-razorpay-refund (none exist) - -Source references: packages/connector-sdk/src/connectors/razorpay/{RazorpaySignalStateVerifier, - RazorpayRefundSignals}.ts, packages/connector-hubspot/src/{HubSpotDealUpdateSignals, - HubSpotSignalStateVerifier}.ts, policies/{razorpay-refund,hubspot-deal-update}/1.0.0/ - policy.json, packages/runtime/src/services/business-transaction-service.ts:41 - -Documentation references: docs/CLAIMS.md §3.10, §4 (Future Claims); docs/VERIFICATION-GAPS.md - G-24 update ("accepted risk") - -Authorization traces: §4 (both capabilities, every input classified) - -Build output: npx tsc -b → clean, 0 errors -Test output: npm test -- --maxWorkers=2 → 968 passed, 39 skipped, 0 failed — identical to - Phase 2K's final count, confirming zero code change -``` - ---- - -## Final Verification - -| Item | Status | -| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Remaining authorization findings independently verified | ✓ — both re-derived from current source, not assumed (§2, §3); one new finding (stale `RazorpayRefundService`/tutorial-61 citations) surfaced along the way | -| Every confirmed gap resolved (if required) | N/A by design — both confirmed genuine but trigger STOP conditions; not resolved, deliberately, with reasoning and a recommended future path (§7) | -| Credential isolation unchanged | ✓ — no file touched | -| Canonical Capability → Policy binding unchanged | ✓ — no file touched; full regression re-confirms it still enforces (razorpay/hubspot integration tests from Phase 2K still pass) | -| Authorization independently verified | Partial — every input classified (§4); two remain Caller Asserted by confirmed, disclosed design | -| Replay protection unchanged | ✓ — no file touched | -| Audit generation unchanged | ✓ — no file touched | -| Runtime behavior unchanged except for verified authorization enforcement | ✓ — no enforcement change was made this phase; behavior is byte-for-byte identical to Phase 2K (968/968, 0 failed) | -| Public API compatibility preserved or explicitly documented | ✓ — no change made; the compatibility tension that _would_ arise from closing either gap (removing the `preAuthorizedForAmountChange` escape hatch) is documented (§7) rather than acted on | - -Supported by: repository searches, source references, authorization traces, and build/test output, all in §11. - -## Final Recommendation - -**Superseded — see the update note at the top of this document. TD-23 is now resolved, -both halves, in later sessions this document predates.** - -**TD-23 NOT RESOLVED.** _(as of this phase; preserved as written for the historical record)_ - -Both reported exceptions were independently confirmed genuine — not incorrect, not already fixed, not superseded by Phase 2K's work. Per this phase's own STOP conditions, no implementation change was made: both are intentional, self-documented design decisions with a stated safe default (not oversights), both are explicitly disclosed in `docs/CLAIMS.md`/`docs/VERIFICATION-GAPS.md` as accepted risk / future work, and closing either would require designing genuinely new authorization capability — a signed pre-authorization artifact; a real cumulative-refund ledger with a deliberate concurrency design — squarely outside "minimum necessary change" and this phase's explicit "do not redesign authorization" boundary. This is a deliberate, evidence-based STOP, not a failure to attempt a fix: the adversarial analysis (§5) confirms exactly these two paths succeed and no others, the static audit (§6) confirms no third instance of this pattern exists anywhere in the codebase, and a recommended, properly-scoped path is given for each (§7) rather than left implicit. The marketing claim's "No exceptions" wording remains unsupported for these two specific, named facts; every other property this phase checked — credential isolation, replay protection, audit generation, capability-to-policy binding, and every other authorization input across both capabilities' full traces — holds. diff --git a/docs/architecture/phase3a-authorization-artifact-design.md b/docs/architecture/phase3a-authorization-artifact-design.md deleted file mode 100644 index 78caa3a6..00000000 --- a/docs/architecture/phase3a-authorization-artifact-design.md +++ /dev/null @@ -1,368 +0,0 @@ -# Phase 3A — Authorization Artifact Design - -An architecture and specification phase only. **No production source code changed. No public API changed.** This document is a frozen specification for a new cryptographic artifact — to be implemented, if at all, in a separately-chartered future phase. - -**Fixed against:** commit `72e6da5` (`docs(architecture): independently re-verify remaining authorization exceptions (TD-23)`), the tip of `main`. Working tree was clean before this phase began. - ---- - -## 1. Problem Statement - -Phase 2L (§2, §3, read directly — see §3 below) independently confirmed two authorization facts that `PolicyEngine` evaluates but that are never independently verified: the Razorpay `razorpay-refund/1.0.0` policy's `dailyCumulativeAfterThisRefundPaise`, and the HubSpot `hubspot-deal-update/1.0.0` policy's `preAuthorizedForAmountChange`. Both are caller-declared, and both directly gate whether a real, fund-moving or record-mutating execution is approved. This phase's charter is to determine — from repository evidence, not assumption — whether closing either gap requires a genuinely new cryptographic authorization primitive, or whether existing infrastructure can simply be extended. - -## 2. Independent Verification Summary - -This phase did not re-derive the two findings from scratch a third time — Phase 2L already did that independently, twice removed from the original marketing-claim verification (Phase 2K's own report, itself independently re-derived from the original verification task). Instead, this phase's Precondition step (§3) independently confirmed Phase 2L's report exists, is committed, and is readable, and re-read its conclusions directly rather than trusting a summary of it. Its findings are treated as established fact for the purposes of this design phase's own charter (deciding what, if anything, to build in response), not re-litigated. - -## 3. Phase 2L Scope Verification - -Read directly from `docs/architecture/phase2l-authorization-exceptions.md` (commit `72e6da5`), not from any summary: - -- **Exactly two capabilities violated Property B:** `razorpay:refund-create` (§2 of that report) and `hubspot:deal-update` (§3 of that report). -- **Exactly which fields were caller-controlled:** `signals.dailyCumulativeAfterThisRefundPaise` (Razorpay) and `signals.preAuthorizedForAmountChange` (HubSpot) — named explicitly, nothing broader. -- **Whether these are genuinely separate problems or one architectural limitation:** Phase 2L's own text (§7) already draws a distinction worth taking seriously: it recommends _"a dedicated future phase to design and wire a real cumulative-refund ledger"_ for Razorpay, and _"a dedicated future phase to design a signed pre-authorization artifact"_ for HubSpot — two different nouns (a ledger; an artifact), not one. This phase's own independent analysis (§4 below) confirms Phase 2L's implicit distinction was correct: **these are not the same problem.** One is a missing internal aggregation over data Parmana already owns and has already cryptographically committed to (its own historical Trust Records). The other is a missing external attestation from a party that is neither the AI caller nor Parmana's own runtime — a fact no existing system, internal or external, currently records at all. - -## 4. Existing Infrastructure Assessment - -Read directly, fresh, for this phase (not assumed from any prior phase's inventory): - -| Primitive | File(s) | What it does | Reusable as-is for a new artifact? | -| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Canonical serialization | `packages/crypto/src/CanonicalSerializer.ts` | Deterministic, key-sorted, UTF-8 byte serialization of any object | **Yes** — algorithm-agnostic, no changes needed | -| Deterministic hashing | `packages/crypto/src/TrustRecordHasher.ts`, `ExecutableContentHasher.ts` | Canonical-serialize then hash; the exact mechanism `businessTransactionHash` uses | **Yes** — the same hasher can bind any new payload shape | -| Signing | `packages/crypto/src/ArtifactSigner.ts` | Canonical-serialize, then `crypto.signature.sign(bytes, privateKey)` | **Yes** — takes `unknown`, no payload-shape assumption | -| Signature verification | `packages/crypto/src/SignatureVerifier.ts` | Canonical-serialize, then `crypto.signature.verify(bytes, signature, publicKey)` | **Yes** — same, payload-shape-agnostic | -| Signature algorithms | `packages/shared/src/config/CryptoAlgorithms.ts` | `ed25519` (default), `dilithium3`/ML-DSA-65 (configurable) | **Yes** — `SignatureAlgorithm` type reusable verbatim | -| Execution Authorization envelope | `packages/shared/src/domain/execution-authorization.ts`, `packages/crypto/src/{AuthorizationSigner,AuthorizationVerifier}.ts` | Signed by **Parmana's own runtime key** (`FileKeyProvider`, `DEFAULT_KEY_ID`), issued **after** `PolicyEngine.evaluate` approves, proves "Parmana's runtime approved this decision" | **Not directly** — this artifact attests to Parmana's own decision, not an independent third party's; reusing its _signing key_ would make the new artifact self-certifying (the AI's own request would end up "approved" by the same process that needs to consult independent evidence) | -| Nonce / replay protection | `packages/envelope-verifier/src/NonceStore.ts` (`checkAndRecord(nonce, expiresAt): Promise`) | Atomic burn-on-first-use, TTL-bounded | **Yes** — the interface itself is payload-agnostic; a new artifact can burn nonces through the exact same interface (a separate logical store/table to avoid colliding with Gateway authorization nonces, discussed in §6) | -| Second existing signed-artifact precedent | `packages/shared/src/domain/refusal-record.ts` (RFC-0021) | A durable, signed artifact distinct from `ExecutionTrustRecord`/Receipt, proving a policy REJECT — still signed with Parmana's own key, but establishes that **this codebase already treats "more than one kind of signed domain artifact" as an ordinary, existing pattern**, not a foreign concept | Precedent, not directly reusable (same-key limitation as Execution Authorization) | -| Trusted-identity registry precedent | `packages/api/src/bootstrap/createConnectorAuthenticator.ts` | A hardcoded, server-side list of `{connectorId, publicIdentity}` pairs, checked by `SignedTokenConnectorAuthenticator` | **Precedent to follow** for a new "trusted approval issuer" registry (§9) — same shape of problem (verify a claimed identity against a small, server-controlled trust list), not literally reusable code | -| Policy condition operators | `packages/policy/src/OperatorEvaluator.ts`, `PolicyOperator` enum (`eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `between`, ...) | The vocabulary `PolicyEngine` already uses to compare a fact against a literal | **Yes** — directly reusable as the new artifact's own scope-comparison vocabulary (§7), avoiding a second, parallel comparison DSL | -| `SignalStateVerifier` extension point | `packages/policy/src/types/SignalStateVerifier.ts`, `CompositeSignalStateVerifier.ts` | Optional, capability-scoped, independently re-derives specific facts and overrides an APPROVE to REJECT on mismatch — the exact seam TD-23's two findings live inside today | **Yes, for the Razorpay ledger case** — extending an _existing_ `RazorpaySignalStateVerifier` implementation to also independently recompute `dailyCumulativeAfterThisRefundPaise` from Parmana's own storage is architecturally identical to what it already does for four other facts | -| `ExecutionTrustRecordRepository` | `packages/shared/src/repositories/execution-trust-record-repository.ts` | Durable store of every approved execution — **but its interface offers only `findByTransactionId(id)`, no query/aggregation by date range or action** | Data already exists and is already cryptographically committed (each record's own `trustRecordHash`/`signature`); the _interface_ would need one new query method — an extension, not a new primitive | - -**Conclusion, per finding, supported by the table above:** - -- **Razorpay daily cumulative cap:** the fact this policy needs (a sum of today's already-approved, already-signed, already-stored refund amounts) is data Parmana **already owns and has already cryptographically committed to**. No new signing party, no new trust boundary, no new artifact type is needed — only a new query/aggregation capability layered onto the existing `ExecutionTrustRecordRepository` and consumed by the existing, already-extensible `RazorpaySignalStateVerifier`. This is **Existing Infrastructure Extension**. -- **HubSpot `preAuthorizedForAmountChange`:** the fact this policy needs (did some independent, authoritative party — a human manager, a business approval workflow — actually approve this specific over-threshold change) is **not data Parmana owns today, in any form, signed or not**. No existing artifact represents "an entity other than the AI caller and other than Parmana's own runtime attests to a specific, bounded fact." Reusing the Execution Authorization's signing key would not add security value, because that key represents Parmana's own runtime's decision, not an independent party's — using it to also sign the caller's own claim would be Parmana certifying its own input, not verifying someone else's. This gap **cannot** be closed by extending `AuthorizationVerifier`, `SignalStateVerifier`, or any other existing verification component without first inventing the artifact those components would then verify. This requires **a new authorization primitive** — built, per the table above, almost entirely from existing cryptographic building blocks, but a new payload shape, a new trust boundary, and a new issuance/verification integration point nonetheless. - -## 5. Decision Gate Outcome - -**Split verdict, per finding, each independently supported by repository evidence (§4):** - -- **Razorpay daily cumulative cap → PATH A, Existing Infrastructure Extension.** **DESIGN NOT REQUIRED** for this finding. No Authorization Artifact is designed or needed here. **Recommended, separately-chartered implementation phase:** add one query method to `ExecutionTrustRecordRepository` (both `MemoryExecutionTrustRecordRepository` and `SupabaseExecutionTrustRecordRepository` implementations) that sums `intent.parameters.amountPaise` across today's approved `razorpay:refund-create` executions (scope decision left to that phase: per-payment-account vs. global daily total — Phase 2L's own recommendation already flagged this as an open scope question); extend `RazorpaySignalStateVerifier` to add `dailyCumulativeAfterThisRefundPaise` to its own `VERIFIED_SIGNAL_KEYS`, computing the "actual" value from this new query exactly as it already does for `paymentStatus`/`paymentCurrency`/`refundableRemainingPaise`/`requestedExceedsRemainder`. The TOCTOU/concurrency race Phase 2L flagged (two concurrent refunds each reading a stale cumulative total before either commits) is a real, separate design question that phase must resolve explicitly (e.g., an atomic increment-and-check against a dedicated ledger row, mirroring the `consumed_nonces`/`razorpay_webhook_events` primary-key-as-atomicity pattern already used elsewhere in this codebase) — not assumed away by a naive sum-then-compare query. -- **HubSpot `preAuthorizedForAmountChange` → PATH B, New Authorization Primitive Required.** The remainder of this document (§6 onward) is the frozen specification for that primitive. - -This document's remaining sections are scoped to the HubSpot-class problem — **and, by design, to any future policy that needs an independent, external, bounded approval of a fact `PolicyEngine` cannot itself verify** (the artifact is deliberately generalized, not HubSpot-specific — see §6's `capability`/`resourceId`/`scope` fields). It is **not** a design for the Razorpay ledger, which needs no new artifact at all. - ---- - -## 6. Requirements Matrix - -Every authorization decision the repository currently performs in production (the 6 capabilities canonically bound to a policy, TD-22), every input, and its classification — re-derived directly from current source for this phase, extending (not merely repeating) Phase 2L's own trace: - -| Capability | Resource | Scope | Business object | Input | Classification | -| ------------------------------------------------------------------------------------- | ------------------------ | ---------------------- | ------------------------------ | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `razorpay:payment-fetch` | Razorpay payment (read) | read-only | `parameters.paymentId` | `paymentId` | Structural (bound to `intent`, execution is read-only) | -| `razorpay:refund-fetch` | Razorpay refund (read) | read-only | `parameters.refundId` | `refundId` | Structural | -| `razorpay:refund-create` | Razorpay refund (create) | write, fund-moving | `parameters.paymentId` | `paymentId` | Business-State Verified (real Razorpay fetch, `RazorpaySignalStateVerifier`) | -| — | — | — | — | `paymentStatus`, `paymentCurrency`, `refundableRemainingPaise`, `requestedExceedsRemainder` | Business-State Verified | -| — | — | — | — | `requestedRefundAmountPaise` / `parameters.amountPaise` | Cryptographically Verified (bound via `SignalIntentBinder`, itself covered by the signed `businessTransactionHash`) | -| — | — | — | — | `policy.name`/`.version` | Structural (`CapabilityPolicyBinder`, TD-22) | -| — | — | — | — | **`dailyCumulativeAfterThisRefundPaise`** | **Caller Asserted** — see §5's Path A recommendation | -| `hubspot:deal-fetch` | HubSpot deal (read) | read-only | `parameters.dealId` | `dealId` | Structural | -| `hubspot:deal-update` | HubSpot deal (update) | write, record-mutating | `parameters.dealId` | `dealId` | Business-State Verified (real HubSpot fetch, `HubSpotSignalStateVerifier`) | -| — | — | — | — | `currentDealStage`, `dealStageTransitionAllowed`, `amountChangeExceedsThreshold`, `amountDeltaAbs` | Business-State Verified | -| — | — | — | — | `proposedDealStage`/`parameters.dealstage`, `proposedAmount`/`parameters.amount` | Cryptographically Verified (bound via `SignalIntentBinder`) | -| — | — | — | — | `policy.name`/`.version` | Structural (`CapabilityPolicyBinder`, TD-22) | -| — | — | — | — | **`preAuthorizedForAmountChange`** | **Caller Asserted** — this document's subject | -| `payments:execute` (vendor-payment, **not currently registered in production, TD-1**) | vendor-payment (execute) | write, fund-moving | `parameters.target`/`vendorId` | `vendorVerified`, `invoiceVerified`, `paymentApproved`, `sufficientFunds`, `riskScore` | **Caller Asserted** — disclosed in `docs/VERIFICATION-GAPS.md` G-24 as unaddressed, out of scope there too; not currently exploitable in production since this capability is not registered (TD-1), but the same class of gap, and directly why this artifact's design (§7) is kept general rather than HubSpot-specific | - -No input in this matrix was classified **Unable to Certify** — every field's provenance is directly traceable to source, confirmed fresh for this phase. - -**Note on generality:** the `payments:execute` row is included specifically because it demonstrates this is not a two-instance, capability-specific problem — it is a general shape ("an attestation about business trustworthiness or approval that no fetchable external system records") that recurs, and the artifact this document specifies is designed to cover all three occurrences, not only HubSpot's. - ---- - -## 7. Authorization Artifact Specification - -**Name:** `ApprovalArtifact` (payload: `ApprovalPayload`; signed envelope: `SignedApproval`) — naming mirrors `ExecutionAuthorizationPayload`/`SignedExecutionAuthorization` exactly, for consistency with this codebase's existing convention. - -### 7.1 Payload fields - -| Field | Type | Mandatory | Description | -| ------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `version` | `1` (literal) | **Yes** | Payload format version. Verifiers MUST reject any other value (including missing) before attempting signature verification — identical discipline to `ExecutionAuthorizationPayload.version`. | -| `approvalId` | `string` (UUID v4) | **Yes** | Unique identifier for this artifact. | -| `issuer.approverId` | `string` | **Yes** | Identifies the approving party — a human role, an approval-workflow system identity, or equivalent. **Never** the AI caller's own `callerId`, and **never** Parmana's own runtime identity. | -| `issuer.keyId` | `string` | **Yes** | Identifies which of that approver's registered public keys signed this artifact (an approver may rotate keys; mirrors `SignedExecutionAuthorization.keyId`'s purpose exactly). | -| `issuedAt` | `string` (ISO-8601 UTC) | **Yes** | When the approval was granted. | -| `expiresAt` | `string` (ISO-8601 UTC) | **Yes** | Verifiers MUST reject an artifact past this time — identical discipline to `ExecutionAuthorizationPayload.expiresAt`. No default; an approver-chosen, bounded value, deliberately short-lived relative to the business process it represents (an hour to a few days, not months). | -| `capability` | `string` | **Yes** | The exact capability (`intent.action`) this approval is scoped to — e.g. `"hubspot:deal-update"`. Prevents capability substitution (§8). | -| `resourceId` | `string` | **Yes** | The exact business object this approval covers, in the same identifier shape the capability's own `intent.parameters` carries (e.g. a HubSpot `dealId`) — not a wildcard, not a pattern. Prevents authorization transfer (§8). | -| `scope.field` | `string` | **Yes** | The name of the specific policy-evaluated fact this artifact attests to — e.g. `"amountChangeExceedsThreshold"`'s underlying quantity, or more precisely the delta itself, `"amountDeltaAbs"`. Exactly one fact per artifact — an approval is never a blanket "everything about this request is fine." | -| `scope.comparator` | one of `PolicyOperator` (`packages/policy/src/types/Policy.ts`) restricted to `eq`, `lte`, `gte`, `lt`, `gt`, `between` | **Yes** | Reuses the exact comparator vocabulary `PolicyEngine`'s own `OperatorEvaluator` already implements and this codebase already tests — no new comparison DSL. | -| `scope.value` | `number \| string \| { min: number; max: number }` (shape depends on `comparator`) | **Yes** | The bound being approved — e.g. `{ comparator: "lte", value: 50000 }` meaning "approved for an amount delta up to 50,000." | -| `constraints` | `Readonly>` | Optional | Reserved, additive extension point for future scope dimensions (e.g. a specific allowed target `dealstage`) not yet needed for the HubSpot case; absent by default. | -| `nonce` | `string` (UUID v4) | **Yes** | Single-use. Burned on first successful verification (§10) — reuses the existing `NonceStore` interface verbatim. | - -### 7.2 Envelope fields (`SignedApproval`) - -| Field | Type | Mandatory | Description | -| ----------- | ---------------------------------------------------------------------------------------------------------------- | --------- | -------------------------------------------------------- | -| `payload` | `ApprovalPayload` | **Yes** | The signed content, §7.1. | -| `signature` | `Signature` (`packages/shared/src/domain/signature.ts` — `{algorithm, keyId, value, signedAt}`, reused verbatim) | **Yes** | Signature over the canonical serialization of `payload`. | - -**Mandatory vs. optional, summarized:** every field in §7.1 is mandatory except `constraints`. There is no mandatory/optional distinction in §7.2 — both fields are always present in a well-formed artifact. - -### 7.3 Trust boundaries - -- The **issuer's signing key is never Parmana's own runtime key** (`FileKeyProvider`'s `DEFAULT_KEY_ID`). It is a separate keyspace, provisioned per-approver, verified against a small, explicit, server-side trust registry (§9) — the same shape of trust list `createConnectorAuthenticator.ts` already uses for trusted connector identities, applied here to trusted approval issuers instead. -- The **AI caller never holds or can construct a valid `SignedApproval`.** It can only attach an artifact it received out-of-band (from the approver) to its request; it cannot forge one (no private key), modify one (signature covers the full canonical payload), or manufacture one for a resource/amount it wasn't given (§8). -- **Parmana's runtime is a verifier of this artifact, not its issuer** — symmetrical to how `EnvelopeVerifier`/`AuthorizationVerifier` already verify artifacts Parmana itself did not sign (the receiving-system-independent-verification pattern `docs/CLAIMS.md` §2.9/§3.1 already establishes for the Execution Authorization itself, applied here in the _other_ direction: Parmana is the receiver, an external approver is the issuer). - -## 8. Canonical Serialization - -Reused verbatim: `packages/crypto/src/CanonicalSerializer.ts`. `ApprovalPayload` is serialized via `serialize()` before signing and before verification — recursive key-sorting, `Date`-to-ISO-string normalization, UTF-8 encoding, identical to how `ExecutionAuthorizationPayload` and `RefusalRecord` are already serialized. No new serialization logic is specified or needed; this is a direct reuse, not an extension. - -## 9. Signature Model - -- **Algorithm:** either `ed25519` or `dilithium3` (ML-DSA-65/FIPS 204), per `packages/shared/src/config/CryptoAlgorithms.ts`'s existing `SignatureAlgorithm` type — an issuer may use either, exactly as `SIGNATURE_PROVIDER` already lets Parmana's own runtime choose either for its own signing. No new algorithm is introduced. -- **Signing:** `ArtifactSigner.sign(payload, issuerPrivateKey)` — reused verbatim; the _only_ difference from how `AuthorizationSigner` uses it is _whose_ private key is passed in. -- **Issuer key registry (new, small, precedented):** a server-side, explicit list — structurally identical to `createConnectorAuthenticator.ts`'s trusted-connector-identity list — mapping `(approverId, keyId) → publicKey`. Provisioning and rotation of this registry is deliberately **out of scope** for this design (§16, Open Questions) — this document specifies the artifact and its verification, not the issuer key management operational process, which is a distinct concern (analogous to how `KEY-MANAGEMENT.md` already separates key custody from the artifacts that key signs). - -## 10. Verification Algorithm - -Deterministic, fixed-order, no early return between independent checks (mirroring `AuthorizationVerifier.verify()`'s own stated no-timing-oracle discipline exactly): - -``` -function verifyApprovalArtifact( - artifact: SignedApproval, - request: { action: string; resourceId: string; requestedValue: JsonValue }, - trustedIssuerKeys: (approverId, keyId) => KeyObject | undefined, - nonceStore: NonceStore, - revokedApprovals: RevokedApprovalStore, // §12 - now: Date, -): ApprovalVerificationResult { - - // 1. Version gate — fails closed first, exactly like AuthorizationVerifier. - // Not signature/secret-dependent, so short-circuiting here introduces - // no timing oracle (identical reasoning to the existing verifier). - if (artifact.payload.version !== 1) { - return { valid: false, checks: { versionSupported: false, ... rest: false } }; - } - - // 2. Every remaining check runs unconditionally, in this fixed order, - // with no early return between them. - const publicKey = trustedIssuerKeys(artifact.payload.issuer.approverId, artifact.payload.issuer.keyId); - const signatureVerified = publicKey !== undefined - ? await SignatureVerifier.verify(artifact.payload, artifact.signature, publicKey) - : false; // an unresolvable issuer is a verification failure, not a distinct error path - - const notExpired = now.getTime() < Date.parse(artifact.payload.expiresAt); - - const notRevoked = !(await revokedApprovals.isRevoked(artifact.payload.approvalId)); - - const capabilityMatches = artifact.payload.capability === request.action; - - const resourceMatches = artifact.payload.resourceId === request.resourceId; - - const scopeSatisfied = OperatorEvaluator.evaluate( - request.requestedValue, - artifact.payload.scope.comparator, - artifact.payload.scope.value, - ); - - // 3. Nonce consumption is attempted LAST, and only recorded as the - // deciding factor once every other check has already independently - // passed -- mirroring ExecutionGateway's own "nonce consumption - // attempted only after every other check passes" ordering exactly - // (see docs/CLAIMS.md 2.21's isSoleFailureNonceReplay reasoning). - // An artifact that fails on any other ground never burns its nonce, - // preserving a legitimate retry with a corrected request. - const priorChecksPassed = - signatureVerified && notExpired && notRevoked && capabilityMatches && resourceMatches && scopeSatisfied; - - const nonceUnseen = priorChecksPassed - ? await nonceStore.checkAndRecord(artifact.payload.nonce, artifact.payload.expiresAt) - : false; - - return { - valid: priorChecksPassed && nonceUnseen, - checks: { - versionSupported: true, - signatureVerified, - notExpired, - notRevoked, - capabilityMatches, - resourceMatches, - scopeSatisfied, - nonceUnseen, - }, - }; -} -``` - -**Every result is deterministic:** the same `(artifact, request, trustedIssuerKeys, now)` input always produces the same `checks` object (nonce/revocation state aside, which are the intentional, explicit, stateful exceptions any single-use or revocable primitive must have — identical in kind to the existing `NonceStore`'s own state-dependent determinism). - -## 11. Security Properties - -| Property | How the artifact prevents it | Evidence/reasoning | -| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Forged authorization | `signatureVerified` requires a valid signature under a _registered, trusted_ issuer key — the AI holds no such key | §9, §10 step 2 | -| Modified authorization | Signature covers the full canonical serialization of `payload`; any bit-flip (amount, resourceId, capability, expiry) invalidates it | §8, existing `SignatureVerifier` semantics, unchanged | -| Capability substitution | `capabilityMatches`: `payload.capability` must equal `request.action` exactly | §10 step 2 | -| Scope escalation | `scopeSatisfied`: the _actual_ requested value (derived from `intent.parameters`, not the caller's own declared signal) must satisfy `payload.scope`'s bound | §10 step 2 — critically, this reuses the same "compare against the real executed value, not the caller's claim" discipline `SignalIntentBinder` already established | -| Amount modification | Same as scope escalation — `scope.value`'s numeric bound is checked against the real amount that would execute | §10 step 2 | -| Replay | `nonceUnseen`: burn-on-first-successful-use via the existing `NonceStore` interface | §10 step 3 | -| Stale authorization | `notExpired`: `expiresAt` checked against `now` | §10 step 2 | -| Duplicate authorization | Same mechanism as replay — a second attempt with the same `approvalId`/`nonce` fails `nonceUnseen` | §10 step 3 | -| Authorization transfer (to another business object) | `resourceMatches`: `payload.resourceId` must equal the request's real resource identifier exactly | §10 step 2 | - -## 12. Lifecycle - -``` -Authorization Request - ↓ (a human/business process, outside Parmana, decides an over-threshold - ↓ change should be permitted for a specific deal/amount) -Approval - ↓ (the approver — a human role or business system with a registered - ↓ signing key — decides to grant it, scoped to one capability, one - ↓ resourceId, one bounded fact) -Artifact Creation - ↓ (an ApprovalPayload is constructed: version, approvalId, issuer, - ↓ issuedAt, a short expiresAt, capability, resourceId, scope, nonce) -Signing - ↓ (ArtifactSigner.sign(payload, issuerPrivateKey) -- reused verbatim, §9) -Storage - ↓ (the signed artifact is handed to whatever system will submit the - ↓ Business Transaction -- out of scope how; Parmana does not store - ↓ unconsumed artifacts, mirroring how it does not store unconsumed - ↓ Execution Authorizations either) -Distribution - ↓ (the artifact reaches the AI/caller through whatever channel the - ↓ approval workflow uses -- out of scope for this design, §16) -Verification - ↓ (§10's algorithm runs as part of policy/signal verification, - ↓ integrated as described in §13, before the provisional decision - ↓ is finalized) -Execution - ↓ (unchanged -- the connector executes exactly as it does today, - ↓ using intent.parameters, never the artifact itself) -Consumption - ↓ (the nonce is burned at the moment §10's algorithm returns valid; - ↓ a second presentation of the same artifact fails nonceUnseen) -Expiration - ↓ (an artifact whose expiresAt has passed fails notExpired regardless - ↓ of whether it was ever consumed) -Revocation - (an operator may add approvalId to the RevokedApprovalStore, §14, - at any point before consumption -- a revoked artifact fails notRevoked - even if unexpired and unconsumed) -``` - -Every state transition above is a **verification-time check** (§10), not a stateful workflow Parmana itself drives — Parmana never initiates, approves, or distributes an artifact; it only verifies one presented to it, exactly as it already does for the Execution Authorization it _does_ issue (the direction is simply reversed: there, Parmana is issuer and a receiving system is verifier; here, an external approver is issuer and Parmana is verifier). - -## 13. Repository Integration - -**Integration points only — no existing component is redesigned:** - -- **`RuntimeEngine`:** a new, optional, trailing constructor parameter (exactly the established pattern from RFC-0021's `RefusalRecordBuilder`, RFC-0022's `SignalStateVerifier`, and TD-22's `CapabilityPolicyBinder`) — e.g. `approvalVerifier?: ApprovalVerifier`. When a policy rule's fact depends on an artifact-backed field (identified by a new, analogous opt-in policy declaration, mirroring `boundSignals`'s own opt-in shape), `RuntimeEngine.execute()` would look up the field's real value via this verifier before `PolicyEngine.evaluate()` runs — architecturally the same insertion point TD-22's `CapabilityPolicyBinder` already uses (after policy load, before `SignalIntentBinder`). -- **`PolicyEngine`:** untouched. It continues to evaluate whatever `signals` object it is given — the artifact-verification step would _populate_ `signals.preAuthorizedForAmountChange` (or an equivalent) from a verified artifact instead of trusting the caller's own declared value, exactly as `SignalStateVerifier` already overrides caller-declared signals with independently-derived ones today. -- **`ExecutionGateway`:** untouched. The artifact is verified _before_ the Gateway's own authorization/nonce/signature checks run (it is a policy-evaluation-time concern, not an execution-gateway-time concern) — a structurally separate trust boundary from the Execution Authorization the Gateway already verifies. -- **Replay Protection (`NonceStore`):** reused, not modified. A new, logically separate nonce namespace (or a new Supabase table mirroring `consumed_nonces`'s exact primary-key-as-atomicity shape) is required so artifact nonces cannot collide with or be confused with Gateway authorization nonces — an operational detail (§16), not an interface change. -- **Capability Registry:** untouched. `ConnectorRegistry`/`GatewayConnectorRegistry` have no awareness of this artifact at all — it is consumed entirely within the policy-evaluation stage, upstream of connector resolution. -- **Audit Pipeline:** extended, not redesigned — an `ExecutionAuditSink`-shaped event (mirroring the existing `execution.completed`/`execution.rejected` events `SessionCredentialSecureConnector` already emits) should record every artifact verification attempt (`approval.verified`/`approval.rejected`, naming `approvalId`, `issuer.approverId`, and which check failed) — the same "audit every accept/reject outcome" discipline `docs/CLAIMS.md` §2.16/§2.19 already establishes for caller authentication. -- **Canonical Capability → Policy Binding (TD-22):** unchanged and unaffected — `CapabilityPolicyBinder` continues to enforce which policy governs a capability; this artifact is consumed _within_ that already-correctly-selected policy's evaluation, one layer downstream. - -## 14. Failure Semantics - -Every failure fails closed — no path returns an approval on ambiguous or missing evidence: - -| Failure | Behavior | -| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Missing artifact | Treated exactly as today's safe default (`preAuthorizedForAmountChange` absent → `false`) _unless_ a future policy declares the artifact mandatory for a given rule, in which case its absence is itself a rejection, not a default | -| Expired artifact | `notExpired: false` → overall `valid: false` → ordinary policy REJECT, no rule evaluated on faith | -| Malformed artifact (fails schema validation before verification is even attempted) | Rejected before §10 runs at all, logged distinctly from "no artifact provided" for operator visibility, but has the identical policy effect (REJECT) | -| Invalid signature | `signatureVerified: false` → `valid: false` | -| Capability mismatch | `capabilityMatches: false` → `valid: false` — this artifact does not apply to this action, regardless of how well-formed or validly signed it otherwise is | -| Scope mismatch (wrong fact) | `scopeSatisfied` is evaluated against the _declared_ `scope.field`; a policy consulting a different fact than the artifact covers cannot use it at all — a configuration-time, not runtime, concern (§16) | -| Amount mismatch (real amount exceeds `scope.value`'s bound) | `scopeSatisfied: false` → `valid: false` | -| Nonce reuse | `nonceUnseen: false` → `valid: false`, and — critically — the nonce is **never re-burned** by a failed attempt (§10 step 3 only calls `checkAndRecord` once every other check already passed), so a legitimate request that failed for an unrelated, corrected reason can still succeed on retry with the same artifact, exactly once | -| Revoked artifact | `notRevoked: false` → `valid: false`, checked independently of expiry (a revoked-but-unexpired artifact still fails) | - -No failure mode above produces a fabricated approval, a fabricated Trust Record, or a fabricated Receipt — every failure is an ordinary `PolicyDecision` REJECT flowing through the exact same `ExecutionGate.enforce()` path every other rejection already does, unchanged. - -## 15. Threat Model - -| Threat | Assumption / trust boundary | Resistance | -| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Malicious AI | The AI is assumed hostile by default (this is this project's entire premise) — it holds valid API credentials but no artifact-issuer private key | Cannot create, modify, or extend the scope of a valid artifact (§11); can only relay one it received unmodified | -| Compromised AI | Same trust boundary as malicious AI — compromise does not grant access to a key the AI never held | Identical resistance; the artifact's security does not depend on the AI's integrity at all, only on keeping the issuer's private key outside the AI's reach | -| Prompt injection | Out of scope for this artifact specifically (a cryptographic verification layer, not an input-sanitization layer) — assumed to already be addressed, if at all, upstream of this design, since prompt injection targets the AI's _decision-making_, not the artifact's cryptography | This artifact provides no protection against an AI _choosing_ to submit a request it shouldn't (that remains policy's job); it only prevents the AI from fabricating evidence that such a request was pre-approved when it wasn't | -| Forged signatures | Same as forged authorization (§11) | Requires the issuer's private key, held outside the AI's and Parmana's runtime's reach | -| Replay attacks | Nonce burn-on-first-use (§10 step 3), same mechanism and same trust level as the existing `NonceStore` | Identical residual risk to the existing Execution Authorization nonce model — a persistent (not in-memory) store is required for fleet-wide single-use, exactly as `docs/CLAIMS.md` §3.2 already documents for that artifact; the same caveat applies here and is not a new risk | -| Stolen artifacts | An artifact is bearer evidence, like the Execution Authorization envelope already is — anyone holding a valid, unexpired, unconsumed artifact can present it | Bounded by short TTL (§7.1) and single-use (§10); a stolen-but-unused artifact is a real residual risk, identical in kind to a stolen Execution Authorization, not a new risk this design introduces | -| Race conditions | Nonce check-and-record must be atomic (the existing `NonceStore` contract already requires this); two concurrent requests presenting the _same_ artifact must not both succeed | Directly inherited from the existing, already-proven atomic `checkAndRecord` contract (`packages/storage/src/supabase/SupabaseNonceStore.ts`'s own concurrent-race test, reused verbatim for a new namespace) | -| Duplicate execution | Covered by the existing `businessTransactionId` uniqueness guard (`DuplicateBusinessTransactionError`), entirely independent of this artifact | No change; this artifact does not weaken or duplicate that existing protection | -| Partial execution | Out of scope — this artifact is a pre-execution authorization gate, not an execution-atomicity mechanism; unaffected by and unrelated to partial-execution concerns elsewhere in the runtime | -| Capability substitution | §11 | `capabilityMatches` check | -| Policy substitution | Out of this artifact's scope — already closed structurally by TD-22's `CapabilityPolicyBinder`, unaffected by and independent of this design | - -**Explicit assumptions:** (1) the issuer's private key is held and used by a party genuinely independent of the AI and of Parmana's own runtime — this design cannot verify _who_ holds a key, only that a registered key was used; (2) the trusted-issuer-key registry (§9) is itself correctly and securely provisioned — this design specifies verification, not issuer-key operational custody (§16); (3) the artifact's distribution channel (§12, "Distribution") is not compromised in a way that lets the AI obtain an artifact scoped to a request it should not make — this is an operational, not cryptographic, concern, explicitly out of this design's scope. - -**Residual risks, named rather than implied:** a compromised or colluding _approver_ (the human/system holding the issuer key) can validly authorize anything within their own signing authority — this artifact proves _an_ authorized party approved something, not that the approval itself was wise or that the approver wasn't coerced; that is a business-process control outside this cryptographic design's reach, identical in kind to how a signed Execution Authorization proves Parmana's runtime approved a decision without proving the underlying policy was well-designed. - -## 16. Verification Plan - -Certification tests required before any implementation of this design could be considered complete — specified here so a future implementation phase has a concrete, pre-agreed bar, not specified vaguely and left to interpretation later: - -- **Unit tests:** canonical serialization round-trips for `ApprovalPayload` (mirroring `CapabilityPolicyBinder.test.ts`'s own style); every field of §10's algorithm tested independently (version mismatch, expired, not-yet-valid signature, wrong capability, wrong resourceId, scope violated in each direction of each supported comparator, nonce reuse, revoked); a deterministic-output test asserting identical `(artifact, request, now)` inputs always produce identical `checks`. -- **Integration tests:** a full `POST /execute` HTTP-boundary test (mirroring `hubspot-deal-update.integration.test.ts`'s existing conventions exactly) proving an over-threshold amount change is rejected with no valid artifact, approved with one, and rejected again if the same artifact is replayed on a second request. -- **Architecture tests:** a `tests/architecture/`-style scan (matching this repository's existing `execution-boundary.test.ts` convention) proving the artifact-verification component is reachable from exactly one production integration point and that no alternate path can inject a "verified" fact without going through §10's algorithm. -- **Adversarial tests:** every threat named in §15 reproduced as a live-shaped test, matching this repository's own established convention (e.g. `SignalIntentBinder.test.ts`'s "blocks the exact live exploit" style) — a forged artifact, a modified amount bound, a capability-substituted artifact, a resource-transferred artifact, a replayed artifact, an expired artifact, a revoked artifact — each independently proven rejected. -- **Deterministic replay tests:** confirming the verification algorithm itself is side-effect-free except for the single, explicitly-scoped nonce burn (§10 step 3) — running the same well-formed artifact through verification twice without consumption (e.g. a dry-run/pre-check mode, if one is added) must not itself burn the nonce. -- **Cryptographic verification tests:** signature verification tested against both `ed25519` and `dilithium3` issuer keys (mirroring `dilithium3-cross-instance.test.ts`'s existing coverage of the same distinction for the Execution Authorization), and a cross-algorithm rejection test (an `ed25519`-signed artifact presented with a `dilithium3` key configured for that issuer, and vice versa) confirming `assertKeyType`'s existing fail-closed discipline extends here too. - -**Success criteria, measurable:** 100% of the adversarial cases in §15 have a passing, named regression test; the full existing test suite (968 tests as of this phase, per Phase 2K/2L) continues to pass unmodified, proving no existing behavior regresses; `tsc -b` and the existing architecture-boundary suite remain clean. - -## 17. Open Questions - -Deliberately left unresolved by this design — decisions for the implementation phase, not inferred here: - -1. **Issuer key provisioning and rotation process.** This design specifies _verification_ against a trusted-issuer registry (§9); it does not specify how an approver is onboarded, how their key is generated/stored, or how rotation/offboarding works operationally. -2. **Distribution channel.** How a signed artifact physically reaches the AI/caller (embedded in the Business Transaction request body directly? a separate header? a reference id the caller resolves against a Parmana-hosted store?) is unspecified — a real design decision with API-shape consequences, deferred to implementation. -3. **Whether `constraints` (§7.1) needs concrete fields now or can remain a truly empty, reserved extension point until a second use case (beyond HubSpot amount changes) actually needs it.** -4. **Revocation store design** (`RevokedApprovalStore`, §10/§14) — this document assumes its existence and interface shape (`isRevoked(approvalId): Promise`) but does not specify its storage backend, matching how this design generally specifies interfaces and leaves storage implementation to the future phase. -5. **Whether a single artifact should ever be allowed to cover multiple resourceIds or a range/pattern** — this design deliberately specifies exactly one `resourceId` per artifact (§7.1) as the safer default; whether a legitimate business need (e.g., "pre-approve any deal in this pipeline up to $X") justifies relaxing this is left open, not decided here. - -## 18. Future Work - -- Implement this design in a dedicated, separately-chartered phase, per this document's own instruction not to prototype or implement here. -- Implement the Razorpay Path A recommendation (§5) — a genuinely separate, smaller, non-artifact piece of work; scheduling it alongside or before this artifact's own implementation is a project-planning decision, not an architectural dependency (neither blocks the other). -- Revisit `payments:execute`'s five caller-asserted facts (§6's requirements matrix) once a real vendor-payment connector replaces the test-only `MockConnector` (TD-1) — this design's generality (capability/resourceId/scope, not HubSpot-specific fields) is intended to cover that future case without a second design phase, but this was not separately verified against vendor-payment's actual policy shape and should be re-checked when that connector is built. -- A future documentation-verification phase should address the stale `docs/CLAIMS.md` §3.4 citations Phase 2L flagged (§1, referencing this phase's own predecessor) — unrelated to this artifact, noted here only because it was surfaced in the same document chain. - ---- - -## Final Verification - -| Item | Status | -| ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| Every authorization requirement documented | ✓ — §6, extended from Phase 2L's own trace to include all 6 production capabilities, not only the 2 with confirmed gaps | -| Every caller-controlled authorization identified | ✓ — §6; three total facts across two capabilities plus one not-yet-reachable capability, none newly discovered beyond Phase 2L's own findings | -| Artifact schema completely specified (Path B finding only) | ✓ — §7, every field defined, mandatory/optional stated explicitly | -| Verification algorithm completely specified (Path B finding only) | ✓ — §10, fully deterministic, ordered, no early return between independent checks | -| Failure semantics deterministic | ✓ — §14, every failure named and fails closed | -| Threat model documented | ✓ — §15, assumptions, trust boundaries, and residual risks all stated explicitly, not implied | -| Integration points identified | ✓ — §13, integration points only; no existing component's architecture is redesigned | -| No production source code changed | ✓ — this phase produced only this document | -| No public API changed | ✓ — no route, schema, or field was modified; §17's Open Question #2 (distribution channel) explicitly defers any future API-shape decision | - -Supported by: repository searches, source references, and current-implementation analysis throughout §4, §6, §7–§15, each citing the specific file or prior phase report it draws from. - -## Final Recommendation - -**DESIGN CERTIFIED** — for the HubSpot-class finding (Path B) specifically. - -Repository evidence (§4) demonstrates the Razorpay daily-cumulative-cap finding does **not** require a new authorization artifact — it requires extending existing, already-signed, already-stored infrastructure (`ExecutionTrustRecordRepository`, `RazorpaySignalStateVerifier`) with a new query capability, correctly classified **DESIGN NOT REQUIRED** and left to a separately-recommended implementation phase (§5). Repository evidence equally demonstrates the HubSpot `preAuthorizedForAmountChange` finding **cannot** be closed by extending any existing verification component, because no existing artifact represents an attestation from a party independent of both the AI caller and Parmana's own runtime — this genuinely requires a new primitive (§4, §5). That primitive's design is complete: every mandatory field is specified (§7), canonical serialization and signing are fully reused from existing, proven infrastructure (§8, §9), the verification algorithm is fully specified and deterministic (§10), every named security property is demonstrated (§11), the complete lifecycle is specified (§12), integration points are identified without redesigning any existing component (§13), every failure mode fails closed (§14), the threat model is documented with assumptions and residual risks stated explicitly rather than implied (§15), and a concrete, measurable verification plan is specified for the future implementation phase to be held to (§16). Open questions (§17) are genuine implementation-phase decisions (key provisioning, distribution channel, revocation storage), not gaps in the architectural specification itself. diff --git a/docs/architecture/phase3b-cumulative-authorization.md b/docs/architecture/phase3b-cumulative-authorization.md deleted file mode 100644 index 1d7edb73..00000000 --- a/docs/architecture/phase3b-cumulative-authorization.md +++ /dev/null @@ -1,171 +0,0 @@ -# Phase 3B — Repository-Derived Cumulative Authorization - -Implements Phase 3A's recommendation: replaces the caller-declared `dailyCumulativeAfterThisRefundPaise` signal with a value independently derived from repository state, closing the one remaining Property B gap Phase 3A classified as **DESIGN NOT REQUIRED** (no new Authorization Artifact — existing infrastructure extension only). - -**Fixed against:** commit `4c73df8` (`docs(architecture): design the Authorization Artifact and split TD-23's remaining findings`), the tip of `main`. Working tree was clean before this phase began. - ---- - -## 1. Phase 3A Recommendation - -Read directly from `docs/architecture/phase3a-authorization-artifact-design.md` §5 (not from any summary): - -> **Razorpay daily cumulative cap → PATH A, Existing Infrastructure Extension. DESIGN NOT REQUIRED for this finding.** ... **Recommended, separately-chartered implementation phase:** add one query method to `ExecutionTrustRecordRepository` ... that sums `intent.parameters.amountPaise` across today's approved `razorpay:refund-create` executions ... extend `RazorpaySignalStateVerifier` to add `dailyCumulativeAfterThisRefundPaise` to its own `VERIFIED_SIGNAL_KEYS` ... The TOCTOU/concurrency race Phase 2L flagged ... is a real, separate design question that phase must resolve explicitly (e.g., an atomic increment-and-check against a dedicated ledger row, mirroring the `consumed_nonces`/`razorpay_webhook_events` primary-key-as-atomicity pattern already used elsewhere in this codebase) — not assumed away by a naive sum-then-compare query. - -All three elements of this recommendation — the repository query, the verifier extension, and the explicit atomicity design — are implemented in this phase. - -## 2. Independent Verification - -Confirmed fresh, not assumed: Phase 3A's own report (§4) demonstrates its conclusion with direct evidence (existing `ExecutionTrustRecordRepository`/`RazorpaySignalStateVerifier` extension points; no existing artifact needed). This phase re-traced the same source files independently before writing any code (§3), and did not simply implement Phase 3A's recommendation on faith — the trace surfaced one detail Phase 3A's own design summary had not spelled out (the exact mechanics of _why_ a plain repository query cannot be atomic against the concurrent race, given a real external API call sits between the check and the write — §7), which directly shaped this phase's implementation. - -## 3. Repository Audit - -Traced fresh, from current source: - -- **`Execution`** (`packages/shared/src/domain/execution.ts`): `executionId`, `businessTransactionId`, `decision`, `status` (`PROCESSING`/`COMPLETED`/`FAILED`), `startedAt`, `completedAt`, `evidence?`, `metadata?`. Does **not** directly carry `intent.parameters` — that lives on `ExecutionEvidence`. -- **`ExecutionEvidence`** (`packages/shared/src/domain/execution-evidence.ts`): `action`, `target`, `parameters` (the parameters _actually used_, not merely requested), `success` (boolean), `executedAt`, `attributes?`. This — not `Decision.signals` — is the authoritative record of what actually executed, confirmed by direct type inspection. -- **`ExecutionTrustRecord`** (`packages/shared/src/domain/execution-trust-record.ts`): `executions: readonly Execution[]`, plus `transaction`, `trustRecordHash`, `signature` — the aggregate is itself cryptographically signed; nothing in it is caller-mutable after creation. -- **`ExecutionTrustRecordRepository`** (`packages/shared/src/repositories/execution-trust-record-repository.ts`), before this phase: `create`, `findByTransactionId`, `appendExecution`, `appendOverride`, `appendVerification`, `appendReceipt`, `appendSettlementConfirmation` — **no query or aggregation method existed**, confirmed by reading the full interface. -- **`SupabaseExecutionTrustRecordRepository`**: `executions` is a dedicated Postgres table (`execution_id TEXT PRIMARY KEY, business_transaction_id TEXT NOT NULL, execution_json JSONB NOT NULL, created_at TIMESTAMPTZ NOT NULL`), with an existing index on `created_at` (`supabase/migrations/20260629013035_initial_schema.sql`) — sufficient for an efficient, indexed time-range query directly against the existing table, no new table needed for this specific query. -- **`RazorpaySignalStateVerifier`**: already the established, capability-scoped `SignalStateVerifier` extension point where `paymentStatus`/`paymentCurrency`/`refundableRemainingPaise`/`requestedExceedsRemainder` are independently re-derived from a real Razorpay fetch — confirmed the exact insertion point Phase 3A's recommendation names. -- **Available fields, confirmed sufficient**: `evidence.action` (filter by `"razorpay:refund-create"`), `evidence.success` (filter successes only), `evidence.executedAt` (time-window filter), `evidence.parameters.amountPaise` (the value to sum). All four are already captured, on every execution, by existing, unmodified code (`SdkConnectorExecutor.execute()`'s `buildConnectorEvidence` call). **Sufficient information exists; no additional persistence was required** for the observability/reconciliation query. - -## 4. Audit Completeness Verification - -Every sub-question required by this phase's Task 1.5, verified against current source (not assumed): - -**Every successful refund execution produces exactly one persisted Trust Record.** Confirmed by direct inspection of `packages/api/tests/integration/execution-failure.integration.test.ts` (Phase 2D): _"No Trust Record: `BusinessTrustPipeline.execute()` is only reached after `RuntimePipeline.execute()` returns successfully; the thrown error propagates out of `RuntimeEngine.execute()` before that point, so `Runtime.execute()` never calls `trustRecords.create()`."_ A connector failure produces **zero** persisted artifacts, not a `FAILED`-status one — "successful" and "has a persisted Trust Record" are the same set by construction. - -**Failed executions do not contribute to cumulative totals.** True for two independent reasons, both confirmed: (1) a policy-`REJECTED` decision never reaches execution at all (produces only a `RefusalRecord`, RFC-0021, which this phase's query never reads); (2) a connector-level failure produces no Trust Record whatsoever (above). Both failure classes are structurally absent from the data this phase's query reads — no filtering logic was needed to exclude them beyond the `evidence.success`/`status` checks already present for defense in depth. - -**Duplicate executions cannot produce duplicate cumulative entries.** `BusinessTransactionService.accept()`'s uniqueness guard (`packages/runtime/src/services/business-transaction-service.ts:41`, `DuplicateBusinessTransactionError`) rejects a resubmitted `businessTransactionId` before `RuntimeEngine`, policy evaluation, or execution ever run — confirmed present and unchanged by this phase. - -**Replayed requests cannot inflate cumulative totals.** `POST /replay` (per Phase 2G's own independent finding, unaffected by this phase) performs a signature recheck only — no re-execution, no new Trust Record. A resubmission attempt is caught by the duplicate-transaction guard above. - -**Historical queries cannot silently omit committed executions — with one identified, pre-existing, narrow exception.** A process crash in the exact window between a real, successful Razorpay API call and `trustRecords.create()`/`appendExecution()`'s durable commit would leave a real-world refund invisible to `sumSuccessfulExecutionAmounts` (§6). This is **not introduced or worsened by this phase** — it is a pre-existing, systemic characteristic of the entire "Trust Record as source of truth" architecture, equally true for receipts, verification, and audit today. Critically, **this phase's actual real-time enforcement mechanism does not depend on Trust Record persistence timing at all** — the `RazorpayDailyRefundLedger` reservation (§7) is written _before_ the Razorpay API call is even made, entirely independent of whether the eventual Trust Record commit succeeds. The crash-window gap affects only the secondary, reconciliation-grade `sumSuccessfulExecutionAmounts` query, never the actual cap enforcement. This is stated explicitly, not silently assumed away, per this phase's own instruction — and it is a reason to proceed, not a STOP condition, precisely because the enforcement path sidesteps it. - -**The repository query has the same business scope as the governing policy.** `policies/razorpay-refund/1.0.0/policy.json`'s `reject-exceeds-daily-cumulative-cap` rule has no per-account or per-payment qualifier in its own text, and only one Razorpay credential (`RAZORPAY_KEY_ID`/`SECRET`) is configured per deployment — confirmed by reading `createRazorpayCredentialProvider.ts` fresh. The per-refund cap (500,000 paise) is a separate, already-independently-verified check on `refundableRemainingPaise`; the daily cumulative cap's evident purpose, being an _additional_ check beyond it, is a global daily volume guard. **Scope decision, stated explicitly: global (all `razorpay:refund-create` executions), UTC calendar day, implicitly INR-only** (every counted refund already passed the policy's own `paymentCurrency === "INR"` check). - -No condition above triggered this phase's STOP instruction — the one identified durability gap is pre-existing, narrow, and does not affect the actual enforcement mechanism this phase builds. - -## 5. Historical Query Design - -Added to `ExecutionTrustRecordRepository` (`packages/shared/src/repositories/execution-trust-record-repository.ts`) as an **optional** method — `sumSuccessfulExecutionAmounts?(action, parameterKey, since, until): Promise` — optional specifically so the seven pre-existing inline test-fake implementations of this interface (`packages/runtime/tests/unit/{execution-authorization-wiring,execution-trust-application-replay,refusal-record-fail-open,runtime-builder,runtime,verification-negative,verification-service}.test.ts`) keep compiling and behaving identically without adding it, matching this codebase's established backward-compatibility discipline for interface extension. - -- **`MemoryExecutionTrustRecordRepository`**: iterates every stored record's `executions`, filtering on `status === COMPLETED`, `evidence.success === true`, `evidence.action === action`, and `evidence.executedAt` within `[since, until)`, summing `evidence.parameters[parameterKey]`. -- **`SupabaseExecutionTrustRecordRepository`**: a single SQL query directly against the existing `executions` table's `execution_json` JSONB column (`SUM((execution_json->'evidence'->'parameters'->>$2)::numeric)`), filtered identically. **No new table, no schema change** for this query — it reuses the exact table and index that already existed. - -This method exists as the **reconciliation-grade ground truth** — a read-only, always-correct-relative-to-what-was-persisted view — not as the real-time enforcement mechanism (§7 explains why it cannot be). - -## 6. Signal Verification Changes - -`RazorpaySignalStateVerifier` (`packages/connector-sdk/src/connectors/razorpay/RazorpaySignalStateVerifier.ts`) gained three new optional constructor options (`dailyRefundLedger`, `dailyCumulativeCapPaise`, `refundDayResolver`), matching this codebase's established optional-additive-field convention for backward compatibility. `dailyCumulativeAfterThisRefundPaise` is no longer left in `VERIFIED_SIGNAL_KEYS`'s exclusion list — it is now independently derived via a new private method, `reserveDailyCumulative`, run only when the four pre-existing checks found no violation (mirroring the established "a request already going to be rejected needs no further independent check" discipline). Wired into production unconditionally in `createRazorpaySignalStateVerifier.ts` — the daily cap is a structural invariant for all real API traffic, not an opt-in configuration. - -**A caller-declared value is now compared against the real, ledger-derived one** using the same uniform "any disagreement is a violation" discipline the other four verified facts already use — not only "is it over cap," since a caller who understates the total while it happens to still be within cap is equally caught (§7's implementation detail). - -## 7. Concurrency and Atomicity Verification - -**A race exists, and was identified before implementation, not discovered after.** The daily-cap check runs inside `RazorpaySignalStateVerifier.findViolations()`, before the real Razorpay API call; the real, executed amount is only confirmed _after_ that call succeeds. A genuine Postgres transaction cannot span the external API call in between (holding a database lock across network latency to a third party is a correctness and availability hazard, and does not even fully close the gap — a crash after the external call but before commit is unavoidable regardless). A pure `SELECT SUM(...) FROM executions` read-then-decide check therefore has an unavoidable window: two concurrent requests can both read the same pre-reservation total before either commits, and both independently pass the cap check. - -**This was surfaced explicitly to the user before implementation** (mid-phase), given the schema and money-moving-authorization stakes involved, rather than resolved unilaterally. The chosen design: a small, dedicated atomicity primitive — `razorpay_daily_refund_reservations` (`refund_day DATE PRIMARY KEY, reserved_paise BIGINT NOT NULL DEFAULT 0, updated_at TIMESTAMPTZ NOT NULL`), added via `supabase/migrations/20260805170000_add_razorpay_daily_refund_reservations.sql`. This is **not a transaction ledger** — it stores nothing about individual refunds, only a single running total per UTC day — and is architecturally identical in kind to `consumed_nonces`/`razorpay_webhook_events`, whose entire existence is likewise to make one specific check atomic, not to record history generally. - -**How it closes the race:** `RazorpayDailyRefundLedger.reserve(refundDay, amountPaise)` is a single `INSERT ... ON CONFLICT (refund_day) DO UPDATE SET reserved_paise = reserved_paise + EXCLUDED.reserved_paise RETURNING reserved_paise` statement (`SupabaseRazorpayDailyRefundLedger.ts`). The **write is the check** — there is no separate read-then-decide step. Two concurrent reservations for the same day are serialized by Postgres's own row lock on that day's row; each caller's returned `totalAfterReservation` correctly reflects the other's already-applied contribution the moment it commits. This is the same class of atomicity `consumed_nonces`'s primary key already provides for a uniqueness check, applied here to an additive counter instead. - -**Compensation, not a downstream hook:** if a reservation would push the total over the cap (or if the caller's declared value disagrees with the real one), the reservation is released **immediately, synchronously, within the same verifier call** — before any connector call is ever made — since the outcome ("this request will be rejected") is already known at reservation time. This deliberately avoids needing a callback from a later, separate execution-outcome event, which would have required threading a new hook through `RuntimeEngine` — explicitly forbidden by this phase's Preserve list. - -**Known, accepted, documented residual imprecision:** if a reservation is _kept_ (request approved, cap check passed) but the downstream connector call subsequently fails for an unrelated reason (network error, Razorpay API error), the reservation is **not** retroactively released — no hook exists for this, by design (see above). This means the reservation ledger can, in this one specific failure mode, run slightly _ahead_ of reality — a safe-direction (more conservative, never permissive) discrepancy, correctable by comparing the ledger's total against `sumSuccessfulExecutionAmounts`'s real total (§5) and manually adjusting if an operator chooses to reconcile. Building automatic reconciliation was out of this phase's scope (a new scheduler/background-job component, not an additive extension of what exists). - -**Adversarial tests added, proving the race is actually closed, not merely reasoned about:** - -- `packages/connector-sdk/tests/unit/InMemoryRazorpayDailyRefundLedger.test.ts`: 50 concurrent `reserve()` calls for the same day sum exactly, with each individual call's own returned total forming the unbroken sequence 40,000, 80,000, ..., 2,000,000 — proof no two concurrent calls observed (and used) the same pre-reservation total. -- `packages/api/tests/integration/razorpay-refund.integration.test.ts`, new case: five concurrent `POST /execute` requests of 500,000 paise each (2,500,000 combined, against the 2,000,000 default cap) through the real HTTP route and real production bootstrap chain. Asserts: at least one request is rejected; the sum of refunds that actually landed on the mock Razorpay server never exceeds the cap; the approved-response count and the executed-refund total agree exactly (no silent over- or under-execution). - -## 8. Adversarial Validation - -Beyond the concurrency case (§7), `packages/api/tests/integration/razorpay-refund.integration.test.ts` gained a case proving a caller cannot inflate its own headroom by declaring a false, low `dailyCumulativeAfterThisRefundPaise`: four legitimate refunds bring the real total to 1,920,000; a fifth, dishonest request (declaring `300000` as if it were the day's first refund, when the real post-reservation total would be `2,220,000`) is rejected, and the four legitimate refunds are confirmed unaffected. Combined with Phase 2K/2L's pre-existing coverage (capability substitution, policy substitution, signal/intent mismatch), every adversarial category this phase's Task 3/5 named is now covered: - -| Adversarial attempt | Prevented? | Evidence | -| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | -| Inflate cumulative total (declare a false low value) | **Yes** | New test, §8 | -| Reduce cumulative total artificially | **Yes** (same mechanism — any disagreement is a violation, not only understatement) | `reserveDailyCumulative`'s uniform mismatch check, §6 | -| Split transactions to stay under per-refund cap while exceeding daily cap | **Yes** | New test, §7 (five concurrent requests, each individually valid) | -| Bypass daily limit outright | **Yes** | Same test | -| Fabricate cumulative state | **Yes** | New test, §8 | -| Exploit concurrent authorization races | **Yes** | §7's atomic reservation + concurrency test | -| Exploit check-before-record timing windows | **Yes** | §7 — the write _is_ the check, no separate read-then-record step exists | - -## 9. Regression Testing - -``` -npx tsc -b → clean, 0 errors -npm test -- --maxWorkers=2 → 142 test files passed (+1), 15 skipped (unchanged); - 979 tests passed (+8: 7 new ledger unit tests, +2 net - new integration cases, the remainder architecture- - boundary-suite cases automatically generated for new - bootstrap/connector-sdk files, exactly as happened for - every prior phase's new files), 39 skipped, 0 failed -``` - -Every pre-existing test continues to pass unmodified — confirmed by the identical skip count and by the specific tests already exercising the affected areas (`razorpay-refund.integration.test.ts`'s three pre-existing cases, the TD-22 capability-policy-binding case) all still passing. Replay protection, audit generation, and `CapabilityPolicyBinder` were not touched by this phase (no file under `execution-gateway`/`execution-control`/`envelope-verifier` was modified) and their own tests remain green. - -## 10. Remaining Limitations - -- The crash-window durability gap (§4) remains a pre-existing, narrow, systemic characteristic of the Trust Record architecture, not specific to or worsened by this phase — and does not affect this phase's actual enforcement mechanism, only the secondary reconciliation query. -- Reservations for approved requests whose downstream connector call subsequently fails are not automatically released (§7) — a documented, safe-direction (conservative, never permissive) imprecision, reconcilable manually via `sumSuccessfulExecutionAmounts` but not automated in this phase. -- `RAZORPAY_DEFAULT_DAILY_CUMULATIVE_CAP_PAISE` remains a hardcoded constant, unchanged by this phase, still requiring the policy JSON and the connector-sdk constant to be kept in sync by hand — a pre-existing coupling risk (`RazorpayRefundSignals.ts`'s own comment already names this), not introduced or worsened here. -- The scope decision (global daily total, not per-account) was reasoned from the current single-credential deployment model (§4); if a future deployment configures multiple Razorpay accounts, this scope decision would need explicit revisiting — not something this phase's evidence could resolve in advance of that scenario existing. - -## 11. Evidence Summary - -``` -Repository searches: grep for dailyCumulativeAfterThisRefundPaise/cumulative/refundTotal/ - authorizationAmount across packages/*/src (excluding tests/dist) -- confirms the caller- - controlled signal now appears only in the two files that independently verify it, and no - third, previously-undiscovered instance of this pattern exists anywhere - -Source references: packages/shared/src/domain/{execution,execution-evidence, - execution-trust-record}.ts, packages/shared/src/repositories/ - execution-trust-record-repository.ts, packages/storage/src/{memory,supabase}/ - ExecutionTrustRecordRepository.ts, packages/connector-sdk/src/connectors/razorpay/ - {RazorpaySignalStateVerifier,RazorpayDailyRefundLedger,InMemoryRazorpayDailyRefundLedger}.ts, - packages/storage/src/supabase/SupabaseRazorpayDailyRefundLedger.ts, - supabase/migrations/20260629013035_initial_schema.sql, - supabase/migrations/20260805170000_add_razorpay_daily_refund_reservations.sql, - packages/api/tests/integration/execution-failure.integration.test.ts (Phase 2D), - packages/runtime/src/services/business-transaction-service.ts:41 - -Regression tests: packages/connector-sdk/tests/unit/InMemoryRazorpayDailyRefundLedger.test.ts - (7 new), packages/api/tests/integration/razorpay-refund.integration.test.ts (+2 new cases: - fabricated-value rejection, concurrent-request adversarial test) - -Build output: npx tsc -b → clean, 0 errors -Test output: npm test -- --maxWorkers=2 → 979 passed (+8), 39 skipped, 0 failed -``` - ---- - -## Final Verification - -| Item | Status | -| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Phase 3A independently re-verified | ✓ — read directly, §1, §2; the recommendation's three elements (query, verifier extension, atomicity) all implemented | -| Repository-derived cumulative authorization implemented | ✓ — `RazorpayDailyRefundLedger`, wired into `RazorpaySignalStateVerifier`, §6, §7 | -| Caller-declared cumulative authorization eliminated | ✓ — any disagreement between declared and ledger-derived value is now a violation, §6, static audit §confirms no bypass | -| Execution history independently verified as authoritative | ✓, with one named, pre-existing, narrow exception that does not affect enforcement — §4 | -| Every successful execution contributes exactly once to cumulative authorization | ✓ — the atomic reservation, not the Trust Record history, is the enforcement mechanism, and reserve() is called exactly once per request reaching that check, §7 | -| Cumulative authorization verified atomic against concurrent requests | ✓ — §7, proven by both a unit-level concurrency test and an HTTP-level adversarial integration test | -| Existing Trust Record infrastructure reused | ✓ — `sumSuccessfulExecutionAmounts` reuses the existing `executions` table verbatim, no schema change for it | -| No new Authorization Artifact introduced | ✓ — consistent with Phase 3A's own DESIGN NOT REQUIRED conclusion; the one new table is a dedicated atomicity primitive, not an artifact or a ledger of individual transactions | -| Authorization semantics preserved | ✓ — `PolicyEngine`, `SignalIntentBinder`, `ExecutionGate`, `CapabilityPolicyBinder` all unchanged | -| Replay protection unchanged | ✓ — no file under `envelope-verifier`/`execution-gateway` touched | -| Audit generation unchanged | ✓ — no audit sink file touched | -| Runtime behavior unchanged | ✓ — `RuntimeEngine.ts` itself was not touched by this phase at all (unlike TD-22, which needed a `RuntimeEngine` change; this phase's entire change surface is `RazorpaySignalStateVerifier` and storage/bootstrap) | -| Public API compatibility preserved | ✓ — no route, request/response schema, or field changed | - -Supported by: repository searches, source references, repository traces, regression tests, and build output, all in §11. - -## Final Recommendation - -**PHASE 3B COMPLETE.** - -Repository evidence demonstrates all required conditions: caller-controlled cumulative authorization has been eliminated (§6, static audit confirms no remaining instance); repository-derived cumulative authorization is authoritative, implemented via an atomic reservation ledger rather than a race-prone read-then-decide query (§7); historical execution records were independently verified complete for this purpose, with one pre-existing, narrow, non-enforcement-affecting exception named explicitly rather than hidden (§4); cumulative authorization is proven atomic under concurrent execution by both a unit-level and an HTTP-level adversarial test, not merely argued architecturally (§7, §8); and the daily cumulative limit is proven, by direct test, unable to be exceeded regardless of request timing. No new Authorization Artifact or cryptographic primitive was introduced, consistent with Phase 3A's own architectural conclusion — the one new database table is a small, dedicated atomicity counter, architecturally identical in kind to `consumed_nonces`, not a transaction ledger or a new trust boundary. `RuntimeEngine`, `ExecutionGateway`, the Capability Registry, `CapabilityPolicyBinder`, replay protection, audit generation, and every cryptographic signature path were confirmed untouched. Full regression suite (979 passed, +8 new, 0 failed) confirms no unintended behavior change. diff --git a/docs/architecture/phase3c-approval-artifact.md b/docs/architecture/phase3c-approval-artifact.md deleted file mode 100644 index 7307c9a3..00000000 --- a/docs/architecture/phase3c-approval-artifact.md +++ /dev/null @@ -1,146 +0,0 @@ -# Phase 3C — Approval Artifact Implementation - -Implements Phase 3A's frozen specification (§7–§10): replaces the caller-declared `preAuthorizedForAmountChange` boolean with a cryptographically verifiable, independently-issued Signed Approval Artifact, closing the one remaining Property B gap Phase 3A classified as **PATH B, New Authorization Primitive Required**. Does not revisit Razorpay (Phase 3B's own, separately-chartered, existing-infrastructure closure), redesign authorization, or alter the canonical Capability → Policy binding (TD-22). - -**Fixed against:** commit `32a8d2e` (`fix(razorpay): derive daily cumulative refund cap from repository, close TD-23 (Razorpay half)`), the tip of `main`. Working tree was clean before this phase began. - ---- - -## 1. Precondition Verification - -Read directly from `docs/architecture/phase3a-authorization-artifact-design.md` §5 and §7 (not from any summary): the Decision Gate produced a **split verdict** — Razorpay's daily cumulative cap was **PATH A, DESIGN NOT REQUIRED** (closed separately in Phase 3B); HubSpot's `preAuthorizedForAmountChange` was **PATH B, New Authorization Primitive Required**, with §6 onward being the frozen specification for that primitive. Phase 3B's own report (`phase3b-cumulative-authorization.md`, Final Recommendation) confirms it closed only the Razorpay half and left the HubSpot half explicitly to a later phase. This phase implements exactly and only that remaining half — confirmed as the correct, un-superseded scope before any code was written. - -## 2. Artifact Types (Task 1) - -`ApprovalPayload`/`SignedApproval` (`packages/shared/src/domain/approval-artifact.ts`) implement §7's schema field-for-field: `version` (literal `1`), `approvalId`, `issuer.{approverId,keyId}`, `issuedAt`, `expiresAt`, `capability`, `resourceId`, `scope.{field,comparator,value}`, optional `constraints: Readonly>` (matched to §7.1's exact type, not loosened to `unknown`), `nonce`, and `signature: Signature` (reused verbatim from `packages/shared/src/domain/signature.ts`, unmodified). `ApprovalScopeComparator` is restricted to §7.1's exact six-value vocabulary (`eq`, `lte`, `gte`, `lt`, `gt`, `between`) — deliberately narrower than `PolicyEngine`'s own `PolicyOperator`, since one artifact attests to exactly one bounded fact, never an arbitrary condition tree. Exported additively from `packages/shared/src/domain/index.ts` (`export * from "./approval-artifact.js"`, one new line, no existing export touched). - -A new package, `@parmana/approval`, hosts everything artifact-specific — mirroring this codebase's established one-package-per-trust-boundary-concern convention (`envelope-verifier`, `replay`, `receipt`). It depends on `@parmana/crypto`, `@parmana/envelope-verifier` (for the `NonceStore` interface only), and `@parmana/shared`; nothing depends on `@parmana/policy`, avoiding a cross-package dependency the verifier has no other reason to need. - -## 3. Verification Algorithm (Task 2) - -`ApprovalVerifier.verify()` (`packages/approval/src/ApprovalVerifier.ts`) implements §10's algorithm exactly: the version gate fails closed first (not signature/secret-dependent, so no timing oracle from short-circuiting here); every remaining check — `issuerKnown`, `signatureVerified`, `notExpired`, `notRevoked`, `capabilityMatches`, `resourceMatches`, `scopeSatisfied` — runs unconditionally in a fixed order with no early return, mirroring `AuthorizationVerifier.verify()`'s own no-timing-oracle discipline; nonce consumption (`nonceUnseen`) is attempted **last**, and only when every other check has already independently passed, so a request rejected on any other ground never burns the artifact's single use. `SignatureVerifier` (§9) and `CanonicalSerializer` (§8) are reused verbatim, unmodified — no new cryptographic primitive, no new serialization format. - -`evaluateApprovalScope` (`ApprovalScopeEvaluator.ts`) implements §7.1's six comparators as a small, self-contained function — deliberately not importing `@parmana/policy`'s `OperatorEvaluator`, both to avoid the unneeded cross-package dependency above and because `OperatorEvaluator`'s own `between` uses a 2-element array, incompatible with §7.1's `{min, max}` object shape. Verified by direct source comparison that semantics agree exactly with `OperatorEvaluator`'s corresponding cases wherever the two vocabularies overlap. - -`ApprovalIssuerRegistry`/`StaticApprovalIssuerRegistry` (`ApprovalIssuerRegistry.ts`) resolve `(approverId, keyId) → {publicKey, revoked}` — structurally identical to `createConnectorAuthenticator.ts`'s existing trusted-connector-identity list, per §9's own recommendation. **Resolves §17 Open Question #4** (revocation-store design) by modeling revocation at the issuer-key level (a `revoked: boolean` flag per registry entry) rather than a separate per-`approvalId` revocation table: revoking one compromised or offboarded approver's key instantly invalidates every artifact they ever signed — a coarser but simpler, operationally real guarantee, and the one this phase's own "revoked issuer" framing (Task 2) calls for. Per-artifact revocation remains a possible future refinement (§10, Remaining Limitations), not implemented here. - -`isSignedApprovalShape` (`SignedApprovalGuard.ts`) is the runtime type guard standing between arbitrary caller-supplied JSON (`signals.approvalArtifact`, an untyped `JsonValue` over the wire, never a real `SignedApproval` instance) and `ApprovalVerifier.verify()`. Duck-types every field the verifier actually reads; a value that fails the guard is treated as "no approval artifact presented," never thrown — the same fail-closed discipline a fetch error or missing field already receives elsewhere in this codebase's `SignalStateVerifier` implementations. - -**Unit tests** (`packages/approval/tests/unit/ApprovalVerifier.test.ts`, 15 cases): trusted/valid artifact approves; malformed version fails before signature; unknown issuer; revoked issuer (with an explicit assertion that the signature itself verified genuinely — revocation is an independent check, not a signature failure in disguise); tampered payload; forged signature (signed by a key never registered for the claimed approverId); expired; capability substitution; authorization transfer (wrong `resourceId`); scope escalation (requested value exceeds the approved bound); `between` comparator; same-instance replay; corrected-retry-after-unrelated-rejection (proves a rejection on any other ground never burns the nonce); cross-instance replay against a **shared** nonce store (simulates two independent processes/requests each constructing their own verifier, proving replay protection is a property of the shared store, not of any one verifier instance); determinism (two independent verifiers, same inputs, same fresh nonce stores, identical result). `SignedApprovalGuard.test.ts` (18 cases) covers every malformed-shape rejection path plus a non-throwing hostile-input case. - -## 4. HubSpotSignalStateVerifier Integration (Task 3) - -`HubSpotSignalStateVerifierOptions` gained one new, optional, trailing constructor field — `approvalVerifier?: ApprovalVerifier` — following this codebase's established backward-compatible extension pattern (RFC-0021's `RefusalRecordBuilder`, RFC-0022's `SignalStateVerifier`, TD-22's `CapabilityPolicyBinder`, Phase 3B's own `dailyRefundLedger`): every pre-existing call site that constructs `HubSpotSignalStateVerifier` directly (including every existing test) keeps compiling and behaving identically when the option is omitted. - -`preAuthorizedForAmountChange` is removed from `VERIFIED_SIGNAL_KEYS`'s exclusion comment and instead independently verified by a new private method, `verifyPreAuthorization`, run — mirroring `RazorpaySignalStateVerifier`'s own `reserveDailyCumulative` short-circuit exactly — only when: (a) no other violation already exists (a request already going to be rejected on independently-verified grounds needs no approval check, which would otherwise needlessly burn a single-use artifact's nonce for a request that will never execute); (b) `approvalVerifier` is configured (omitted ⇒ prior behavior, unchanged); and (c) the independently re-derived `amountChangeExceedsThreshold` is actually `true` — below-threshold changes have no bearing on this signal's policy outcome, so no real approval could ever have been required for one, and none should be consumed. - -`requestedValue` passed to `ApprovalVerifier.verify()` is the **independently re-derived** `amountDeltaAbs` (from the real HubSpot deal fetch), not the caller's own declared value — closing the scope-escalation vector where a caller could present a genuine approval for a smaller amount and reuse it to authorize a larger one; `ApprovalVerifier`'s own `scopeSatisfied` check rejects that mismatch. - -**Unit tests** (`packages/connector-hubspot/tests/unit/HubSpotSignalStateVerifier.test.ts`, 10 cases, constructed directly against a hand-built mock `ExecutionSystem` gateway — the same unit-test convention this codebase already uses for `SignalStateVerifier` implementations, since none had a dedicated unit test file before this phase): unaffected when `approvalVerifier` omitted (regression); no false positive (declared `false`, no artifact, threshold exceeded ⇒ no violation); valid artifact approves; no artifact presented ⇒ violation; unknown-issuer artifact ⇒ violation; scope escalation ⇒ violation; authorization transfer (wrong deal) ⇒ violation; replay (second presentation of the same artifact) ⇒ violation; unrelated violation on the same request never consumes the artifact's nonce (corrected retry still succeeds); no verification attempted at all when the amount change doesn't exceed the threshold. - -**Integration test** (`packages/api/tests/integration/hubspot-deal-update.integration.test.ts`, +1 case, `(TD-23)`): through the real, production-wired `POST /execute` bootstrap chain (`createApplication` → `createHubSpotSignalStateVerifier`), a declared `preAuthorizedForAmountChange: true` backed by a well-formed but necessarily untrusted artifact (the production issuer registry is empty by default, §5) is rejected — `403 POLICY_DENIED`, error message names `preAuthorizedForAmountChange`, the deal's amount is unchanged on the mock HubSpot server, and no mutating `PATCH` call is ever made (a read-only deal-fetch `GET` does occur, since the provisional decision was APPROVE before `HubSpotSignalStateVerifier` ran and caught the mismatch — this is the verifier doing its job, not a leak). - -## 5. Replay Protection (Task 4) - -Per §13's explicit instruction, Approval Artifact nonces are tracked in a **new, logically separate** durable store, not `consumed_nonces` (ExecutionGateway's own Authorization-envelope replay protection): an artifact's nonce and a Gateway authorization envelope's nonce are distinct trust domains issued by distinct parties (an external business approver vs. Parmana's own runtime), and sharing one table would let a coincidental collision between the two unrelated namespaces falsely report "already consumed" for one because of the other. - -- **`consumed_approval_nonces`** (`supabase/migrations/20260805180000_add_consumed_approval_nonces.sql`): structurally identical to `consumed_nonces` — `nonce TEXT PRIMARY KEY` is the entire atomicity mechanism, append-only, RLS enabled. -- **`SupabaseApprovalNonceStore`** (`packages/storage/src/supabase/SupabaseApprovalNonceStore.ts`): structurally identical to `SupabaseNonceStore` — a single `INSERT`, a `23505` unique-violation maps to "already consumed" (`false`), every other error rethrown, fail closed. -- **`createApprovalNonceStore.ts`** (`packages/api/src/bootstrap`): `MemoryNonceStore` under `NODE_ENV=test`, `SupabaseApprovalNonceStore` in production, failing closed at startup if `DATABASE_URL` is not configured — mirrors `createNonceStore.ts`'s own production/test split exactly. - -**Trusted issuer provisioning** (`createApprovalIssuerRegistry.ts`): a small, explicit, hardcoded list (`TRUSTED_APPROVAL_ISSUERS`), mirroring `createConnectorAuthenticator.ts`'s own trusted-connector-identity list — provisioning a new approver means adding an entry and deploying, per §9's own "out of scope" note on registry management (§17 Open Question #1). **Empty by default**: no real business-approver key has been provisioned yet, which is the correct fail-closed starting state — every `preAuthorizedForAmountChange` claim is rejected (`issuerKnown` fails for every artifact) until an operator adds a real entry and provisions the matching public-key PEM file under `PARMANA_KEY_DIR/approval-issuers/`, the same file-based key-loading convention `createGatewayKeyPair.ts` already establishes for the Gateway's own keypair. - -`createHubSpotSignalStateVerifier.ts` wires `approvalVerifier` **always**, never omitted in production — mirroring `createRazorpaySignalStateVerifier.ts`'s own comment on `dailyRefundLedger` exactly: independent verification of `preAuthorizedForAmountChange` is a structural invariant for all real API traffic, not opt-in configuration. Omitting it is only ever done by tests that construct `HubSpotSignalStateVerifier` directly. - -## 6. Adversarial Validation - -| Adversarial attempt | Prevented? | Evidence | -| ----------------------------------------------------------------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------- | -| Forge an artifact without the approver's private key | **Yes** | `ApprovalVerifier.test.ts`, forged-signature case (§3) | -| Tamper with a genuine artifact's payload after signing | **Yes** | Same file, tampered-payload case | -| Replay a genuine artifact a second time | **Yes** | Same-instance and cross-instance (shared-store) replay cases | -| Present an artifact from an unregistered/unknown issuer | **Yes** | `ApprovalVerifier.test.ts` and `HubSpotSignalStateVerifier.test.ts`, unknown-issuer cases | -| Present an artifact from a revoked issuer's key | **Yes** | `ApprovalVerifier.test.ts`, revoked-issuer case (with independent proof the signature itself was genuine) | -| Reuse an artifact for a different deal/business object (authorization transfer) | **Yes** | Both test files, authorization-transfer cases | -| Reuse an artifact for a different capability | **Yes** | `ApprovalVerifier.test.ts`, capability-substitution case | -| Reuse a genuine, smaller-amount approval to authorize a larger amount (scope escalation) | **Yes** | Both test files, scope-escalation cases | -| Present an expired artifact | **Yes** | `ApprovalVerifier.test.ts`, expired case | -| Present a malformed/wrong-version artifact | **Yes** | `ApprovalVerifier.test.ts`, malformed-version case; `SignedApprovalGuard.test.ts`, 18 shape-rejection cases | -| Bypass verification entirely via the real, production-wired API with no real issuer provisioned | **Yes** | `hubspot-deal-update.integration.test.ts`, `(TD-23)` case — proves the fail-closed default, end to end | -| False-positive: penalize an honest caller who correctly declares no pre-authorization | **No regression** | `HubSpotSignalStateVerifier.test.ts`, no-false-positive case | - -## 7. Regression Testing - -``` -npx tsc -b → clean, 0 errors (root + all workspace project references, including the new - @parmana/approval package) -npm test → 145 test files passed, 15 skipped; 1025 tests passed, 39 skipped, 0 failed -``` - -This phase authored 44 new test cases directly: 15 in `packages/approval/tests/unit/ApprovalVerifier.test.ts`, 18 in `packages/approval/tests/unit/SignedApprovalGuard.test.ts`, 10 in `packages/connector-hubspot/tests/unit/HubSpotSignalStateVerifier.test.ts` (a file that did not exist before this phase — no prior unit test constructed `HubSpotSignalStateVerifier` directly), and 1 new case (`(TD-23)`) appended to the existing `packages/api/tests/integration/hubspot-deal-update.integration.test.ts`. Any remaining delta beyond these 44, relative to Phase 3B's own final count, is the architecture-boundary suite's `it.each` cases automatically generated for this phase's new source files — the same auto-sweep behavior every prior phase's doc already notes (e.g. Phase 3B §9), not a manually authored addition. - -Every pre-existing test continues to pass unmodified — confirmed by the identical skip count and by the specific tests already exercising the affected area (all 21 pre-existing `connector-hubspot` tests, all 6 pre-existing `hubspot-deal-update.integration.test.ts` cases, the full 142-case architecture-boundary suite) all still passing. `RuntimeEngine`, `ExecutionGateway`, `CapabilityPolicyBinder`, `consumed_nonces`/`SupabaseNonceStore`, audit generation, and every Razorpay file were confirmed untouched (no file under `execution-gateway`/`execution-control`/`envelope-verifier`/`runtime`/`policy` was modified; `git status` shows zero changes to any Razorpay-named file). - -## 8. Remaining Limitations - -- **Issuer provisioning is manual and out of scope**, per §17 Open Question #1 — deployed with zero trusted issuers configured; a real approver's key must be added to `TRUSTED_APPROVAL_ISSUERS` and its PEM file provisioned by an operator before any real pre-authorization can verify. This is a deliberate, fail-closed starting state, not an oversight. -- **Revocation is issuer-key-scoped, not per-artifact** (§3) — resolves §17 Open Question #4 with the simpler of two designs Phase 3A left open; a compromised single artifact cannot be individually revoked without revoking its issuer's entire key, a coarser guarantee accepted here as sufficient for this phase's scope. -- **`constraints` remains a reserved, unused extension point** (§7.1's own "not yet needed" framing) — present in the type, read by nothing. -- **No self-service approver onboarding flow** — provisioning is a code change + deploy, identical in kind to `createConnectorAuthenticator.ts`'s own existing operational model, not a gap introduced by this phase. - -## 9. Evidence Summary - -``` -Repository searches: grep for preAuthorizedForAmountChange across packages/*/src (excluding - dist/) -- confirms the caller-declared field now appears only in - HubSpotDealUpdateSignals.ts (the signal's own definition, unchanged) and the new - independent-verification call sites, no third, previously-undiscovered trust point exists - -Source references: packages/shared/src/domain/{approval-artifact,signature}.ts, - packages/approval/src/{ApprovalVerifier,ApprovalIssuerRegistry,ApprovalScopeEvaluator, - SignedApprovalGuard}.ts, packages/connector-hubspot/src/HubSpotSignalStateVerifier.ts, - packages/storage/src/supabase/SupabaseApprovalNonceStore.ts, - packages/api/src/bootstrap/{createApprovalIssuerRegistry,createApprovalNonceStore, - createHubSpotSignalStateVerifier}.ts, - supabase/migrations/20260805180000_add_consumed_approval_nonces.sql - -Regression tests: packages/approval/tests/unit/{ApprovalVerifier,SignedApprovalGuard}.test.ts - (33 new), packages/connector-hubspot/tests/unit/HubSpotSignalStateVerifier.test.ts - (10 new), packages/api/tests/integration/hubspot-deal-update.integration.test.ts - (+1 new case) - -Build output: npx tsc -b → clean, 0 errors -Test output: npm test → 1025 passed (+46), 39 skipped, 0 failed -``` - ---- - -## Final Verification - -| Item | Status | -| ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | -| Phase 3A's frozen specification independently re-verified before implementation | ✓ — §1, read directly from source, not summary | -| Approval Artifact implemented exactly per §7's schema | ✓ — §2, field-for-field, including `constraints`'s exact `JsonValue` type | -| Verification algorithm implemented exactly per §10 | ✓ — §3, fixed order, no early return, nonce burned last | -| Canonical serialization and signature model reused verbatim | ✓ — §3, `CanonicalSerializer`/`SignatureVerifier` unmodified | -| Issuer trust model implemented, never trusts caller identity | ✓ — §3, `StaticApprovalIssuerRegistry`, resolves §17 Q4 | -| `preAuthorizedForAmountChange` independently verified, caller-declared value no longer trusted verbatim | ✓ — §4, `verifyPreAuthorization`, any disagreement is a violation | -| Scope escalation (reuse smaller-amount approval for a larger amount) prevented | ✓ — §4, §6, requestedValue is the independently re-derived amount | -| Replay protection implemented, in a dedicated, separate nonce store | ✓ — §5, `consumed_approval_nonces` distinct from `consumed_nonces` | -| Production wiring fails closed with no issuer provisioned | ✓ — §5, §6, proven by the live-API `(TD-23)` integration test | -| No false positives for honest callers | ✓ — §6, dedicated no-false-positive test | -| Adversarial categories covered | ✓ — §6, 12/12 rows | -| Razorpay (Phase 3B) untouched | ✓ — §7, zero changes to any Razorpay file | -| Canonical Capability → Policy binding (TD-22) untouched | ✓ — §7, `CapabilityPolicyBinder`/`CapabilityPolicyBinding.ts` not modified | -| `RuntimeEngine`/`ExecutionGateway` untouched | ✓ — §7, zero changes to either file; integration achieved entirely via the pre-existing `SignalStateVerifier` extension point | -| Replay protection (Gateway/Authorization side) and audit generation unchanged | ✓ — §7, no file under `envelope-verifier`/`execution-control`/audit sinks modified | -| Public API compatibility preserved | ✓ — §4, no route, request/response schema, or field changed; `signals.approvalArtifact` is accepted via the pre-existing generic `PolicySignals` bag | -| Full regression suite green | ✓ — §7, 1025 passed (+46), 39 skipped, 0 failed | - -Supported by: repository searches, source references, regression tests, and build output, all in §9. - -## Final Recommendation - -**PHASE 3C COMPLETE.** - -Repository evidence demonstrates all required conditions: the Approval Artifact is implemented exactly per Phase 3A's frozen §7–§10 specification, reusing `CanonicalSerializer`/`SignatureVerifier`/the `NonceStore` interface verbatim with no new cryptographic primitive; `preAuthorizedForAmountChange` is no longer trusted verbatim from the caller, but independently verified against a real, externally-issued, single-use, scope-bound artifact via the pre-existing `HubSpotSignalStateVerifier` extension point; every named adversarial category (forgery, tampering, replay — both same-instance and cross-instance against a shared store — unknown/revoked issuer, capability substitution, authorization transfer, scope escalation, expiry, malformed shape) is proven prevented by direct test, not merely argued architecturally, and a live-API integration test proves the production default is genuinely fail-closed with zero issuers provisioned; replay protection for artifact nonces is durable and deliberately isolated from the Gateway's own authorization-envelope nonce store. `RuntimeEngine`, `ExecutionGateway`, the canonical Capability → Policy binding, Razorpay's Phase 3B cumulative-authorization closure, existing replay protection semantics, and audit generation were all confirmed untouched — this phase's entire change surface is a new `@parmana/approval` package, an additive domain-type export, one additive `HubSpotSignalStateVerifierOptions` field plus its private verification method, one new Supabase table/store pair, and bootstrap wiring. Full regression suite (1025 passed, +46 new, 0 failed) confirms no unintended behavior change. diff --git a/docs/architecture/phase3d-independent-authorization-certification.md b/docs/architecture/phase3d-independent-authorization-certification.md deleted file mode 100644 index e2eab17c..00000000 --- a/docs/architecture/phase3d-independent-authorization-certification.md +++ /dev/null @@ -1,840 +0,0 @@ -# Phase 3D — Independent Authorization Certification - -Certifies, from current repository state only, whether the public claim _"Even if AI -has valid credentials, it still cannot execute anything your business hasn't -authorized. No exceptions"_ is fully supported by the implemented system. This is a -certification pass, not an implementation phase: no production source code and no -documentation other than this new file were changed. - -**Certified against:** commit `cb467fc` ("complete hybrid signature migration"), the tip -of `main`. Working tree was clean before this phase began (`git status`, `git diff ---stat`, `git log --oneline -10` all confirmed at the start of this session). - -**Method:** treated the repository as never audited before. Prior phase documents -(2K, 2L, 3A, 3B, 3C) and `docs/VERIFICATION-GAPS.md`/`docs/CLAIMS.md` were read only as -claims to independently re-verify, never as conclusions to inherit. Every finding below -traces to a file:line read directly in this session, either by the certifying process -itself or by one of four parallel evidence-gathering passes it commissioned and then -independently cross-checked (§1). Where a sub-agent's finding could not be independently -confirmed, or where its own report flagged an open question, that is stated explicitly -rather than smoothed over. - ---- - -## 1. Certification Methodology - -Two verification tracks ran in parallel and were then reconciled against each other: - -1. **Direct source tracing** (this process): read `packages/api/src/bootstrap/*`, - `packages/api/src/routes/execute.ts`, `packages/runtime/src/RuntimeEngine.ts`, - `packages/runtime/src/RuntimeBuilder.ts`, `packages/runtime/src/ExecutionGate.ts`, - `packages/policy/src/CapabilityPolicyBinding.ts`, both `SignalStateVerifier` - implementations, `packages/approval/src/ApprovalVerifier.ts`, - `packages/execution-gateway/src/connector-execution/GatewayRazorpayAdapter.ts`, - `packages/storage/src/supabase/{SupabaseNonceStore,SupabaseRazorpayDailyRefundLedger}.ts`, - and the governing policy JSON files, in full, before commissioning any sub-agent work. -2. **Four independently-scoped evidence passes**, each given the specific files and - questions to trace and instructed to cite file:line for every claim and to mark - anything it could not confirm as `NOT VERIFIED`/`UNABLE TO CERTIFY` rather than assume - a pass: - - Property A (capability inventory, credential isolation) - - Property B + C (independent authorization, structural enforcement) - - Property D + E (replay resistance, concurrency safety) - - Property F + repository-wide search + adversarial review - -Every open question a pass raised was independently re-investigated by this process -before being accepted into this document (see §3–§7, "independently re-verified" notes). -Two flags raised by the Property B/C pass (exact Razorpay refund-create dispatch site; -`SdkConnectorExecutor` internals) were closed by direct re-reading in this session, not -taken on faith — see §5.2. - -Regression suite: `npx tsc -b` (clean, 0 errors) and -`npm test -- --maxWorkers=2` (148 test files passed, 15 skipped; 1039 tests passed, 39 -skipped, 0 failed) were both run fresh in this session (§Regression Verification below), -not assumed from a prior phase's report. - ---- - -## 2. Production Capability Inventory - -Derived strictly from the production bootstrap composition root, not documentation. - -**Entrypoint trace:** `packages/api/src/server.ts` (Node's real entrypoint, top-level -statements run on import — no test/mock branch wraps it) → -`createExecutionSystem()` (`packages/api/src/bootstrap/createExecutionSystem.ts:11-13`, -unconditionally `createExecutionGateway()`) → `createExecutionControl()` -(`createExecutionGateway.ts:33`) → `createConnectorRegistry()` -(`createExecutionControl.ts:58`). - -`createConnectorRegistry.ts:43-168` registers a connector only when its credential -provider factory returns non-`undefined`: - -| Connector | Registration gate | Production capability? | -| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | -| `vendor-payment` | `createVendorPaymentConnector()` returns a real connector only when `process.env.NODE_ENV === "test"` (`createVendorPaymentConnector.ts:30-32`); otherwise `undefined`, logged and skipped (`createConnectorRegistry.ts:55-64`) | **No.** Confirmed by reading the gating condition directly, not inferred from the comment alone. | -| `razorpay` | `createRazorpayCredentialProvider()` returns `undefined` unless both `RAZORPAY_KEY_ID` and `RAZORPAY_KEY_SECRET` are set (`createRazorpayCredentialProvider.ts:85-89`) | **Yes**, conditional on operator-supplied secrets. | -| `hubspot` | `createHubSpotCredentialProvider()` returns `undefined` unless `HUBSPOT_PRIVATE_APP_TOKEN` is set | **Yes**, conditional on operator-supplied secrets. | - -**Capability action strings exposed** (confirmed against `GatewayRazorpayAdapter.execute()`'s -switch, `GatewayRazorpayAdapter.ts:98-109`, and `GatewayHubSpotAdapter.execute()`'s -switch, `GatewayHubSpotAdapter.ts:109-118`): - -| Capability action | Kind | Connector method | -| ------------------------ | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `razorpay:payment-fetch` | read | `fetchPayment` | -| `razorpay:refund-create` | **money-moving write** | `createRefund` | -| `razorpay:refund-fetch` | read | `fetchRefund` | -| `hubspot:deal-fetch` | read | `fetchDeal` | -| `hubspot:deal-update` | **record-mutating write** | `updateDeal` (deny-by-default property allowlist — `HUBSPOT_ALLOWED_DEAL_UPDATE_PROPERTIES`, `HubSpotTypes.ts:36`; any other property name throws before any network call, `GatewayHubSpotAdapter.ts:162-171`) | - -**This certification's scope is therefore the two write capabilities, -`razorpay:refund-create` and `hubspot:deal-update`**, since those are the only production -actions capable of executing something a business would need to have authorized. `payments:execute` -(vendor-payment) is a capability defined in code and policy (`policies/vendor-payment/2.0.0`) -but is **not reachable in production under any current environment configuration** — it -has no gate that would ever register it outside `NODE_ENV=test`. It is addressed -separately in §12 because, if it were ever wired into production as currently written, it -would **not** satisfy this claim (see §12.1). - ---- - -## 3. Property A Verification — Credential Isolation - -### 3.1 Origin, storage, read, use - -| Capability | Credential origin (env var) | Read site | Storage at rest | -| ---------- | ----------------------------------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| razorpay | `RAZORPAY_KEY_ID` / `RAZORPAY_KEY_SECRET` | `createRazorpayCredentialProvider.ts:34-35`, inside `resolve()`, lazily, per call | None. `CredentialHandle` is constructed fresh per resolution (`brandCredentialHandle`), wrapped in a 30-second single-use session credential (`InMemorySessionCredentialVault`, default TTL `GatewayConnectorRegistry.ts:24,124`), consumed exactly once and revoked in a `finally` block on every exit path (`SessionCredentialSecureConnector.ts:77-89`). No disk/DB persistence anywhere in this chain. | -| hubspot | `HUBSPOT_PRIVATE_APP_TOKEN` | `createHubSpotCredentialProvider.ts:35`, inside `resolve()` | Same mechanism as above. | - -Both providers read `process.env` only inside `resolve()`, never at module scope, and -name only the variable in thrown errors, never the value (`createRazorpayCredentialProvider.ts:38-41`). - -### 3.2 Full call-graph trace: does the AI-facing path ever hold the raw credential? - -Traced end-to-end, `POST /execute` → resolution: - -``` -routes/execute.ts:108-111 application.execute(transaction) - ExecutionTrustApplication.execute() - RuntimeEngine.execute() -- policy load, SignalIntentBinder, - CapabilityPolicyBinder, PolicyEngine, - SignalStateVerifier: NO credential contact - ExecutionGate.enforce(decision) -- fail-closed gate, RuntimeEngine.ts:375 - authorizationSigner.sign(...) -- signs an envelope, not a credential - pipeline.execute(context) - ExecutionComponent.execute() - ExecutionRequestBuilder.build() -- object has NO credential field - executionSystem.execute(request) - ExecutionGateway.execute() -- signature/expiry/hash/nonce verify only - SessionCredentialExecutionControl.execute() - ExecutionControlService.execute() - registry.resolveCapability(action) - connector.execute(request) <-- FIRST point CredentialProvider.resolve() runs - = CredentialVaultAdapter.getCredential (only call site of .resolve() in the repo) -``` - -`ExecutionRequestBuilder.build()` (`packages/runtime/src/ExecutionRequestBuilder.ts:29-50`) -constructs its return value purely from `toExecutableContent(...)` and the signed -`authorization` envelope — verified directly, no credential field exists on the type or -in the object literal. The credential is resolved for the first time inside -`ExecutionControlService.execute()`, strictly downstream of policy evaluation -(`RuntimeEngine.ts:269`), the fail-closed gate (`RuntimeEngine.ts:375`), and authorization -signing (`RuntimeEngine.ts:397`). Confirmed further: `GatewayRazorpayAdapter.execute()` -(read directly, `GatewayRazorpayAdapter.ts:58-122`) is the only class that ever reads -`context.credential.value.{keyId,keySecret}` and builds the `Basic` auth header -(`GatewayHubSpotAdapter.ts` equivalent: `Bearer ${privateAppToken}`); both are -`Object.freeze`d, package-private construction, reachable only via -`createGatewayRazorpayConnector.ts`/`createGatewayHubSpotConnector.ts`, which is exactly -what `createRazorpayConnector.ts`/`createHubSpotConnector.ts` (the production bootstrap -call sites) instantiate. - -**Classification: VERIFIED.** No AI-facing object — the HTTP request, the transaction, -the policy decision, the signed authorization, the execution request — ever carries or -passes through a component holding the raw connector credential. Authorization is fully -decided (policy evaluated, gate enforced, envelope signed) before the credential provider -is touched for the first time. - -### 3.3 Disclosed exception: truncated credential fragments in the response body - -Both adapters return a **redaction-intended prefix** of the credential in -`ConnectorResponse.metadata`: - -- `GatewayRazorpayAdapter.ts:132,151,209`: `keyIdRedacted: redactRazorpayKeyId(keyId)` → - `RazorpayTypes.ts:74-76`: first 8 characters of `key_id` (the less-sensitive half of the - Razorpay credential pair; `key_secret` is never touched by this function). -- `GatewayHubSpotAdapter.ts:145,194`: `bearerRedacted: redactHubSpotToken(token)` → - `HubSpotTypes.ts:73-75`: first 12 characters of the **entire** HubSpot Private App - token — for HubSpot the bearer token _is_ the whole credential, so this is a literal - fragment of the actual secret, not a separate, less-sensitive identifier. - -`ConnectorEvidence.ts:31`'s generic redaction filter (`SENSITIVE_KEY_PATTERN = -/credential|secret|token|apikey|api_key|password|authorization/i`, applied to metadata -**key names**, `redactSensitiveKeys`, lines 39-48) does **not** match the key names -`keyIdRedacted`/`bearerRedacted` — confirmed by direct regex inspection — so this -truncated fragment passes unfiltered through `buildConnectorEvidence()` → -`ExecutionEvidence.attributes` → the `ExecutionTrustRecord` returned in the `POST -/execute` JSON response body (`routes/execute.ts:113`). This is intentional, tested -behavior (`razorpay-connector.test.ts:231`, `hubspot-connector.test.ts:268` assert the -exact truncated values), not an oversight discovered by accident, but it is a genuine -exception to a "zero credential bytes ever reach the caller" reading of Property A. - -**Materiality assessment:** an 8-character `key_id` prefix or 12-character bearer-token -prefix does not, by itself, grant any additional execution capability — the calling AI -still cannot construct a valid, authorized request without passing the exact same -independent-authorization gauntlet (§4) for every subsequent call; the fragment cannot be -used to bypass `RuntimeEngine`/policy evaluation, forge a signature, or reconstruct the -full secret. This does **not** contradict the claim under review (which concerns -execution authorization, not credential confidentiality), but it is disclosed here as a -genuine, verified exception to a maximally strict reading of "credential isolation," not -folded silently into a blanket VERIFIED. See §12.2. - -**Test-coverage caveat, independently confirmed:** `credential-isolation.integration.test.ts` -was read in full. It exercises the _generic_ session-credential single-issue/single-use/ -always-revoked mechanism (shared by every connector via `SessionCredentialSecureConnector`) -through a real HTTP `POST /execute` call, but does so against a synthetic, test-only -`vendor-payment`-shaped double (`createInspectableExecutionSystem.ts`), not the real -Razorpay/HubSpot adapters or credential providers, and asserts only audit-sink content and -HTTP status — never response-body content. **It would not catch a regression that leaked -a connector's full secret, or a larger fragment than today's 8/12 characters, into -`ConnectorResponse.metadata`.** This is a real, disclosed gap in regression coverage for -the specific property "no credential-derived bytes reach the caller," independently -confirmed by reading the test file in full. - ---- - -## 4. Property B Verification — Independent Authorization - -Traced for both write capabilities: `Request → Capability → Policy → Authorization → -Execution`. - -### 4.1 Razorpay `razorpay:refund-create` - -| Authorization input | Classification | Mechanism | -| ------------------------------------------------------------------------------------------- | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `paymentStatus`, `paymentCurrency`, `refundableRemainingPaise`, `requestedExceedsRemainder` | Independently verified | `RazorpaySignalStateVerifier.findViolations()` re-fetches the real payment from Razorpay (`executeRazorpayCapability` against `razorpay:payment-fetch`) and rejects on any mismatch against the caller's declared signal (`RazorpaySignalStateVerifier.ts:74-79,171-178`). Fails closed on fetch error. | -| `requestedRefundAmountPaise` | Caller-declared, structurally bound to Intent | `policies/razorpay-refund/1.0.0/policy.json:16-18` (`boundSignals`) binds it to `parameters.amountPaise`; `SignalIntentBinder.findViolations()` (`RuntimeEngine.ts:216-226`) rejects any divergence _before_ `PolicyEngine.evaluate` runs — cannot describe a different real-world action than the one that will execute. | -| `dailyCumulativeAfterThisRefundPaise` (TD-23) | Repository-derived, atomic | `RazorpaySignalStateVerifier.reserveDailyCumulative()` atomically reserves the amount against `RazorpayDailyRefundLedger` (real DB counter) and rejects on any disagreement between the caller's declared value and the real, just-reserved total — not merely on exceeding the cap (`RazorpaySignalStateVerifier.ts:220-264`, violation logic line 245). | - -Production wiring, confirmed directly: `createRazorpaySignalStateVerifier.ts` always -supplies `dailyRefundLedger`; `application.ts:48-52` unconditionally composes this -verifier into `CompositeSignalStateVerifier`, passed to `RuntimeFactory.create` on every -boot. There is no environment branch that omits it in production. - -### 4.2 HubSpot `hubspot:deal-update` - -| Authorization input | Classification | Mechanism | -| ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `currentDealStage`, `dealStageChangeRequested`, `dealStageTransitionAllowed`, `amountChangeRequested`, `amountDeltaAbs`, `amountChangeExceedsThreshold` | Independently verified | `HubSpotSignalStateVerifier` re-fetches the real deal and rejects on mismatch (`HubSpotSignalStateVerifier.ts:61-168`). | -| `proposedDealStage`, `proposedAmount` | Caller-declared, structurally bound to Intent | `boundSignals` in `policies/hubspot-deal-update/1.0.0/policy.json:18-21`, enforced by `SignalIntentBinder` the same way as §4.1. | -| `preAuthorizedForAmountChange` (TD-23) | Cryptographically verified | `HubSpotSignalStateVerifier.verifyPreAuthorization()` (lines 186-241) checks `signals.approvalArtifact` via `ApprovalVerifier.verify()` — see §4.3. Only invoked when the amount change actually exceeds threshold, so requests with no bearing on this signal never needlessly consume a single-use artifact. | - -Directly read `packages/approval/src/ApprovalVerifier.ts:90-176` in full. It runs, in -fixed order with no early return between independent checks (mirroring -`AuthorizationVerifier`'s own no-timing-oracle discipline): payload version → issuer -resolution against a registry independent of Parmana's own runtime key → **Ed25519 -signature verification against the resolved issuer's public key** → not-expired → -not-revoked → capability match → resource match → scope satisfied. Critically, -`requestedValue` passed into the scope check is the **independently re-derived** -`amountDeltaAbs` from the real HubSpot fetch (`HubSpotSignalStateVerifier.ts:190`), not -the caller's own declared amount — closing the scope-escalation path where a genuine -smaller-amount approval could be replayed for a larger change. Single-use nonce -consumption is attempted **last**, only once every other check independently passed, so a -request rejected on any other ground never burns the artifact. - -Directly confirmed policy enforcement: `policies/hubspot-deal-update/1.0.0/policy.json`'s -`reject-amount-exceeds-threshold-without-preauth` rule fires whenever -`amountChangeExceedsThreshold` is true and `preAuthorizedForAmountChange` is false — and -`HubSpotSignalStateVerifier` forces `preAuthorizedForAmountChange`'s value to equal the -independently-verified outcome (`declaredValue !== actualValue` is a violation, line -236-238), so a caller declaring `true` with no valid backing artifact is rejected before -the (looser) raw policy rule could ever approve it. - -Production wiring, confirmed directly: `createHubSpotSignalStateVerifier.ts` always -supplies `approvalVerifier`; composed the same unconditional way as §4.1. - -**Operational caveat, independently confirmed:** `createApprovalIssuerRegistry.ts:37` — -`TRUSTED_APPROVAL_ISSUERS` is an **empty array** in the current production configuration. -This means every `preAuthorizedForAmountChange` claim is rejected today regardless of the -artifact's genuineness (`issuerKnown` fails for everyone), confirmed by the integration -test `hubspot-deal-update.integration.test.ts`'s `(TD-23)` case. **This is fail-closed, -not fail-open**: it makes the system strictly _more_ restrictive than the claim requires -(no over-threshold change can currently execute at all, authorized or not), so it cannot -itself produce an unauthorized execution. It does mean the "independent business -approval" pathway has been verified structurally and in test, but has never been -exercised against a live, operator-provisioned issuer key in production — an operational -readiness note, addressed in §12.3, not a certification defect. - -### 4.3 Search for caller-declared "trust me" authorization flags - -Searched `packages/*/src` (production code) for `preAuthorized`, `callerAsserted`, -`selfAsserted`, `trustCaller`, `skipVerification`, `bypassPolicy`, `skipPolicy`, -`bypassAuthorization`, `isTrusted`, `trustedCaller`, `callerDeclaredAuthorized`, -`assumeAuthorized`. Only `preAuthorized`/`preauthorized` produced hits, and every hit is -one of: the signal's type declaration, the live verification code that checks it against -a signed artifact (§4.2), the policy schema naming the fact, a design-rationale comment, -or test fixtures exercising both the "no verifier configured" (legacy/unit) and "verifier -configured" (production-shaped) paths. **No live, production-reachable code path reads a -caller-supplied "this is authorized" boolean and treats it as sufficient on its own.** - -**Classification: VERIFIED** for both capabilities, with the operational caveat in §4.2 -disclosed explicitly rather than omitted. - ---- - -## 5. Property C Verification — Structural Enforcement - -### 5.1 Canonical capability→policy binding - -`CANONICAL_CAPABILITY_POLICY_BINDINGS` (`packages/policy/src/CapabilityPolicyBinding.ts:28-53`) -is a single hardcoded `ReadonlyMap` keyed by `Intent.action`, covering exactly the -production-registered capabilities. `CapabilityPolicyBinder.findViolation()` rejects any -request whose declared `transaction.policy` doesn't exactly match the canonical entry. - -Read directly, `packages/runtime/src/RuntimeBuilder.ts:173-174,219`: `capabilityPolicyBinder` -(and `signalIntentBinder`) are **unconditionally instantiated** inside `RuntimeBuilder.build()` -— not configuration, not optional, not passed in from `application.ts` at all. There is no -code path that constructs a production `RuntimeEngine` without them. `RuntimeEngine.execute()` -(read directly, `RuntimeEngine.ts:210-268`) runs this check _before_ `SignalIntentBinder` -and _before_ `PolicyEngine.evaluate`, with a violation short-circuiting straight to -`PolicyOutcome.REJECT` with zero rules evaluated. - -`CapabilityPolicyBinder.test.ts` proves the exact live-shaped exploit -(`razorpay:refund-create` paired with the unrelated, unprotected `customer-refund/1.0.0` -policy; same for HubSpot vs. `vendor-payment`) is rejected. - -**Classification: VERIFIED.** - -### 5.2 Alternate execution paths / bypass search - -Traced every mounted route (`packages/api/src/app.ts:70-249`): only `POST /execute` -reaches `application.execute()`/`RuntimeEngine`. Every other route is read-only, -verification-only, or (the Razorpay webhook route) a passive signature-verified audit/ -dedupe sink that never calls `executionSystem.execute()` or any connector — confirmed by -reading `routes/webhooks-razorpay.ts` in full. - -Closed the two open questions the Property B/C evidence pass flagged, by direct reading -in this session: - -- **Exact `razorpay:refund-create` dispatch site.** `GatewayRazorpayAdapter.execute()` - (read in full, `GatewayRazorpayAdapter.ts:58-122`) handles all three Razorpay - capabilities — including `createRefund` (lines 163-210), the actual write — in one - class. `createGatewayRazorpayConnector.ts:12-14` instantiates exactly this class, and - `createRazorpayConnector.ts:20-33` (the real production bootstrap call site) calls - `createGatewayRazorpayConnector`. There is no separate `RazorpayRefundService` file in - the current tree (confirmed by search — it was deleted in an earlier execution-ownership - refactor per `docs/VERIFICATION-GAPS.md`'s own note); the dispatch path is exactly the - adapter already traced for credential handling in §3.2. **No bypass.** -- **`SdkConnectorExecutor` internals.** Read in full - (`packages/execution-gateway/src/connector-execution/SdkConnectorExecutor.ts`). It is a - pure adapter implementing `execution-control`'s pre-existing `ConnectorExecutor` - interface, called only from inside `ExecutionControlService.execute()` — itself only - reachable after `SessionCredentialExecutionControl`'s request-bound attestation check — - which is itself only reachable after `ExecutionGateway.execute()`'s signature/expiry/ - hash/nonce verification. It is a leaf in the call graph, not an alternate entry point. - **No bypass.** - -`ExecutionGate.enforce()` (read directly, `ExecutionGate.ts:14-46`) throws on any -non-`APPROVED` decision; `ExecutionComponent.execute()` throws if `context.decision`/ -`context.authorization` is missing. There is no path from `RuntimeEngine.execute()` to -connector dispatch that does not pass through this gate. - -**Classification: VERIFIED. No bypass found**, with both flags the sub-agent pass raised -independently closed by direct source reading rather than left open. - -### 5.3 Policy substitution - -`FilePolicyRepository.load(name, version)` does load whatever policy the caller names -(validated only against a path-traversal allowlist, not against capability) — confirmed -directly. This is closed **structurally by ordering, not by the loader itself**: -`CapabilityPolicyBinder` runs immediately after `policyRouter.load()` and before -`PolicyEngine.evaluate()` (`RuntimeEngine.ts:165-226`), so a wrongly-paired policy is -loaded but never evaluated. This is a two-layer control (load-then-gate), not an -intrinsically capability-safe lookup — if `CapabilityPolicyBinder` were ever omitted from -`RuntimeBuilder`, this protection would silently disappear. It is not currently omittable -(§5.1), so this is disclosed as an architectural dependency to watch on future changes, -not a present gap. - -### 5.4 Test vs. production wiring - -`packages/api/tests/test-app.ts` uses the identical production factory functions -(`createApplication`, `createApp`), differing only in caller-auth/webhook mounting -options and in-memory-vs-Supabase storage backends for otherwise identically-constructed -authorization logic (`CapabilityPolicyBinder`, `SignalIntentBinder`, both -`SignalStateVerifier`s, `ExecutionGate` are built the same way regardless of `NODE_ENV`). -**No test-only wiring removes an authorization control.** - -### 5.5 `OverrideService`/`OverrideVerifier` — independently re-confirmed unreachable - -Directly re-verified in this session (not inherited from `02-REMAINING.md`'s claim): -zero matches for `OverrideService`/`OverrideVerifier` anywhere under `packages/api/src`; -zero call sites in `RuntimeFactory.ts`/`RuntimeEngine.ts`/`RuntimeBuilder.ts`. This code -exists (`packages/runtime/src/services/override-service.ts`, -`packages/runtime/src/policy/OverrideVerifier.ts`) but is not instantiated, not wired to -any route, and not reachable from any HTTP path. **Confirmed dead code, not a live -bypass.** - -**Classification: VERIFIED.** - ---- - -## 6. Property D Verification — Replay Resistance - -### 6.1 Execution-authorization nonce (`consumed_nonces`) - -`SupabaseNonceStore.checkAndRecord()` (read directly) is a single `INSERT INTO -consumed_nonces (nonce, expires_at) VALUES ($1, $2)`, atomicity provided entirely by the -`nonce TEXT PRIMARY KEY` constraint — not a check-then-set. A duplicate insert throws -Postgres `23505`, mapped to "already consumed." `ExecutionGateway.ts` runs signature, -expiry, TTL-policy, and `businessTransactionHash` checks first; nonce consumption is the -last, side-effecting step (`isSoleFailureNonceReplay` distinguishes a pure-replay -rejection as `409 NONCE_ALREADY_CONSUMED`). Production wiring fails closed on missing -`DATABASE_URL`, never silently falls back to the in-memory test store. -`CLAIMS.md` 2.10/3.2's cited evidence (`supabase-nonce-store.integration.test.ts`: "two -simultaneous `checkAndRecord` calls for the same nonce: exactly one succeeds, a real -concurrent-INSERT race against Postgres") is exactly the same atomicity idiom independently -confirmed by direct code reading in this session. - -**Classification: VERIFIED.** - -### 6.2 Approval Artifact nonce (`consumed_approval_nonces`, TD-23) - -Structurally identical mechanism, deliberately a **separate table and separate trust -domain** from §6.1 (an artifact's nonce is issued by an external business approver, not -Parmana's own runtime — sharing one table would let an accidental cross-domain collision -misreport "already consumed"). `ApprovalVerifier.verify()` attempts nonce consumption -last, only once every other check (version, issuer, signature, expiry, revocation, -capability, resource, scope) has independently passed. `ApprovalVerifier.test.ts` proves -same-instance replay rejected, cross-instance replay against a shared durable store -rejected, and that a rejection on an unrelated ground never burns the nonce (a corrected -retry still succeeds). - -**Classification: VERIFIED.** - -### 6.3 Razorpay daily cumulative cap ledger - -`SupabaseRazorpayDailyRefundLedger.reserve()` (read directly) is a single `INSERT ... -ON CONFLICT (refund_day) DO UPDATE SET reserved_paise = reserved_paise + EXCLUDED.reserved_paise -RETURNING reserved_paise` — the write _is_ the check, no separate read-then-decide step. -Two concurrent reservations for the same day serialize on Postgres's row lock for that -primary key. - -**Test-coverage gap, independently confirmed:** the only direct concurrency test found -(`InMemoryRazorpayDailyRefundLedger.test.ts`, 50 concurrent `reserve()` calls via -`Promise.all`, asserting an exact strictly-increasing sequence of returned totals) proves -atomicity for the **in-memory** implementation only, which is what `NODE_ENV=test` wiring -uses (confirmed directly: `createRazorpayDailyRefundLedger.ts:23-25` returns -`InMemoryRazorpayDailyRefundLedger` whenever `NODE_ENV === "test"`, including inside -`razorpay-refund.integration.test.ts`'s "five concurrent requests" case). No dedicated -integration test issuing genuinely concurrent connections against -`SupabaseRazorpayDailyRefundLedger` itself was found (`packages/storage/tests` has no file -matching the Razorpay ledger at all, confirmed by search) — unlike `consumed_nonces`, -which does have such a live-Postgres concurrency test (§6.1). The SQL mechanism -(`INSERT ... ON CONFLICT ... DO UPDATE ... RETURNING`) is the same well-established -Postgres atomicity idiom used and independently proven for `consumed_nonces`, so it is -**sound by construction**, but this specific table's live-database concurrent behavior is -**not verified by a direct automated test** in this repository as it stands. - -**Classification: VERIFIED BY CONSTRUCTION; NOT VERIFIED BY LIVE-DATABASE TEST** — disclosed -in §12.4 as a test-coverage gap, not a demonstrated defect. - -### 6.4 Envelope/signature expiry - -Execution-authorization envelope: `AuthorizationVerifier.ts:101-107` checks -`now.getTime() < Date.parse(expiresAt)`, plus a policy-enforced TTL ceiling -(`DEFAULT_MAX_TTL_SECONDS = 300`; production default is 120s, -`EXECUTION_AUTHORIZATION_TTL_SECONDS`). Approval Artifact: identical expiry check in -`ApprovalVerifier.ts:126`. Both **VERIFIED**. - -**Internal Gateway attestation** (the Gateway↔Connector trust layer, distinct from the two -envelopes above): confirmed directly that `GatewayAttestation` has no `expiresAt`/TTL -field and `SignedTokenConnectorAuthenticator.verifiedAttestation` performs no timestamp -check at all. This is self-documented in-source as intentional: replay of the same -attestation is prevented by the single-use `GatewaySession` layer downstream, which is -itself only ever reached after the durable, cross-process §6.1 nonce has already been -consumed (`ExecutionGateway.execute()` always consumes it first, before ever calling -`executionControl.service.execute()` — confirmed by reading the call order directly). -**In the current wiring this is not independently exploitable** — every path to -`ExecutionControlService` is gated behind the durable nonce check — but it is a -**residual architectural assumption**, not a structurally (e.g. type- or interface-level) -enforced guarantee. Disclosed in §12.5. - -**Overall Property D classification: VERIFIED for both production capabilities**, with two -disclosed, non-exploitable-under-current-wiring residual items (§6.3, §6.4) carried into -§12. - ---- - -## 7. Property E Verification — Concurrency Safety - -### 7.1 Primary authorization/replay controls - -Execution-authorization nonce, Approval Artifact nonce, and business-transaction -acceptance (`business_transactions.business_transaction_id TEXT PRIMARY KEY`, mapped -`23505` → `DuplicateBusinessTransactionError`) are all single-`INSERT`-plus-primary-key -mechanisms — atomic and cross-process safe by construction, the nonce stores additionally -proven under live concurrent Postgres load (§6.1). Business-transaction acceptance runs -_before_ `RuntimeEngine.execute()` (confirmed: `ExecutionTrustApplication.execute()` calls -`transactions.accept()` first), so a literal resubmission of the same -`businessTransactionId` cannot reach the refund ledger or the approval verifier twice. - -### 7.2 Razorpay daily cap under concurrency - -As in §6.3: atomic by SQL construction (the reservation upsert serializes on the -`refund_day` row lock), proven under 50-way concurrency for the in-memory test -implementation, not independently proven under live concurrent Postgres load for the -production implementation. Same classification as §6.3. - -### 7.3 Internal gateway-session / session-credential layers - -`InMemoryGatewaySessionStore.consume()` and `InMemorySessionCredentialVault.consume()` -(read directly) both perform their check-and-mark synchronously with no `await` between -them — race-free _within one process_ by Node's single-threaded execution, the same -pattern `MemoryNonceStore` uses. Confirmed by search: **neither has a persistent/ -Supabase-backed implementation** — `createSessionStore.ts` always constructs the in-memory -variant, with no production/test split, unlike every other store in this codebase. - -**Deployment context, checked directly:** `fly.toml`/`fly.live.toml` both set -`min_machines_running = 1`; the current reference deployment does not run multiple -concurrent processes against shared in-memory state by default. Since this layer sits -strictly downstream of the durable, cross-process-safe execution-authorization nonce -(§6.1/§7.1) — which is the actual replay/authorization control the claim depends on — a -session-store race would, at most, affect internal session bookkeeping, not authorization -correctness, under the current single-machine-minimum topology. **If a future deployment -horizontally scales this process, this specific internal layer would provide no -cross-process protection of its own** (though it would fail closed — "session not found" -in the other process — not fail open). Disclosed in §12.6 as a scaling-dependent residual -assumption, not a currently exploitable gap. - -### 7.4 Repository-wide concurrency-pattern search - -No use of `SELECT ... FOR UPDATE` anywhere in the repository — every atomicity guarantee -found is `INSERT`/primary-key-uniqueness or `INSERT ... ON CONFLICT ... DO UPDATE` -(upsert) based, a deliberate, consistent pattern across `consumed_nonces`, -`consumed_approval_nonces`, `business_transactions`, and -`razorpay_daily_refund_reservations`. One unrelated, out-of-scope finding: `ChallengeRecordRepository.append()` -(`packages/storage/src/postgres/PostgresChallengeRecordRepository.ts`) is a genuine, -self-documented read-then-write with no lock — but `ChallengeRecord` is an RFC-0022 -manual-investigation/audit artifact, not part of the Razorpay-refund or HubSpot-deal-update -authorization path, so it does not affect this certification's scope. - -**Overall Property E classification: VERIFIED for the controls this claim depends on** -(execution-authorization nonce, approval nonce, business-transaction uniqueness, refund -ledger reservation mechanism), with the same live-DB test-coverage gap as §6.3 and the -scaling-dependent internal-layer note in §7.3 disclosed rather than hidden. - ---- - -## 8. Property F Verification — Auditability - -Directly read `ExecutionTrustRecord`, `Execution`, `ExecutionEvidence`, and `Decision` -domain types. Every `Execution` carries an immutable `Decision` containing `policy: {name, -version, schemaVersion}` and `signals` — the exact runtime signals evaluated, verbatim — -plus `ExecutionEvidence` (`action`, `target`, `parameters`, `success`, `executedAt`, and -connector `attributes`). `DecisionBuilder.build()` sets `decision.signals = -transaction.signals` verbatim, so both the atomically-reserved Razorpay ledger total -(once independently verified equal to the real one, §4.1) and the **entire embedded -Signed Approval Artifact** (§4.2, including its own signature) end up durably captured -inside the signed Trust Record — not merely referenced by ID, but reproducible for later -independent re-verification against the issuer's public key. - -Every layer (`ExecutionTrustRecord`, `Receipt`, `SettlementConfirmation`, Refusal Record, -flat audit events) is Ed25519-signed (optionally hybrid-PQ for Trust Records/Receipts — -`docs/VERIFICATION-GAPS.md` G-4's own disclosure that this is not yet extended to -execution-authorization/gateway/connector signing, an honest, pre-existing, disclosed -scope limit, not a defect this phase's scope covers) over a canonical serialization, and -independently, cryptographically verifiable by a third party holding only the artifact -and Parmana's public key via deliberately unauthenticated `POST /audit/verify` / `POST -/refusal/verify` routes — confirmed present and mounted outside caller-auth in `app.ts`. -`audit-verify.integration.test.ts` and `refusal-record.integration.test.ts` (both -described in the evidence pass's report and cross-checked against `app.ts`'s route table -in this session) prove tamper detection end-to-end over real HTTP, including the exact -historical G-24 exploit shape now producing a verifiable Refusal Record naming the -mismatched fields. - -**Classification: VERIFIED.** - ---- - -## 9. Repository-Wide Search Results - -Searched `packages/*/src` (production code) for `TODO`, `FIXME`, `XXX`, `HACK`, -`preAuthorized`, `authorization bypass`, `temporary`, `future`, `verification gap`. - -- `preAuthorized`: addressed in full in §4.2/§4.3 — the sanctioned, independently-verified - signal name, not a bypass. -- `TODO`/`FIXME`/`HACK`: three hits, all non-exploitable — a dead scaffolding component - (`ExecutionEvidenceComponent.ts`, not on the live path; the real evidence builder is - confirmed exercised), two cosmetic placeholder-identity/same-process-only-boundary - comments (`createGatewayIdentity.ts`, `createSessionStore.ts`) that name internal, - non-caller-reachable trust boundaries, not caller-facing gaps. -- `temporary`: all benign — documented key-loading mechanisms, the already-disclosed, - never-production-registered `vendor-payment`/`sap`/`workday`/`oracle` mock connectors - (§12.1), and PostgREST-workaround comments unrelated to authorization semantics. -- `future`: roadmap/extension-point comments only (per-artifact revocation, additional - hybrid-signing surfaces) — none describe a present enforcement gap. -- `authorization bypass`: all references are to the historical G-24 finding, explicitly - documented in `docs/VERIFICATION-GAPS.md` as found, reproduced, and fixed — independently - re-confirmed fixed in this session (§5.1, §5.2: `SignalIntentBinder`/`CapabilityPolicyBinder` - are unconditionally wired, not optional). -- `verification gap`: only `docs/VERIFICATION-GAPS.md` itself (a deliberately maintained, - self-critical audit log, not production code). Its currently-open items (`OverrideService` - unreachable — independently re-confirmed §5.5; `packages/receipt` untested but disconnected - from the live route; a permanently-skipped test requiring a constructor change to fix; - hybrid signing's partial scope, §8) are all either non-security, already independently - re-verified as non-exploitable, or already disclosed above — none weaken the claim for - the two in-scope capabilities. - -**No repository item found materially contradicts the claim for `razorpay:refund-create` -or `hubspot:deal-update` as currently wired in production.** - ---- - -## 10. Adversarial Assessment - -Adversary model: an AI agent holding a **valid** Parmana caller API key and, where -applicable, valid Razorpay/HubSpot credentials issued to the connector layer (not to the -AI itself — §3 establishes the AI never possesses these), with perfect prompting, full API -knowledge, and unrestricted ability to construct `POST /execute` request bodies. - -| Attempt | Prevented? | Mechanism (file:line, traced directly or independently cross-checked) | -| ------------------------------------------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Exceed the per-refund or daily cumulative Razorpay cap by manipulating request fields | **Yes** | Amount bound to Intent via `SignalIntentBinder` (§4.1); cap enforced by atomic ledger reservation independent of any caller-declared total (§4.1, §6.3) | -| Execute a HubSpot amount change without a valid Signed Approval Artifact (omit/forge/malformed/expired) | **Yes** | `ApprovalVerifier`'s fixed-order, no-early-return check (§4.2); malformed artifacts fail closed via `isSignedApprovalShape`; today's empty issuer registry makes this doubly true operationally (§4.2 caveat) | -| Forge or replay an approval or execution authorization to authorize a different execution | **Yes** | Execution-authorization: `businessTransactionHash` recomputed and compared, single-use nonce (§6.1). Approval Artifact: `resourceId`/`capability`/scope bound to the independently-derived value, single-use nonce (§6.2) | -| Point a real capability at a different, more permissive policy (policy substitution) | **Yes** | `CapabilityPolicyBinder`, unconditionally wired, runs before evaluation (§5.1, §5.3) | -| Transfer authorization from one transaction/deal to another | **Yes** | `businessTransactionHash` binding (execution layer) and `resourceId`/scope binding against the independently-verified amount (approval layer) — both traced directly, §4.2, §6.2 | - -**No adversarial category in this assessment succeeds against either production write -capability under current repository evidence.** - ---- - -## 11. Public Claim Assessment - -The literal claim string _"Even if AI has valid credentials, it still cannot execute -anything your business hasn't authorized. No exceptions"_ does **not** appear verbatim in -`README.md`, `PROTOCOL.md`, `docs/CLAIMS.md`, or `docs/GUARANTEES.md` as searched in this -session — it is tracked and re-verified as an internal governing claim across the -`docs/architecture/phase2k` → `phase2l` → `phase3a/b/c` lineage, most recently -self-assessed in `phase2l-authorization-exceptions.md` (§9) as **not fully supported**, -naming exactly two named exceptions: Razorpay's caller-declared daily cumulative total, -and HubSpot's caller-declared `preAuthorizedForAmountChange` boolean. - -This certification independently re-traced both exceptions from current source, not from -Phase 3B/3C's own narrative, and confirms both are closed at the mechanism level (§4.1, -§4.2): the Razorpay daily cap is now repository-derived via an atomic reservation ledger, -and HubSpot's pre-authorization claim is now verified against a cryptographically signed, -independently-issued, single-use Approval Artifact, with no caller-declared boolean -trusted verbatim anywhere in the production-reachable path. - -`docs/CLAIMS.md` and `docs/GUARANTEES.md`'s related, adjacent claims (2.4 Authorized -Execution, 2.16 Caller Authentication, 2.20 Duplicate Transaction Rejection, 2.21 Policy -Denial/Replay status codes, G-04 Authorized Execution) were independently spot-checked -against current source in this session (`ExecutionGate.ts`, `app.ts`'s route table, -`SupabaseBusinessTransactionRepository.ts`) and found consistent with the current -implementation — **Fully Supported** by this session's own evidence, not merely by -citation. - ---- - -## 12. Remaining Limitations - -Every item below is disclosed because it is real and independently confirmed — not -because it undermines the certification in §13. None of them provide a currently -exploitable path for a caller (AI or otherwise) holding valid credentials to execute an -action the business has not authorized, under the current production wiring. - -1. **`payments:execute` (vendor-payment) is not a production capability today, and would - not satisfy this claim if it ever became one.** Its signals (`vendorVerified`, - `invoiceVerified`, `paymentApproved`, `sufficientFunds`, `riskScore`) remain pure - caller-declared attestations with no independent verifier and no real system to fetch - them from (confirmed by `docs/VERIFICATION-GAPS.md`'s own investigation, independently - spot-checked: the plausible connectors — SAP, Workday, Oracle — are write-only mocks - with no fetch capability). This is out of this certification's scope only because the - capability is not currently reachable in production (§2) — it is flagged here so that - enabling it in the future is understood to require the same closure work Razorpay and - HubSpot already received, not an oversight this certification missed. **Not closed**: - would require building genuine external verification integrations (KYB, AP-matching, - treasury, risk-scoring) that do not exist in this repository in any form — out of scope - for a remediation pass, a real future project in its own right. -2. ~~Truncated credential fragments (8 chars of Razorpay `key_id`, 12 chars of the HubSpot - bearer token) reach the AI-facing `POST /execute` response body, unfiltered by the - generic metadata-redaction pattern (§3.3).~~ **CLOSED, same session.** - `redactRazorpayKeyId`/`redactHubSpotToken` (`RazorpayTypes.ts`, `HubSpotTypes.ts`) now - return a one-way, truncated SHA-256 fingerprint (`fp_` + 12 hex chars) instead of a - literal substring of the credential — the operator-facing "which key executed this" - signal is preserved (two executions using the same credential still produce the same - fingerprint; a rotated credential produces a different one), but zero credential bytes - of any length reach `ConnectorResponse.metadata`, `ExecutionEvidence.attributes`, the - Trust Record, or the `POST /execute` response body. Both connector unit tests - (`razorpay-connector.test.ts`, `hubspot-connector.test.ts`) were strengthened to assert - the response body contains no substring of the credential at all, not merely a - different-shaped redacted value — closing the exact regression-coverage gap §3.3 - originally flagged. `npx tsc -b` clean; both suites re-run and passing (22/22). -3. **HubSpot's Signed Approval Artifact mechanism has never been exercised against a real, - operator-provisioned issuer key in production** (`TRUSTED_APPROVAL_ISSUERS` ships empty, - §4.2). Fail-closed and therefore not a weakness in itself, but it means the specific - pathway this phase certifies as sound has only been proven in unit/integration tests - against synthetic issuers, not in live operation. **Not closed, deliberately.** - `TRUSTED_APPROVAL_ISSUERS` is a hardcoded, non-injectable list by design - (`createApprovalIssuerRegistry.ts`'s own comment: mirrors - `createConnectorAuthenticator.ts`'s trusted-identity model exactly, self-service - onboarding explicitly out of scope). Making it testable end-to-end through the real - bootstrap function would mean either weakening that deliberate rigidity or fabricating a - registry entry that does not correspond to a real, operator-provisioned key — which - would not be evidence of anything real. Provisioning a genuine approver key is - inherently an operational action for a real business approver to take, not something a - code change can substitute for. -4. ~~The Razorpay daily-cumulative-cap ledger's atomicity is proven under live concurrency - for its in-memory (test) implementation only.~~ **CLOSED, same session.** - `packages/storage/tests/integration/supabase-razorpay-daily-refund-ledger.integration.test.ts` - (new file) adds a direct live-Postgres proof, mirroring - `supabase-nonce-store.integration.test.ts`'s own pattern: a two-way race asserting exact, - non-lost-update totals, a 20-way concurrent-reservation proof matching the in-memory - implementation's own 50-way unit test in kind, and a `release()`-floors-at-zero case. - Gated the same way every other live-database suite in this repository is - (`resolveDatabaseGate`, `ALLOW_LIVE_SUPABASE=1` required to actually run against a real - project) — it was not executed against a live database in this session (doing so writes - real rows to a real project and was not separately authorized), but it compiles cleanly, - is wired into the same suite every other live-DB test already runs under, and skips - cleanly (confirmed: `1 file skipped, 4 tests skipped`) exactly like its siblings when no - live credentials are opted in. -5. **The internal Gateway↔Connector attestation has no independent expiry/TTL of its own** - and relies entirely on being gated behind the durable execution-authorization nonce - upstream in the current wiring (§6.4). **Deliberately not closed.** `GatewayAttestation` - is a shared, foundational crypto primitive (`packages/execution-control/src/ -GatewayAttestation.ts`) whose current no-TTL design is explicitly documented in-source as - intentional, with replay handled at a different, already-durable layer. Adding an - independent TTL is architecturally reasonable defense-in-depth, but is exactly the kind - of new-authorization-primitive change this codebase's own established practice (see - Phase 2L's STOP conditions, TD-23's split into properly-chartered phases) treats as - requiring its own dedicated design phase, not a same-session edit to a primitive several - packages depend on — especially since the adversarial review (§10) found no currently - exploitable path through it. -6. **The internal gateway-session and session-credential-vault layers are in-memory, - single-process only, with no persistent/shared implementation** (§7.3). **Deliberately - not closed.** Building a persistent/shared implementation is infrastructure work of the - same shape `02-REMAINING.md` already tracks as a dedicated, multi-week "big rock" - (nonce-store persistence), not a same-session fix — and, as §7.3 established, this layer - is downstream of and dominated by the already-durable, cross-process-safe nonce check - that is the actual load-bearing control here. -7. **`OverrideService`/`OverrideVerifier` remain unreachable dead code** (§5.5) — safe as - is, but an open design decision (`docs/VERIFICATION-GAPS.md` G-5) rather than a resolved - one. **Deliberately not touched.** `02-REMAINING.md`'s own Tier 0 entry for this - component is an explicit security guard reading "do NOT wire overrides" until its own - documented deficiencies (legacy hashing, no nonce/TTL, no approver authorization) are - fixed with a design partner's input on roles/scopes/rate limits. Wiring it up to "close" - this item would directly contradict that guard and introduce real risk; leaving it - untouched is the correct action, not an omission. -8. **Hybrid/post-quantum signing is wired for Trust Records and Receipts only**, not for - execution-authorization signing, Gateway attestation, or connector signing — an - already-disclosed (`docs/VERIFICATION-GAPS.md` G-4), narrower-than-`CRYPTO_MODE=hybrid`- - implies scope that does not affect this claim (which concerns authorization decisions, - not signature algorithm strength). **Not closed**: G-4 already documents this as a - separately-chartered, larger expansion project, unrelated in kind to this remediation - pass's scope. - -**Summary of this session's remediation:** of the eight items disclosed above, two (#2, #4) -were genuinely closable with small, safe, additive changes and were closed in this same -session, both verified by a clean `tsc -b` and passing tests. The remaining six are either -out of scope for any single remediation pass (#1, #8), inherently require an operational -action a code change cannot substitute for (#3), or would require touching a shared -security-critical primitive or unreachable-by-design component in a way this codebase's own -established practice — and, for #7, an explicit standing security guard — correctly treats -as requiring a dedicated, separately-chartered phase rather than an opportunistic same-session -edit (#5, #6, #7). None of the six remaining items provide a currently exploitable path for -an AI holding valid credentials to execute an unauthorized action. - ---- - -## 13. Evidence Summary - -**Repository searches performed directly in this session:** `OverrideService`/ -`OverrideVerifier` reachability (zero call sites confirmed in `packages/api/src`, -`RuntimeFactory.ts`, `RuntimeEngine.ts`); `preAuthorized`-family and trust-flag pattern -search across `packages/*/src`; the literal claim string across `README.md`, -`PROTOCOL.md`, `docs/CLAIMS.md`, `docs/GUARANTEES.md`; Razorpay-ledger concurrency test -file search across `packages/storage/tests`. - -**Source references read directly, in full or in the cited ranges, in this session:** -`packages/api/src/routes/execute.ts`, `packages/api/src/bootstrap/{createConnectorRegistry, -createRazorpayCredentialProvider,createRazorpayConnector,createRazorpayDailyRefundLedger}.ts`, -`packages/runtime/src/{RuntimeEngine,RuntimeFactory,RuntimeBuilder,ExecutionGate}.ts`, -`packages/policy/src/CapabilityPolicyBinding.ts`, -`packages/connector-sdk/src/connectors/razorpay/RazorpaySignalStateVerifier.ts`, -`packages/connector-hubspot/src/HubSpotSignalStateVerifier.ts`, -`packages/approval/src/ApprovalVerifier.ts`, -`packages/execution-gateway/src/connector-execution/{GatewayRazorpayAdapter, -createGatewayRazorpayConnector,SdkConnectorExecutor}.ts`, -`packages/storage/src/supabase/{SupabaseNonceStore,SupabaseRazorpayDailyRefundLedger}.ts`, -`packages/shared/src/domain/execution-trust-record.ts`, -`policies/{razorpay-refund,hubspot-deal-update}/1.0.0/policy.json`, `fly.toml`/`fly.live.toml`, -plus targeted sections of `docs/VERIFICATION-GAPS.md`, `docs/architecture/phase2l-authorization-exceptions.md`, -`docs/architecture/phase3{a,b,c}-*.md`, and `02-REMAINING.md`, all treated as claims to -verify, not evidence in themselves. - -**Independent evidence passes:** four, covering Property A; Property B+C; Property D+E; -Property F + repository search + adversarial review — each instructed to cite file:line -for every claim and to mark unconfirmed items explicitly. Two flags raised were -independently closed by direct re-reading in this session (§5.2); all other flags raised -were incorporated as disclosed limitations (§12) rather than dismissed. - -**Regression tests (run fresh in this session, not inherited):** - -``` -npx tsc -b → clean, 0 errors -npm test -- --maxWorkers=2 → 148 test files passed, 15 skipped (163 total) - 1039 tests passed, 39 skipped (1078 total), 0 failed -``` - ---- - -## Final Verification - -| Item | Status | -| --------------------------------------------------------- | -------------------------------------------------------------------- | -| Every production capability independently verified | ✓ — §2, derived from bootstrap gating logic, not documentation | -| Credential isolation independently verified | ✓ (with disclosed fragment-leakage exception) — §3 | -| Independent authorization independently verified | ✓ (with disclosed HubSpot-issuer operational caveat) — §4 | -| Canonical capability binding independently verified | ✓ — §5.1, unconditionally wired, exploit-shaped test passes | -| Structural enforcement / no bypass independently verified | ✓ — §5.2, both open flags from the evidence pass closed directly | -| Replay resistance independently verified | ✓ (with disclosed live-DB test-coverage gap for one ledger) — §6 | -| Concurrency safety independently verified | ✓ (with disclosed scaling-dependent internal-layer note) — §7 | -| Auditability independently verified | ✓ — §8 | -| Repository searched for contradictory evidence | ✓ — §9, nothing found that materially contradicts the claim in scope | -| No production source code changed | ✓ — this phase performed reads only | -| No documentation modified | ✓ — this new file is the only addition | - ---- - -## Final Recommendation - -**CLAIM FULLY CERTIFIED.** - -For the two capabilities actually reachable in production today — `razorpay:refund-create` -and `hubspot:deal-update` — every authorization-relevant input is either independently -re-derived from the real external system, structurally bound to the executed Intent, or -cryptographically verified against an artifact issued by a party independent of the -caller; no caller-declared "this is authorized" value is ever trusted on its own anywhere -in the production-reachable path; capability-to-policy binding, replay protection, and -concurrent-request safety are all enforced by mechanisms unconditionally wired into -production bootstrap, not optional configuration; and every successful execution produces -independently, cryptographically verifiable evidence sufficient to reconstruct exactly -what was authorized and why. No adversarial scenario traced in this certification — for a -caller holding a valid Parmana API key and full knowledge of the request format — -succeeds in executing an action the business has not authorized. - -This certification is not unconditional. Eight specific, independently-verified -limitations are disclosed in §12, none of which currently permit an unauthorized -execution under present repository wiring, but several of which (the Supabase ledger's -missing live-concurrency test, the internal Gateway-session layer's single-process -scope, HubSpot's never-yet-live-exercised issuer registry) are architectural or -test-coverage gaps that a future change to wiring, deployment topology, or connector -credentials could turn into real ones if not re-verified at that time. `payments:execute` -is explicitly out of this certification's scope because it is not currently a production -capability — it would not pass this certification if enabled as currently written (§12.1). - -The claim under review — "cannot execute anything your business hasn't authorized" — -is supported, by direct, independently-traced repository evidence, for every capability -this system currently exposes to execution. diff --git a/docs/architecture/post-quantum-cryptography.md b/docs/architecture/post-quantum-cryptography.md deleted file mode 100644 index 5c3aaca0..00000000 --- a/docs/architecture/post-quantum-cryptography.md +++ /dev/null @@ -1,293 +0,0 @@ -\# Post-Quantum Cryptography - -\## Overview - -Parmana supports both classical and post-quantum digital signatures. - -Digital signatures are used to protect: - -\* Execution Trust Records - -\* Receipts - -\* Execution Authorizations (the signed envelope forwarded to receiving systems) - -The active signature algorithm is selected through configuration without changing application code, for one process at a time — see "Operational Constraint: One Provider Per Process" below. - -\--- - -\# Supported Algorithms - -| Algorithm | Type | Status | - -| ---------------------- | ------------ | --------- | - -| Ed25519 | Classical | Supported (default) | - -| ML-DSA-65 (Dilithium3) | Post-Quantum | Supported | - -ML-DSA-65 is implemented using Node's native `node:crypto` support (`generateKeyPairSync("ml-dsa-65")`, `crypto.sign`/`crypto.verify`), backed by OpenSSL >= 3.5. This requires \*\*Node >= 24\*\*. No third-party post-quantum library is used for signing or verification. - -Future releases may add: - -\* ML-DSA-87 - -\* SLH-DSA (SPHINCS+) - -\--- - -\# Configuration - -The signature provider is configured through the environment. - -```dotenv - -SIGNATURE\_PROVIDER=ed25519 - -``` - -or - -```dotenv - -SIGNATURE\_PROVIDER=dilithium3 - -``` - -No application code changes are required. Invalid values are rejected at config load time; an unset value defaults to `ed25519`. - -\--- - -\# Architecture - -``` - -Application - - │ - - ▼ - -ReceiptCrypto / VerificationCrypto / AuthorizationSigner / AuthorizationVerifier - - │ - - ▼ - -CryptoBootstrap - - │ - - ▼ - -SignatureRegistry - - │ - - ├───────────────┐ - - ▼ ▼ - -Ed25519SignatureProvider Dilithium3SignatureProvider - -``` - -Only the configured provider is used for signing and verification. `CryptoBootstrap` caches the resolved provider for the lifetime of the process — see the operational constraint below. - -\--- - -\# Persistent Key Management - -Parmana stores signing keys outside individual packages, as PEM files read by `FileKeyProvider`. - -``` - -/ - - default.private.pem - - default.public.pem - -``` - -The same two filenames are used regardless of which algorithm is configured — a directory holds keys for exactly one algorithm at a time, matching whichever `SIGNATURE\_PROVIDER` produced them. `FileKeyProvider` and `node:crypto`'s `createPrivateKey`/`createPublicKey` auto-detect the key type from the PEM's own encoding (PKCS8/SPKI), so no separate raw-key format is needed for ML-DSA-65. - -The directory defaults to `./keys` (resolved relative to the process's working directory) and can be overridden with: - -```dotenv - -PARMANA\_KEY\_DIR=/absolute/path/to/keys - -``` - -\#\# Generating keys - -Keys are \*\*not\*\* generated automatically. Use the provided script: - -```bash - -node ./node\_modules/tsx/dist/cli.mjs packages/crypto/scripts/generate-keypair.ts --algorithm ed25519 - -\# or - -node ./node\_modules/tsx/dist/cli.mjs packages/crypto/scripts/generate-keypair.ts --algorithm dilithium3 - -``` - -The script refuses to overwrite an existing `default.private.pem`/`default.public.pem` pair unless `--force` is passed. - -\#\# Fail-closed behavior - -If `SIGNATURE\_PROVIDER` is set to an algorithm whose key files are missing at the configured key directory, `FileKeyProvider` throws rather than generating a keypair on the fly. If the key files exist but are the \*\*wrong type\*\* for the configured provider (for example, `SIGNATURE\_PROVIDER=dilithium3` pointed at a directory holding Ed25519 PEMs), signing and verification fail closed with a `CryptoError` naming both the expected and actual key type (`assertKeyType`, used by both `Ed25519SignatureProvider` and `Dilithium3SignatureProvider`) — the process never silently signs with the wrong algorithm. - -This is a deliberate change from an earlier design: Parmana does \*\*not\*\* auto-generate keys on first use. A missing or mismatched key is always an error, never a silent fallback. - -\--- - -\# Runtime Selection - -The configured provider is selected through the Signature Registry. - -``` - -SignatureRegistry - - - - register(Ed25519SignatureProvider) - - - - register(Dilithium3SignatureProvider) - - - - │ - - - - ▼ - - - -config.crypto.signatureProvider (from SIGNATURE\_PROVIDER) - -``` - -The remainder of the Parmana runtime is algorithm-independent: `AuthorizationSigner`, `ExecutionTrustRecordBuilder`, and `ReceiptCrypto` all read the active algorithm from the configured provider (`this.crypto.signature.algorithm`) rather than hardcoding it. - -\--- - -\# Operational Constraint: One Provider Per Process - -`CryptoBootstrap.create()` resolves and caches a single `CryptoProvider` for the lifetime of the process. A process is configured for exactly one signature algorithm at a time — it cannot sign or verify both Ed25519 and ML-DSA-65 authorizations simultaneously. - -This has a direct consequence for cross-system verification: \*\*a signer process and a verifier process must be configured with the same `SIGNATURE\_PROVIDER` and must share the corresponding key material.\*\* `AuthorizationVerifier` does not inspect or dispatch on the envelope's own `algorithm` field — that field is informational metadata recorded in the envelope, not a routing key. A verifier configured for `ed25519` will simply fail to verify an ML-DSA-65-signed envelope (and vice versa), regardless of what the envelope's `algorithm` field says. - -\--- - -\# Algorithm Migration Is Unsolved - -Re-keying an existing deployment from one signature algorithm to another (for example, Ed25519 to ML-DSA-65) while retaining the ability to verify records signed before the switch is \*\*not currently supported\*\*. There is no mechanism for a single verifying process to hold multiple active providers, and no documented migration procedure. Treat a `SIGNATURE\_PROVIDER` change as applying only to authorizations signed after the change; previously issued authorizations and historical Trust Records signed under the old algorithm cannot be verified by a process now configured for the new one. - -\--- - -\# Randomized Signatures (ML-DSA-65) - -ML-DSA-65 signatures are randomized by design: signing the same message twice with the same private key produces two different, independently valid signatures. This is expected cryptographic behavior, not a defect. Any code or test that asserts signature determinism (comparing two signatures of identical input for equality) is valid for Ed25519 only and must not be applied to the post-quantum provider. - -\--- - -\# Receipt Signing - -Receipt generation uses the configured signature provider. - -``` - -Receipt - - │ - - ▼ - -Canonical Serialization - - │ - - ▼ - -Receipt Hash - - │ - - ▼ - -Configured Signature Provider - - │ - - ▼ - -Digital Signature - -``` - -Receipts also record the algorithm that produced the signature. - -Example: - -```json - -{ - - "algorithm": "dilithium3", - - "signature": "" - -} - -``` - -\--- - -\# Security Considerations - -Private keys must never be committed to source control. - -The following should be ignored by Git: - -``` - -keys/ - -\*\*/keys/ - - - -\*.pem - -\*.key - -``` - -Production deployments should use a secure key management solution such as: - -\* AWS KMS - -\* Azure Key Vault - -\* Google Cloud KMS - -\* HashiCorp Vault - -\* Hardware Security Modules (HSM) - -The current file-based provider (`FileKeyProvider`) is intended for development and local deployments only; no KMS/HSM/cloud key vault integration exists today. - -\--- - -\# Benefits - -Post-quantum signatures provide resistance against attacks from cryptographically relevant quantum computers. - -Parmana's provider-based architecture allows organizations to migrate from classical signatures to post-quantum signatures by changing configuration rather than modifying application code, \*\*for newly signed data\*\* — see "Algorithm Migration Is Unsolved" above for what this does not yet cover. diff --git a/docs/architecture/receipt-cryptography.md b/docs/architecture/receipt-cryptography.md deleted file mode 100644 index e96cc328..00000000 --- a/docs/architecture/receipt-cryptography.md +++ /dev/null @@ -1,327 +0,0 @@ -\# Receipt Cryptography - -\## Overview - -Receipts are cryptographic attestations that prove an Execution Trust Record has been successfully verified. - -A receipt contains: - -\* Receipt metadata - -\* Canonical receipt hash - -\* Trust Record hash - -\* Digital signature - -\* Signature algorithm - -Receipts provide tamper-evident proof that an execution has been authorized, verified, and recorded. - -\--- - -\# Purpose - -A receipt serves four purposes: - -\* Prove the integrity of an Execution Trust Record. - -\* Provide a cryptographic signature from Parmana. - -\* Enable independent verification. - -\* Produce long-term audit evidence. - -\--- - -\# Receipt Structure - -```text - -Receipt - -│ - -├── receiptId - -├── businessTransactionId - -├── trustRecordHash - -├── receiptHash - -├── issuedAt - -├── algorithm - -└── signature - -``` - -Every receipt is immutable after creation. - -\--- - -\# Receipt Generation Pipeline - -```text - -Execution Trust Record - - │ - - ▼ - -Canonical Serialization - - │ - - ▼ - -TrustRecordHasher - - │ - - ▼ - -ReceiptHasher - - │ - - ▼ - -ReceiptSigner - - │ - - ▼ - -Receipt - -``` - -Each stage has a single responsibility. - -\--- - -\# Canonical Serialization - -Before hashing, Parmana converts the receipt into a deterministic representation. - -Requirements: - -\* Stable property ordering - -\* UTF-8 encoding - -\* No formatting differences - -\* No runtime-dependent serialization - -Every compliant implementation must produce identical bytes for identical receipts. - -\--- - -\# Receipt Hash - -The receipt hash protects the receipt contents. - -```text - -Receipt - - │ - - ▼ - -Canonical Bytes - - │ - - ▼ - -Hash Provider - - │ - - ▼ - -receiptHash - -``` - -The hash provider is configurable. - -Current implementation: - -\* SHA-256 - -Future implementations: - -\* SHA3-512 - -\* BLAKE3 - -\--- - -\# Digital Signature - -The canonical receipt is digitally signed. - -```text - -receiptHash - - │ - - ▼ - -Signature Provider - - │ - - ▼ - -signature - -``` - -Supported providers: - -\* Ed25519 - -\* ML-DSA-65 (Dilithium3) - -The selected provider is determined by configuration. - -\--- - -\# Receipt Fields - -| Field | Description | - -| --------------------- | ---------------------------------- | - -| receiptId | Unique receipt identifier | - -| businessTransactionId | Business transaction reference | - -| trustRecordHash | Hash of the Execution Trust Record | - -| receiptHash | Hash of the canonical receipt | - -| issuedAt | Receipt creation timestamp | - -| algorithm | Signature algorithm | - -| signature | Base64-encoded digital signature | - -\--- - -\# Verification - -Receipt verification performs the following steps: - -```text - -Receipt - - │ - - ▼ - -Canonical Serialization - - │ - - ▼ - -Receipt Hash - - │ - - ▼ - -Signature Verification - - │ - - ▼ - -Verified / Invalid - -``` - -Verification succeeds only if: - -\* The receipt has not been modified. - -\* The receipt hash matches. - -\* The signature is valid. - -\* The configured public key verifies the signature. - -\--- - -\# Algorithm Independence - -Receipt generation is independent of the underlying signature algorithm. - -Changing: - -```dotenv - -SIGNATURE\_PROVIDER=ed25519 - -``` - -to - -```dotenv - -SIGNATURE\_PROVIDER=dilithium3 - -``` - -changes only the cryptographic implementation. - -The receipt generation workflow remains identical. - -\--- - -\# Security Properties - -Receipts provide: - -\* Integrity - -\* Authenticity - -\* Tamper evidence - -\* Independent verification - -\* Non-repudiation (subject to key management) - -Receipts do not encrypt data. Their purpose is to prove integrity and origin. - -\--- - -\# Design Principles - -Receipt cryptography follows these principles: - -\* Deterministic serialization - -\* Immutable receipt structure - -\* Algorithm-independent business logic - -\* Pluggable cryptographic providers - -\* Persistent signing keys - -\* Verifiable audit evidence - -These principles ensure that receipts remain trustworthy regardless of the cryptographic algorithm selected by the deployment. diff --git a/docs/architecture/repository-certification.md b/docs/architecture/repository-certification.md deleted file mode 100644 index d76d13bc..00000000 --- a/docs/architecture/repository-certification.md +++ /dev/null @@ -1,271 +0,0 @@ -# Repository Certification — Phase 1H - -Independent, evidence-based certification of the architecture established by Phases 1A–1G, performed as an external audit against the implementation, not a continuation of the phases being certified. - -**Certified against:** commit `e8e2265` (`docs(architecture): align repository documentation with implementation (Phase 1G)`), the tip of `main` at the start of this pass. Working tree was clean before certification began (verified: `git status`, `git diff --stat`, `git log --oneline -10`). - -**Method.** Every claim inherited from Phases 1A–1G — including claims made by the Phase 1E/1F/1G documents themselves — was treated as an unverified hypothesis and re-derived directly from source: `grep`/`git log` across `packages/*/src`, reading method bodies in call order, diffing `package.json` dependencies and `tsconfig.json` project references against the documented dependency graph, running the built declaration files against the public barrels, and independently re-running `tsc -b`, `npm test -- --maxWorkers=2`, `npm run lint`, `npm run typecheck`, and `npm run lint:openapi`. Where a claim held up, it's certified with the exact evidence below. Where a falsification attempt succeeded, it's recorded as a finding — not silently fixed in code or quietly patched over — per this phase's own rule: _if a claim can't be demonstrated from the implementation, report it as uncertified rather than modifying code to make it true._ - -**Disclosure on independence.** This pass ran in the same session that performed Phase 1G's documentation work, not a literally fresh session. To compensate, every claim below — including ones this session itself authored in Phase 1G — was re-derived from source rather than recalled, and this report explicitly names the two places (§4, §9) where a Phase 1G document's own claim turned out to need correction, and the two places (§4, §9) where an inherited Phase 1E/1F claim was found to be false. A truly independent reviewer (fresh session or different auditor) re-running the same evidence commands in §11 should reach the same conclusions; that reproducibility, not the auditor's memory state, is what this report leans on. - ---- - -## 1. Repository Overview - -Parmana is an authorization layer for AI-initiated execution: it sits between an AI system's decision to take an action and the business system that performs it, guaranteeing the action only happens if policy-approved, signed, verified, and auditable. npm-workspaces monorepo, TypeScript project references (`tsc -b`), Vitest test runner. - -## 2. Certification Scope - -As specified by Phase 1H: repository architecture, package ownership, execution ownership, execution pipeline, authorization flow, signal verification, replay protection, credential handling, audit generation, public API boundaries, repository invariants, documentation consistency, test coverage, build reproducibility. - -## 3. Repository Inventory - -**Packages (14, all under `packages/*`):** `api`, `connector-hubspot`, `connector-sdk`, `crypto`, `envelope-verifier`, `execution-control`, `execution-gateway`, `execution-system`, `policy`, `receipt`, `replay`, `runtime`, `shared`, `storage`. - -**Internal dependency edges** (from each package's `package.json` `dependencies`, cross-checked against `tsconfig.json` `references` where a type-only edge was suspected): - -| Package | Depends on (`@parmana/*`) | -| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| `shared` | — | -| `policy` | — | -| `crypto` | `shared` | -| `envelope-verifier` | `crypto`, `shared` | -| `execution-system` | `shared` | -| `execution-control` | `crypto`, `shared` | -| `receipt` | `crypto`, `shared` (+ type-only `tsconfig` references to `execution-control`, `execution-system`) | -| `storage` | `envelope-verifier`, `shared` | -| `runtime` | `crypto`, `execution-system`, `policy`, `shared` (+ `tsconfig` reference to `storage`) | -| `replay` | `runtime`, `storage` | -| `connector-sdk` | `crypto`, `envelope-verifier`, `execution-system`, `policy`, `shared` | -| `connector-hubspot` | `connector-sdk`, `crypto`, `envelope-verifier`, `execution-system`, `policy`, `shared` | -| `execution-gateway` | `connector-hubspot`, `connector-sdk`, `crypto`, `envelope-verifier`, `execution-control`, `execution-system`, `shared` | -| `api` | `connector-hubspot`, `connector-sdk`, `crypto`, `envelope-verifier`, `execution-control`, `execution-gateway`, `policy`, `runtime`, `storage` | - -Verified acyclic by successful `tsc -b` (project-reference builds fail on cycles by construction). - -**Public API entry points (`src/index.ts` per package):** all 13 library packages export a conventional barrel; `api` has no `main`/`types` (it's the deployable application, not a library). - -**Execution components (traced in §4):** `ExecutionTrustApplication`, `Runtime`, `RuntimeEngine`, `RuntimePipeline`, `ExecutionComponent`, `ExecutionGateway`, `ExecutionControlService`, `SessionCredentialSecureConnector`, `SdkConnectorExecutor`, `GatewayRazorpayAdapter`/`GatewayHubSpotAdapter`/`GatewayHttpAdapter`/`HttpConnector`. - -**Architecture tests:** `tests/architecture/execution-boundary.test.ts` (393 lines, 12 `describe` blocks — Phase 1E/1F invariant enforcement), `tests/architecture/documentation-references.test.ts` (Phase 1G doc-reference validation). - -**CI configuration:** `.github/workflows/ci.yml` (`build-and-test`: checkout → Node 24 → `npm ci` → `npm run build` → `npm run lint` → `npm run typecheck` → terminology guard → `npm test`, on push to `main` and on every PR) and `.github/workflows/python-sdk.yml` (Python SDK, path-filtered, unrelated to this certification's scope). - -**Test inventory (tracked `.test.ts` files):** 151 total — 107 under `tests/unit/`, 29 under `tests/integration/`, 1 `e2e`, 2 repo-root architecture tests, plus package-local unit suites not matching those directory names. Two integration tests are named and gated as live-vendor tests (`razorpay-live.integration.test.ts`, `hubspot-live.integration.test.ts`), `describe.skipIf`-gated on real vendor credentials that are not present in CI. - -## 4. Architecture Certification - -Each claim below was actively targeted for falsification (alternate call-site patterns, alternate HTTP mechanisms, repo-wide scans rather than trusting the existing test's own file list) before being certified. - -### 4.1 Exactly one production execution pipeline — **Certified** - -Traced end-to-end in source, not just via the existing test: `packages/api/src/routes/execute.ts` → `ExecutionTrustApplication.execute()` (`packages/runtime/src/ExecutionTrustApplication.ts:64`) → `Runtime.execute()` (`Runtime.ts:31`) → `RuntimeEngine.execute()` (`RuntimeEngine.ts:125`) → `RuntimePipeline.execute()` (`RuntimePipeline.ts:30`) → `ExecutionComponent.execute()` (`ExecutionComponent.ts:44`) → injected `ExecutionSystem.execute()`, bound exactly once in production to `ExecutionGateway` (`packages/api/src/bootstrap/createExecutionSystem.ts`, called from `server.ts:35`) → `ExecutionGateway.execute()` (`ExecutionGateway.ts:214`) → `ExecutionControlService.execute()` (`ExecutionControlService.ts:44`) → `SecureConnector.execute()` (production: `SessionCredentialSecureConnector.ts:70`) → `SdkConnectorExecutor.execute()` (`SdkConnectorExecutor.ts:41`) → vendor adapter. - -Independently re-derived every `connector.execute(`/`adapter.execute(` call site across `packages/*/src` (not test files): exactly 3 — `ExecutionControlService.ts:117`, `SdkConnectorExecutor.ts:91`, `RazorpaySettlementProcessor.ts:162` (the one named, read-only, out-of-band worker exception, driven by `scripts/process-razorpay-settlements.ts`, not the request path). No fourth call site exists. - -### 4.2 Execution ownership belongs exclusively to `ExecutionGateway` — **Certified, with a documented caveat (§4.7)** - -Independently re-derived every `implements Connector` across `packages/*/src`: 7 matches — `GatewayRazorpayAdapter.ts`, `GatewayHubSpotAdapter.ts`, `GatewayHttpAdapter.ts`, `HttpConnector.ts` (all four inside `execution-gateway/src`), and `MockConnector.ts` (`connector-sdk/src`, the one named non-gateway exception). No connector package (`connector-sdk`, `connector-hubspot`) implements a vendor adapter. Zero `fetch(` calls, and zero alternate HTTP mechanisms (`axios`, `http.request`, `undici`, etc. — separately grepped for) exist in either connector package's `src/`. - -**Caveat, not a boundary violation:** `MockConnector` — documented in its own source as "Scripted, in-memory Connector for hermetic tests" — is in fact constructed by production bootstrap and live in the production connector registry. See §4.7; this is a real-execution-integrity finding, not an architectural-boundary violation, because `MockConnector` remains the one class the boundary test already names as permitted outside `execution-gateway`, and it is dispatched through the same full pipeline as every other connector. - -### 4.3 Connector packages are passive — **Certified** - -`connector-sdk/src` and `connector-hubspot/src`: zero `fetch(` calls, zero `implements Connector` (production code — `MockConnector` is the SDK's own explicit test double, not a vendor adapter), confirmed by direct grep, not by trusting `execution-boundary.test.ts`'s own scan. - -### 4.4 `RuntimeEngine` never bypasses `ExecutionGateway` — **Certified** - -Read `RuntimeEngine.ts`'s and `ExecutionTrustApplication.ts`'s full import lists directly (not the test's summary): no import of `@parmana/execution-gateway`, `@parmana/connector-sdk`, or `@parmana/connector-hubspot` in either file. `RuntimeEngine` imports only `@parmana/shared`, `@parmana/policy`, and its own local files. - -### 4.5 Authorization precedes execution — **Certified** - -Read `RuntimeEngine.execute()` top to bottom: `executionGate.enforce(decision)` (throws for any non-`APPROVED` decision) runs at line 321, strictly before `authorizationSigner.sign(...)` at line 343–357, strictly before the `RuntimeContext` carrying `execution` is built (line 379) and handed to `RuntimePipeline.execute()`. No path reaches `ExecutionComponent`/`ExecutionGateway` without a signed authorization already in hand. - -### 4.6 Signal verification precedes authorization — **Certified** - -Same read: signal/intent binding (`SignalIntentBinder.findViolations`, line 177) runs before `PolicyEngine.evaluate` is even called for a binding violation; signal/state verification (`this.signalStateVerifier.findViolations`, line 239) runs only when the provisional decision is `APPROVE`, and both precede `executionGate.enforce()` (line 321) and authorization signing (line 343). A state-verification mismatch overrides the decision to `REJECT` before any authorization exists — confirmed directly in the branch at lines 254–273. - -### 4.7 Replay protection precedes execution — **Certified** - -Read `ExecutionGateway.verify()` directly (`ExecutionGateway.ts:141–199`): signature/expiry/TTL checks (`envelopeVerifier.verifyChecks`) and the business-transaction content-hash comparison run first; the nonce is checked and **consumed** (`envelopeVerifier.consumeNonce`) last, gated on `priorChecksPassed` (line 186–190) — a forged or mismatched request cannot burn a nonce. Only on full success does control reach `ExecutionControlService`. - -### 4.8 Audit generation remains intact — **Certified** - -`ExecutionControlService.ts` writes to `ExecutionAuditSink` at 3 call sites (lines 85, 121, 134); `SessionCredentialSecureConnector.ts` at 2 (lines 91, 103) — session creation, credential acquisition, and execution completion, matching the documented audit model. - -### 4.9 Finding: MockConnector is live in production, contradicting Phase 1E's own claim — **New finding, not previously certified** - -`packages/api/src/bootstrap/createVendorPaymentConnector.ts` unconditionally constructs `new MockConnector({ connectorId: "vendor-payment", capabilities: ["payments:execute"] })` — its own docstring: _"This is the production bootstrap for the vendor-payment connector. The current implementation uses MockConnector until the real enterprise connector is introduced."_ `createConnectorRegistry.ts` registers it unconditionally (unlike Razorpay/HubSpot, which are credential-gated and skip registration — with a `console.warn` — when unconfigured). Traced the live wiring: `server.ts:35` → `createExecutionSystem()` → `createExecutionGateway()` → `createExecutionControl()` → `createConnectorRegistry()`. `git log` confirms `createVendorPaymentConnector.ts` was introduced in commit `ddf4bc5` (2026-07-09), well before Phase 1E's baseline (`bdd0da8`, 2026-08-04). - -`docs/architecture/execution-pipeline-report.md` (Phase 1E) stated: _"MockConnector... Explicit test double; never constructed by production bootstrap (confirmed: no `packages/api/src/bootstrap/*.ts` file references it)."_ **This claim was false when written and remained false through Phases 1F and 1G**, uncaught because no automated test checks it — `execution-boundary.test.ts`'s allowlist only asserts _which file_ may implement `Connector` outside `execution-gateway`, never _whether_ that file is constructed by bootstrap. `docs/CLAIMS.md` (a pre-existing, non-Phase-1G document) already discussed `vendor-payment` as a real, production-reachable connector used in its own adversarial-testing narrative — so the underlying fact was known elsewhere in the repository; Phase 1E's specific characterization of `MockConnector` was simply wrong. - -**Impact:** a `payments:execute` transaction submitted against `vendor-payment` in a running production deployment goes through the full, genuine authorization/signature/audit ceremony and returns `{ success: true, metadata: {} }` from `MockConnector.execute()` with no real vendor ever contacted — yet receives a cryptographically signed `ExecutionTrustRecord` and `Receipt` indistinguishable, from the caller's side, from a real execution. - -**Corrective action taken this pass (documentation only, per this phase's charter):** corrected the false claim in `execution-pipeline-report.md` §8, and corrected the matching comment in `execution-boundary.test.ts` (comment text only — no assertion logic changed, no production source touched). See §9 and §10 (Technical Debt, item TD-1) for the unresolved underlying issue, which this phase does not fix. - -## 5. Package Certification - -| Package | Documented purpose (`system-architecture.md`) | Verified against source | Match | -| ------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | -| `shared` | Core domain types, no behavior | No `@parmana/*` deps; `src/` is types/interfaces/config only | ✓ | -| `crypto` | Signing/verification/hashing primitives | Ed25519 (`Ed25519SignatureProvider.ts`), Dilithium3 (`Dilithium3SignatureProvider.ts`), hashing (`ExecutableContentHasher.ts`) all present | ✓ | -| `policy` | Policy Engine, `SignalStateVerifier` interface | `PolicyEngine.ts`, `PolicyRouter.ts`, `SignalIntentBinder.ts` all present; zero `@parmana/*` deps (confirmed independent of `runtime`) | ✓ | -| `replay` | Deterministic reconstruction from Trust Record | Depends on `runtime`+`storage` only, as documented | ✓ | -| `receipt` | Signed Receipt generation; type-only `ExecutionPermit` import | `ExecutionReceiptBuilder.ts` imports `ExecutionPermit` from `@parmana/execution-control` and a type from `@parmana/execution-system`; **not** in `package.json` `dependencies` (only `tsconfig.json` project references) — matches the "type-only" label exactly | ✓ | -| `storage` | Persistence, Postgres/Supabase + in-memory | Confirmed via `packages/storage/src` structure; zero `fetch(` calls | ✓ | -| `envelope-verifier` | Signature/expiry/nonce primitives `ExecutionGateway` composes | Confirmed: `ExecutionGateway.ts` calls `envelopeVerifier.verifyChecks`/`consumeNonce` | ✓ | -| `execution-system` | `ExecutionSystem` interface + non-production `DefaultExecutionSystem`/`HttpExecutionSystem` | Confirmed present; production binding goes to `ExecutionGateway`, not these | ✓ | -| `runtime` | `RuntimeEngine`, policy/signal/authorization orchestration; never imports gateway/connector packages | Confirmed §4.4 | ✓ | -| `execution-control` | `ExecutionControlService`, `SecureConnector`, session/credential layer; depended on only by `execution-gateway`+`api` | Confirmed: exactly `api` and `execution-gateway` list it as a real `package.json` dependency (`receipt`'s reference is type-only, consistent with its own documented status) | ✓ | -| `execution-gateway` | Sole production execution owner; internal adapter registration, credential-backed dispatch | Confirmed §4.1–4.2 | ✓ (see §4.9 for the `vendor-payment` caveat, which is a bootstrap/registry-content issue, not a package-boundary issue) | -| `connector-sdk` | Passive SDK: DTOs, `Connector`/`CredentialProvider` interfaces, `MockConnector`, Razorpay DTOs; zero `fetch()` | Confirmed §4.3. Also ships `WorkdayConnector.ts`/`VendorPaymentConnector.ts`/`SapConnector.ts`/`SalesforceConnector.ts`/`OracleConnector.ts` — all thin wrappers returning a `MockConnector` instance, not undocumented but also not mentioned in `system-architecture.md`'s package table | Partial — see TD-4 | -| `connector-hubspot` | Same pattern, HubSpot-scoped | Confirmed: zero `fetch(` calls, no `implements Connector` | ✓ | -| `api` | HTTP application, composition root; routes only call `ExecutionTrustApplication`; bootstrap only constructs | Confirmed: routes (`execute.ts`, `transactions.ts`) call only `application.execute(...)`-shaped methods; bootstrap directory's only `connector.execute(`-pattern call is the named `RazorpaySettlementProcessor.ts` exception, which lives in `webhooks/`, not `bootstrap/` or `routes/` | ✓ | - -## 6. Public API Certification - -- `packages/execution-gateway/src/index.ts` exports exactly: `GatewayVerificationResult`, `ConnectorRequest`, `Connector`, `ExecutionGateway`, `HttpConnector`, `deepFreeze`, `connector-runtime/index.js`'s contents (non-production alternate types, separately documented as dead/unused — see `system-architecture.md` §7), plus the three named factories (`createGatewayConnectorRegistry`, `createGatewayRazorpayConnector`, `createGatewayHubSpotConnector`). Read `dist/index.d.ts` directly and diffed against `src/index.ts`: identical content — the build is not drifting from source. -- `packages/execution-gateway/tests/unit/public-api-boundary.test.ts` asserts 10 named internal symbols (`GatewayConnectorRegistry`, `GatewayCapabilityConnectorPolicy`, `SdkConnectorExecutor`, `CredentialVaultAdapter`, `GatewayRazorpayAdapter`, `GatewayHubSpotAdapter`, `GatewayHttpAdapter`, `buildConnectorEvidence`, `redactSensitiveKeys`, `sanitizeEndpoint`) are absent from the public surface, and the 3 factories + `ExecutionGateway` are present — read directly, confirmed the test imports `../../src/index.js` (TypeScript source, resolved by Vitest), **not the built `dist/index.js`** as `repository-invariants.md` previously stated; corrected that line (§9). -- `execution-control/src/index.ts` re-exports broadly (`SessionCredentialSecureConnector`, `ExecutionControlService`, etc.) — this is correct per its own documented boundary, which restricts _who may depend on the package_ (Invariant 6, confirmed §5), not which classes within it are internal. No accidental exposure found. -- No other package's barrel was found exporting an implementation class inconsistent with its documented public surface. - -**Certified**, with the one correction noted above already applied. - -## 7. Test Certification - -| Category | What it protects | Verified guarantee | Gaps found | -| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `tests/architecture/execution-boundary.test.ts` | Single execution pipeline, adapter ownership, call-site allowlist, import boundaries, public API boundary (generic layer) | Re-derived every claim independently in §4 rather than trusting this file's own scan; all held | Does **not** check whether an allowlisted class is _constructed by production bootstrap_ — only _which file_ may implement `Connector`. This gap is exactly how the `MockConnector`/`vendor-payment` finding (§4.9) went uncaught for two phases. | -| `tests/architecture/documentation-references.test.ts` | Every backtick-quoted file-path citation in 5 named docs resolves to a real file | Confirmed narrow by design (checks path existence only) | Cannot and does not catch semantic/factual errors in prose — exactly the kind this audit found (the `crypto`→`shared` dependency-graph miswording in §9, and the `vendor-payment` claim in §4.9) were both invisible to this test since they don't involve a broken file path. | -| `packages/execution-gateway/tests/unit/public-api-boundary.test.ts` | Execution-gateway internal classes stay off the public barrel | Confirmed (§6) | None found | -| Package-local unit tests (107 files under `tests/unit/`) | Component-level correctness | Not exhaustively re-verified line-by-line (out of this phase's proportionate scope); spot-checked via full suite run (§8) | — | -| Integration tests (29 files) | Cross-component wiring, Supabase-gated paths | Ran in full; Supabase-dependent tests correctly skip without `SUPABASE_*` env vars (16 skipped files / 40 skipped tests, consistent across runs) | — | -| Live vendor tests (`razorpay-live.integration.test.ts`, `hubspot-live.integration.test.ts`) | Real Razorpay/HubSpot sandbox behavior | `describe.skipIf`-gated on real credentials, confirmed by reading the gating condition directly | **Never run in CI** — CI's `env:` block does not set `RAZORPAY_KEY_ID`/`RAZORPAY_KEY_SECRET`/`HUBSPOT_PRIVATE_APP_TOKEN`. This is correct design (no secrets in CI), but it means live-vendor-protocol correctness is a manual, credentialed verification activity, not a continuous one — see §9. | -| Code coverage | Line/branch coverage of the above | `npm run coverage` exists (`vitest run --coverage`, `@vitest/coverage-v8` installed) | **Not invoked by CI** — no coverage threshold is enforced anywhere in `.github/workflows/ci.yml`. See TD-3. | - -**Partially Certified** — the guarantees the test suite actually provides are real and were independently confirmed, but two specific claim types (whether an allowlisted class is live in production; documentation's semantic accuracy) are structurally outside any current test's reach, and code coverage is unmeasured in CI. - -## 8. Documentation Certification - -Cross-checked README, `docs/CLAIMS.md` (referenced by README), all `docs/architecture/*.md` in the certified set, `docs/developer/extending-parmana.md`, and `docs/architecture/documentation-audit.md` against each other and against source. - -- **README ↔ architecture docs:** package table and architecture diagram consistent with `system-architecture.md`'s fuller treatment; no contradictions found. README's package table omits `connector-hubspot` and `execution-system` — an incompleteness (it's an intro-level table), not a contradiction. -- **`docs/CLAIMS.md` ↔ `vendor-payment` finding:** `CLAIMS.md` §3 already discusses `vendor-payment` as a real, production-registered connector used in its own adversarial-testing narrative (G-24). This corroborates §4.9's finding rather than contradicting it — the fact was known in one document and wrong in another. -- **Two factual corrections made this pass** (both already applied, both minor, both narrowly scoped to the specific wrong sentence — no rewriting beyond the error itself): - 1. `system-architecture.md` §8: dependency graph claimed `crypto` "depends on nothing internal"; `crypto` genuinely depends on `shared` (`package.json`, `tsconfig.json` reference, 18 importing source files). Corrected. - 2. `repository-invariants.md`: claimed the public-API-boundary test "imports the built public entry point at runtime"; it imports TypeScript source directly (`src/index.js`, Vitest-resolved), never `dist/`. Corrected. -- **One factual correction to the Phase 1E baseline** (§4.9, §9): `execution-pipeline-report.md`'s false claim about `MockConnector` never being constructed by production bootstrap. Corrected with a full explanation in place, rather than silently deleted. -- **`docs/architecture/EXECUTION-FLOW-AUDIT.md`** (a point-in-time historical audit log, not part of the Phase 1G-canonical doc set) references `policies/vendor-payment/1.0.0/policy.json`; only `policies/vendor-payment/2.0.0/policy.json` exists today. Left uncorrected: this is a dated historical record of a past state, not a living document making current claims, and rewriting historical audit trails is out of proportion for this finding — noted here instead (TD-6). -- **`system-architecture.md` §8's dependency graph** omits `storage` and `replay` from its edge list, and omits `runtime`'s `tsconfig` reference to `storage`. Not incorrect (the graph doesn't claim to be exhaustive), but incomplete — noted (TD-5), not fixed, to avoid scope creep on a document Phase 1G already certified as consistent. - -**Certified**, with three corrections applied (all documentation, none production source) and two incompleteness items carried to the technical debt register rather than fixed in this pass. - -## 9. CI Certification - -Independently ran every step `.github/workflows/ci.yml` runs, against the same commit: - -| CI step | Result | -| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `npm run build` (`openapi` bundle/lint + `tsc -b`) | `npm run lint:openapi` → clean. `npx tsc -b` → clean, 0 errors. | -| `npm run lint` (`eslint . --ext .ts`) | Clean, 0 errors/warnings. | -| `npm run typecheck` (`tsc --noEmit -p tsconfig.json`) | Clean. | -| Terminology guard (retired "execution governance" term) | **Fails.** Reproduced the exact `grep` command from `ci.yml` against the current tree: `./docs/site/changelog.mdx:93` legitimately quotes "Execution Governance" while narrating the repo's own terminology history (structurally identical to `ROADMAP-v1.md`'s already-exempted case), but `changelog.mdx` is not in the guard's `--exclude` list. This is the one file in the entire repository that trips it. | -| `npm test` (`vitest run`) | Clean at the point CI would run it (before this report's self-reference existed — see below). | - -**Finding, not fixed:** the terminology guard step, as currently configured, would fail if triggered against this exact commit tree. This is reported per this phase's own charter — "if a claim cannot be demonstrated, report it as uncertified rather than modifying code to make it true" — rather than silently adding `changelog.mdx` to `ci.yml`'s exclusion list, which would be a CI-config change this read-only certification pass does not make. See TD-2. - -**What CI automates:** build, lint, typecheck, the terminology guard (currently broken — see above), and the full `npm test` run — which, because `vitest.config.ts`'s `include` pattern (`**/tests/**/*.test.ts`) matches `tests/architecture/*.test.ts`, means **both architecture invariant enforcement and documentation-reference validation run on every push and PR**, not just locally. Confirmed by reading `vitest.config.ts` directly, not assumed. - -**What remains manual, outside CI:** - -- Live-vendor-credential integration tests (Razorpay/HubSpot `*-live.integration.test.ts`) — correctly excluded from CI (no secrets in CI), but this means real-vendor-protocol correctness is verified only when a human runs them with real credentials. -- Code coverage — measured by tooling that exists but is never invoked in CI (TD-3). -- Everything in this Phase 1H report itself — package-ownership tracing, dependency-graph cross-checking, public-barrel diffing, and the `vendor-payment` finding — none of which any current automated check performs. This entire class of verification is, by construction, a manual audit activity; Phase 1H exists precisely because it isn't automated. - -**Partially Certified** — build, lint, typecheck, and test execution are all genuinely automated and were confirmed green; the terminology guard is automated but currently broken; coverage and live-vendor correctness are correctly-scoped-out-of-CI manual activities, now stated explicitly rather than left implicit. - -## 10. Technical Debt Register - -| ID | Status | Severity | Description | Location | Rationale | Recommended phase | -| ----- | ------------------------------------------------------------------------------------------------ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| TD-1 | **Closed** (2026-08-05, Phase 2A.4 — see `docs/operations/td1-closure-summary.md`) | **High** | `vendor-payment` connector is permanently backed by `MockConnector`, unconditionally registered (unlike credential-gated Razorpay/HubSpot), fabricating signed `{success:true}` Trust Records/Receipts for `payments:execute` with no real vendor ever contacted. | `packages/api/src/bootstrap/createVendorPaymentConnector.ts`, `createConnectorRegistry.ts` | Self-documented as an intentional interim stand-in ("until the real enterprise connector is introduced"), but no gating or evidentiary marking distinguishes its output from a genuine execution. | Phase 2: implement the real connector, or gate registration behind explicit configuration (mirroring Razorpay/HubSpot's fail-closed pattern), or mark its Trust Records as non-authoritative. | -| TD-2 | **Closed** (2026-08-05, Phase 2C — see `docs/architecture/phase2c-terminology-guard.md`) | **High** | CI's retired-terminology guard fails against the current tree: `docs/site/changelog.mdx:93` legitimately narrates repo terminology history but isn't in the guard's exclusion list. | `.github/workflows/ci.yml` (guard step), `docs/site/changelog.mdx` | Structurally identical, already-solved case exists for `ROADMAP-v1.md`; this file was simply missed. | Immediate/next phase: add `changelog.mdx` to the exclusion list (or move the historical note into an already-exempted file). | -| TD-3 | Open | **Medium** | No CI-enforced code coverage threshold; `npm run coverage` exists but is never invoked by `ci.yml`. | `.github/workflows/ci.yml`, `package.json` | Coverage regressions in any package are currently silent. | Phase 2: add a coverage step/threshold gate. | -| TD-4 | Open | **Low** | `connector-sdk` ships five vendor-named wrapper files (`WorkdayConnector.ts`, `VendorPaymentConnector.ts`, `SapConnector.ts`, `SalesforceConnector.ts`, `OracleConnector.ts`) that all just return a `MockConnector` instance; none are mentioned in `system-architecture.md`'s package responsibility table. | `packages/connector-sdk/src/connectors/*` | Not a boundary violation (still `MockConnector` under the hood), but an undocumented surface. | Next documentation pass: either document them explicitly as example/demo connector stubs, or remove if unused. | -| TD-5 | Open | **Low** | `system-architecture.md` §8's dependency graph omits `storage` and `replay` packages, and omits `runtime`'s reference to `storage`. | `docs/architecture/system-architecture.md` §8 | Incomplete, not incorrect — the graph reads as a summary of pipeline-central packages. | Next documentation pass, if the graph's intended scope is exhaustive. | -| TD-6 | Open | **Low** | `EXECUTION-FLOW-AUDIT.md` references `policies/vendor-payment/1.0.0/policy.json`; only `2.0.0` exists today. | `docs/architecture/EXECUTION-FLOW-AUDIT.md` | Stale path in a point-in-time historical audit record, not a living document. | No action needed unless the file is promoted to living-doc status. | -| TD-7 | Open | **Low** | `packages/api/src/bootstrap/createVendorPaymentSecureConnector.ts` is a committed, 0-byte file, dead since introduction (`ddf4bc5`), referenced by nothing. | `packages/api/src/bootstrap/createVendorPaymentSecureConnector.ts` | Pure cosmetic cruft; no functional impact (nothing imports it). | Any future cleanup pass: delete. | -| TD-8 | Open | **Low** | `receipt`'s `package.json` doesn't declare `@parmana/execution-control`/`@parmana/execution-system` even as `devDependencies`/`peerDependencies`, relying solely on `tsconfig.json` project references for its type-only import. | `packages/receipt/package.json` | Works today only because npm workspaces hoist every package; would silently break if `receipt` were ever extracted/published standalone. | Before any standalone package extraction. | -| TD-9 | Open | **Low** | `ExecutionRequestBuilder.build()` manually re-lists `businessTransactionId`/`action`/`target`/`parameters` instead of calling the shared `toExecutableContent()` helper `RuntimeEngine.ts` already uses for the same four fields. | `packages/runtime/src/ExecutionRequestBuilder.ts` | No drift today (both derive identical values), but nothing enforces they can't diverge — this pair feeds `ExecutionGateway`, a trust-critical boundary. | Next runtime-package touch: one-line change to call `toExecutableContent()`. | -| TD-10 | Open | **Low** | `typescript/` client SDK: 5 of 9 test files remain empty stubs (`HealthApi.test.ts`, `ParmanaClient.test.ts`, `PolicyApi.test.ts`, `ReplayApi.test.ts`, `VerificationApi.test.ts`); 4 were filled in and a new integration test added since first found (2026-07-07), unchanged since Phase 2B (2026-08-05). | `typescript/test/*.test.ts` | Published client SDK, partially covered. | Session-sized: write real coverage for the 5 remaining surfaces. | -| TD-11 | Open | **Low** | `ExecutionEvidenceComponent.execute()` is a no-op (`return context;` unchanged) with a `// TODO: Build ExecutionEvidence...` comment; confirmed unreferenced anywhere outside its own file. | `packages/runtime/src/components/ExecutionEvidenceComponent.ts` | Dead pipeline stage, not wired into any live `RuntimePipeline` composition. | Any future cleanup pass: finish it or delete it. | -| TD-12 | Open | **Low** | `OverrideService`/`OverrideVerifier` are unreachable from any route — zero references from `packages/api/src/**`. | `packages/runtime/src/policy/OverrideVerifier.ts`, `packages/runtime/src/services/override-service.ts` | Guarded/intentionally parked capability, not a boundary violation. | No action needed unless this capability is scheduled for exposure. | -| TD-13 | Open | **Medium** | `POST /replay` exists and returns 200, but `ExecutionTrustApplication.replay()` does not invoke `@parmana/replay`'s `ReplayExecutor`/`ReplayPipeline` at all — it re-fetches the Trust Record and re-verifies its hash/signature, functionally duplicating `/verify`. `@parmana/replay`'s own "deterministic reconstruction" machinery remains entirely unreachable from any route. | `packages/runtime/src/ExecutionTrustApplication.ts:131-160` (the route handler), `packages/replay/src/*` (the unreachable package) | Refined this phase from the prior, vaguer framing ("Replay unreachable from /replay") — the route is not missing, it does something materially different from what the package/route name and `docs/CLAIMS.md` 2.7 imply. Not a security or trust-fabrication issue (nothing is misrepresented as more-verified than it is), but a claims-precision gap. | Next phase touching `/replay`: either wire in the real package, or correct the route's documented behavior/name to match what it actually does. | -| TD-14 | Open | **Low** | `LedgerSerializer`'s `JSON.stringify(entry, Object.keys(entry).sort())` uses a flat key allowlist applied at every recursion level, so any nested object whose keys don't also appear at the top level serializes as `{}`. | `packages/storage/src/ledger/LedgerSerializer.ts:9` | Confirmed dead code (same orphaned storage/ledger subsystem as TD-12/TD-13's territory) — the bug is real but nothing live exercises it. | No action needed unless this subsystem is revived. | -| TD-15 | **Closed** (2026-08-05, Phase 2D — see `docs/architecture/phase2d-execution-failure-testing.md`) | Low (was: untested-but-parked) | `execution-failure.integration.test.ts` was permanently `describe.skip`'d with a stated blocker ("RuntimeFactory always creates a DefaultExecutionSystem internally") that Phase 2B found already resolved, and that Phase 2D independently confirmed — plus found and fixed a second, deeper problem the DI fix alone didn't touch: the test never used the DI seam, and its assertions described a response shape (`response.body.execution.status`) that has never matched this API's actual, intentional no-leak error contract. | `packages/api/tests/integration/execution-failure.integration.test.ts` | Restored using `createInspectableExecutionSystem` (an existing helper), with assertions verified empirically against real current behavior, plus new coverage (Trust Record/Receipt/Refusal-Record absence, exactly-once connector invocation, determinism) beyond the original scope. | — | -| TD-16 | Open | **Medium** | `ReceiptCrypto` and `SettlementConfirmationCrypto` each directly instantiate their own `FileKeyProvider()`, bypassing the "crypto composition root" (`CryptoBootstrap`) the rest of the system uses; both self-documented as temporary. Live in production (`ReceiptService` constructs `ReceiptCrypto` directly). | `packages/crypto/src/ReceiptCrypto.ts`, `packages/crypto/src/SettlementConfirmationCrypto.ts` | Not exploitable today (`FileKeyProvider`/`KEY_PROVIDER=local` is the only implemented key provider anywhere in the repo) — a latent gap that would activate silently the moment a KMS/HSM provider (roadmap Move 1) is implemented and everything else migrates but these two classes don't. | Sequence with roadmap Move 1 (KMS/HSM custody), not before — premature to build the seam for a provider that doesn't exist yet. | -| TD-17 | Open | **Low** | `createGatewayIdentity.ts`/`createSessionStore.ts` carry `TODO: Replace ... with your production gateway identity / production authentication mechanism` comments that read as unresolved security gaps but, traced fully, are not: the identity string is a non-secret label (real auth is `SignedTokenConnectorAuthenticator`'s cryptographic signature check), and the session-issuance "authentication" value is checked by in-process object-reference equality, not content, so its value is architecturally inert. | `packages/api/src/bootstrap/createGatewayIdentity.ts`, `createSessionStore.ts` | Misleading comments, not a real security gap — independently traced end-to-end (Phase 2B). | Next bootstrap touch: reword the comments to stop reading as open security work. | -| TD-18 | Open | **Low** | `GET /transactions` filters caller-scoped results after pagination is applied, not by pushing the scope into the storage query — a page can under-fill (never over-disclose) when other callers' rows occupy its window. Self-documented as an accepted trade-off. | `packages/api/src/routes/transactions.ts:58-64` | Explicitly not a security gap by the route's own comment; a pagination-correctness nuisance only. | Low priority; revisit if/when caller-scoped pagination correctness becomes a real user complaint. | -| TD-19 | Open | **Low** | 9 debug `console.log` statements sit in the core request/execution hot path, unconditional (no log-level or `NODE_ENV` gate), firing on every request: `"[ROUTE] before/after execute"` (2, `execute.ts`) and numbered `"[APP] 1"`–`"[APP] 7"` stage markers spanning the entire `ExecutionTrustApplication.execute()` method. Introduced 2026-07-18 (`a6b9d8b`, "Execution Integrity Platform v0.3"); never previously flagged by any prior audit (Phase 1H's certification, `GAP-AUDIT.md`, or Phase 2B's assessment). | `packages/api/src/routes/execute.ts:108,115`, `packages/runtime/src/ExecutionTrustApplication.ts:68,73,78,83,88,94,102` | Debug instrumentation left behind after a development session; no functional or security impact, but noise in every production log line for the system's most central method. | Next touch to either file: delete all 9 (or replace with a real, level-gated logger if execution tracing is actually wanted). | -| TD-20 | Open | **Low** | A second, entirely unused "composition root" for Execution Control exists alongside the real one: `ExecutionControlComposition.ts` and `ConnectorFactory.ts` (100 lines combined) have zero references from any other file in the repository. Matches `docs/investigations/GAP-AUDIT.md`'s G-13 finding (2026-07-07) precisely, still unaddressed a month later and never previously added to this register. | `packages/api/src/bootstrap/ExecutionControlComposition.ts`, `packages/api/src/bootstrap/ConnectorFactory.ts` | Confusion risk for a new contributor or auditor who reads the wrong file as source of truth (the real chain is `createExecutionControl.ts`/`createConnectorRegistry.ts`) — zero functional risk since nothing constructs or imports either dead class. | Any future cleanup pass: delete both (already confirmed zero references; low-risk removal). | -| TD-21 | Open | **Low** | `docs/CLAIMS.md:835` still describes `create-connector-registry.test.ts` as asserting "vendor-payment remains resolvable when razorpay is not" — true before Phase 2A, false since: that exact test was changed to assert the opposite (fails closed) outside `NODE_ENV=test`. Flagged during Phase 2A/2A.1 but never corrected. | `docs/CLAIMS.md:835` | Stale claim about current test behavior in an actively-referenced claims document (not a dated historical snapshot like `EXECUTION-FLOW-AUDIT.md`/TD-6). | Next `CLAIMS.md` touch: correct the one clause; out of scope for a dedicated phase on its own. | - -No item above is classified **Critical**, and none of TD-3 through TD-21 constitutes an active bypass of the tested architectural boundaries (single pipeline, adapter ownership, call-site allowlist, import boundaries all held under falsification attempts, re-confirmed as recently as Phase 2E). TD-1 and TD-2 were **High** because they represented, respectively, a live production-trust gap and a currently-broken CI gate — both real and actionable, neither an architectural-boundary breach; both are now closed. TD-13 and TD-16 are the only open items rated **Medium** (§ their rows above for why); everything else open is **Low**. - -**TD-1 closure note (2026-08-05, added Phase 2A.4 — this is an addendum, not a revision of the finding above, which remains the accurate record of what this certification pass found on its original date):** TD-1 is closed. The finding, severity, description, location, and rationale above are preserved exactly as originally certified — they were accurate when written and remain the historical record of the issue. Closure rests on a four-phase evidence chain, each independently verified: Phase 2A implemented the fix (commit `6eff8ec`); Phase 2A.1 conducted an independent operational readiness review before deployment; Phase 2A.2 deployed the exact reviewed commit to production and confirmed the fix active via direct production log evidence; Phase 2A.3 resolved the one remaining uncertainty (historical database identity) via self-triggered, timestamp-correlated production evidence and found zero non-test-attributed historical records. Full evidence chain, commit SHAs, and dates: `docs/operations/td1-closure-summary.md`. - -**TD-2 closure note (2026-08-05, added Phase 2C):** TD-2 is closed. Root cause (the exclusion list's inherent tendency to fall behind every new document that legitimately narrates the retired term) diagnosed and the list completed, re-organized by category, with guidance for future maintainers to extend it correctly rather than needing another dedicated repair phase. Full evidence: `docs/architecture/phase2c-terminology-guard.md`. - -**TD-15 addition and closure note (2026-08-05, added Phase 2E, closed Phase 2D):** TD-15 was never added to this table before Phase 2E — it existed only in `docs/architecture/phase2b-technical-debt-assessment.md`'s own, separate register. Added here retroactively, already closed, for a single canonical source of truth. Full evidence: `docs/architecture/phase2d-execution-failure-testing.md`. - -**TD-9 through TD-14 and TD-16 through TD-18 provenance note (added Phase 2E):** first identified in `docs/architecture/phase2b-technical-debt-assessment.md` (2026-08-05) and never previously merged into this canonical register. Each was independently re-verified against current source during Phase 2E, not copied — TD-13 in particular was refined (the `/replay` route exists and returns 200, it does not silently 404; the gap is that it doesn't invoke `@parmana/replay`'s actual machinery). TD-19, TD-20, and TD-21 are new findings from Phase 2E itself. Full evidence and risk scoring: `docs/architecture/phase2e-engineering-debt-assessment.md`. - -Phase 1A–1G's completed work (the pipeline consolidation, connector passivation, invariant locking, and documentation alignment itself) is not listed here, per this phase's instruction not to re-classify completed work as debt. - -## 11. Evidence Summary - -Every certified claim above is backed by one of: - -- Direct source reads with line numbers cited (RuntimeEngine.ts, ExecutionGateway.ts, ExecutionControlService.ts, etc.) -- Independent `grep`/repo-wide scans re-run rather than trusted from existing tests (`implements Connector`, `connector.execute(`/`adapter.execute(`, `fetch(`, import lists) -- `git log` for chronological ordering of the `vendor-payment`/`MockConnector` finding -- Diffed `package.json` `dependencies` against `tsconfig.json` `references` for every package, for the full §3 dependency table -- Independently re-run commands, this session, against commit `e8e2265`: `npx tsc -b`, `npm run typecheck`, `npm run lint`, `npm run lint:openapi`, `npm test -- --maxWorkers=2`, and the exact terminology-guard `grep` from `ci.yml` -- Direct comparison of `dist/index.d.ts` against `src/index.ts` for the execution-gateway public barrel - -## 12. Certification Limitations - -- **Not a fresh-session audit.** This pass ran in the same session as Phase 1G. Mitigated by re-deriving every claim from source rather than recollection (see disclosure at the top of this report), but a genuinely independent reviewer re-running §11's commands is the strongest available substitute for a literal fresh session. -- **Proportionate, not exhaustive, unit-test review.** 107 unit test files were inventoried and run, not read line-by-line; their pass/fail status was verified, their individual assertion quality was not audited file-by-file. -- **No live-vendor verification performed.** Razorpay/HubSpot live integration tests were not run with real credentials during this pass — their gating condition was verified by reading the code, not by execution. -- **`docs/CLAIMS.md` and `docs/site/*` were consulted only where they bore directly on a specific finding** (the `vendor-payment` corroboration, the terminology-guard file), not audited as complete documents in their own right — they are outside Task 6's named document set. -- **No performance, security-penetration, or dependency-vulnerability audit was performed.** This certification is architectural and documentation-focused, per its stated scope. - ---- - -## Final Verification - -| Item | Classification | -| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Repository architecture | **Certified** | -| Single execution pipeline | **Certified** (§4.1) | -| Execution ownership | **Certified**, with a documented production-integrity caveat that does not implicate the architectural boundary itself (§4.2, §4.9, TD-1) — **caveat closed 2026-08-05, Phase 2A.4; see `docs/operations/td1-closure-summary.md`** | -| Authorization precedes execution | **Certified** (§4.5) | -| Signal verification precedes authorization | **Certified** (§4.6) | -| Replay protection precedes execution | **Certified** (§4.7) | -| Audit generation | **Certified** (§4.8) | -| Public API boundaries | **Certified** (§6) | -| Repository invariants (Invariants 1–7, `repository-invariants.md`) | **Certified** — every invariant's underlying claim was independently re-derived from source, not just from its own enforcing test | -| Documentation consistency | **Certified**, with 3 factual corrections applied during this pass (§8) | -| Build | **Certified** (`tsc -b`, `npm run typecheck`, `npm run lint`, `npm run lint:openapi` all clean against `e8e2265`) | -| Test suite | **Partially Certified** — the suite is genuinely comprehensive and green, but two guarantee classes (bootstrap-construction of allowlisted classes; documentation semantic accuracy) are outside any current test's reach, evidenced concretely by the `vendor-payment` finding (§4.9, §7) | -| CI | **Partially Certified** — build/lint/typecheck/test steps are automated and confirmed green; the terminology guard is automated but currently fails against this tree (§9, TD-2); coverage and live-vendor checks are correctly manual, not gaps in what CI claims to do | -| No production behavior changed during certification | **Confirmed** — every edit made this pass was to `docs/architecture/*.md` or to comments inside `tests/architecture/execution-boundary.test.ts`; zero files under any `packages/*/src` were modified; `git diff --stat` against `packages/*/src` is empty | - -**Overall:** the architecture established through Phases 1A–1G is real, independently verifiable, and holds under active falsification attempts on every tested boundary. It is a suitable baseline for Phase 2, conditioned on TD-1 and TD-2 being addressed early — neither is an architectural defect, but both are live, evidenced gaps between what the repository claims and what it currently does. - ---- - -## Addendum (2026-08-05, Phase 2A.4) - -This certification report is preserved exactly as originally published — nothing above this addendum has been rewritten, and its findings remain an accurate, point-in-time record of the repository's state as of Phase 1H. Repository status has changed since that publication date: **TD-1 has since been closed**, through a four-phase program (Phase 2A implementation, Phase 2A.1 operational readiness review, Phase 2A.2 production deployment verification, Phase 2A.3 historical integrity verification) independently verified at each stage. The complete evidence chain, commit SHAs, and dates are recorded canonically in `docs/operations/td1-closure-summary.md`; this addendum exists only to point there, not to restate that evidence. TD-2 through TD-8 are unchanged and remain open, exactly as this report originally classified them. diff --git a/docs/architecture/repository-invariants.md b/docs/architecture/repository-invariants.md index f726f7d9..90a75030 100644 --- a/docs/architecture/repository-invariants.md +++ b/docs/architecture/repository-invariants.md @@ -1,6 +1,6 @@ # Repository Invariants — Phase 1F Canonical Reference -This document is the canonical reference for the architectural invariants established by Phases 1A–1E (passive connector SDKs, execution ownership inside `execution-gateway`, internalized implementation, a single production execution pipeline) and locked permanently in Phase 1F. It supersedes informal descriptions scattered across prior phase reports — those reports remain useful history (see `docs/architecture/execution-pipeline-report.md`), but this document is what to trust for "is X enforced, and how." +This document is the canonical reference for the architectural invariants established by Phases 1A to 1E (passive connector SDKs, execution ownership inside `execution-gateway`, internalized implementation, a single production execution pipeline) and locked permanently in Phase 1F. It supersedes informal descriptions scattered across prior phase reports (those reports remain in git history), but this document is what to trust for "is X enforced, and how." Every invariant below is backed by an automated test that fails the build on violation, not by convention or code review discipline. diff --git a/docs/architecture/strategic-positioning-validation.md b/docs/architecture/strategic-positioning-validation.md deleted file mode 100644 index 90c9ffb3..00000000 --- a/docs/architecture/strategic-positioning-validation.md +++ /dev/null @@ -1,242 +0,0 @@ -# Strategic Positioning — Source Code Validation (Living Document) - -Certifies, from current repository state, whether the locked strategic positioning **"We -Are Not in the AI Race"** is supported by the implementation. This document is distinct -from `docs/architecture/phase3d-independent-authorization-certification.md`: Phase 3D -certified one specific marketing claim ("AI cannot execute anything the business hasn't -authorized") scoped to the two production-reachable capabilities. This document certifies -the broader positioning — that the authorization mechanism itself protects institutional -authority against execution risk from _any_ source, not only AI, and does not depend on any -particular AI technology. - -**This is a living document.** Unlike a one-time audit, its verdict is a snapshot that can -be invalidated by ordinary code changes — a new connector, a new caller-identity concept, a -change to `RuntimeEngine`'s pipeline, or the reintroduction of an unverified-signal -capability would each require re-checking sections below, not just appending to them. See -"When to Re-Validate" at the end. - -**Status as of this revision: SUPPORTED BY IMPLEMENTATION — YES, directly validated.** -Upgraded from three prior PARTIALLY verdicts (preserved below as history, not erased — see -"Verdict History"). The structural change that earned this: `payments:execute` -(vendor-payment), the one capability whose authorization-relevant facts were pure -caller-declared attestations with no independent verifier, was **removed from the -repository entirely**, not merely gated more tightly. A fourth, independent validation pass -— run fresh after that removal, with no reliance on the first three passes' conclusions — -confirmed no connector resolves for it in any environment. See §3.1 and §6. - -**Honesty constraint, carried forward precisely, not rounded away:** the fourth pass did -not re-verify everything from scratch. It re-ran 2 of the 10 negative tests cited across -this document's history fresh (`authority-type-agnostic-execution.integration.test.ts`, -`create-connector-registry.test.ts`'s new cross-environment absence case); the other 8 were -cited from code paths confirmed unchanged by the removal, not individually re-executed in -that pass. Multi-tenant isolation (§3.3) and the direct-database-write bypass finding were -explicitly left as "unchanged, not re-traced" rather than re-asserted clean. **"YES" means -the specific invariant this positioning states — only institutionally authorized actions -become execution — is now supported without a known exception, not that every property in -this document was independently re-proven in the same session.** - -### Verdict History - -| Pass | Verdict | What changed before the next pass | Where recorded | -| ----------------------------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | -| 1 (initial) | PARTIALLY SUPPORTED | — | This document, original revision | -| 2 (after AI-agnosticism test + DB-write-bypass trace) | PARTIALLY SUPPORTED, narrower | `authority-type-agnostic-execution.integration.test.ts` added; DB-write-bypass ruled out | This document, §2 upgrades noted inline | -| 3 (fresh re-verification, rule-20 discipline) | PARTIALLY SUPPORTED, confirmed unchanged | No repository change; independent re-confirmation only | Delivered in-conversation | -| **4 (after vendor-payment removal)** | **SUPPORTED BY IMPLEMENTATION — YES** | `payments:execute` removed entirely (`docs/VERIFICATION-GAPS.md` G-27); replacement test-only connector independently scrutinized and found to introduce no new bypass | Delivered in-conversation; this revision | - -Passes 1–3 are not deleted or rewritten — they were correct assessments of the repository -state at the time each ran. The verdict changed because the repository changed, not because -the earlier passes were wrong. - ---- - -## 1. Locked Positioning Under Validation - -> Parmana will not compete in the AI race. Parmana is technology agnostic. Its purpose is -> to protect institutional authority over what is allowed to become real-world execution. -> AI agents are one source of execution risk, but they are not the only source... **Only -> what you authorize should become real.** ... **AI may decide. You decide what becomes -> real.** - -Validated against commit history through `authority-type-agnostic-execution.integration.test.ts` -and the `docs/CLAIMS.md` §2.24 / §2.22 / §2.23 entries this document's findings are recorded -in. - ---- - -## 2. What Is Now Directly Validated - -| Property | Status | Evidence anchor | -| -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| No AI-vendor dependency anywhere in the monorepo | DIRECTLY VALIDATED | Zero hits for openai/anthropic/langchain/gemini/etc. across every `package.json` | -| Authorization mechanism has no concept of "AI" | DIRECTLY VALIDATED | `RuntimeEngine`, `PolicyEngine`, `SignalIntentBinder`, `CapabilityPolicyBinder` — zero references to caller identity, model, or vendor anywhere in source | -| Authorization outcome is caller-type-agnostic, not merely caller-type-blind by omission | **DIRECTLY VALIDATED (upgraded from INFERRED)** | `docs/CLAIMS.md` §2.24; `packages/api/tests/integration/authority-type-agnostic-execution.integration.test.ts` — an arbitrary, non-enum `authorityType` string produces byte-identical APPROVE and REJECT decisions to a conventional one, through the real `POST /execute` route. Re-run fresh in pass 4 (2/2 passing) after the vendor-payment removal, confirming no regression. | -| Institutional authority (policy) controls execution, not the caller | DIRECTLY VALIDATED | `FilePolicyRepository` reads from a server-controlled directory; no HTTP route writes policy content; `CapabilityPolicyBinder` fixes capability→policy binding structurally (`docs/CLAIMS.md` §2.22, TD-22) | -| Authorization enforced strictly before execution, for the two live capabilities | DIRECTLY VALIDATED | `ExecutionGate.enforce()` gates every path to a connector; `ExecutionComponent` throws if decision/authorization missing | -| Unauthorized execution fails closed, not merely reported | DIRECTLY VALIDATED | 13 negative tests, cited in full in the Phase 3D certification §6 and the Strategic Positioning audit §6 | -| No route, SDK method, or repository write path bypasses `RuntimeEngine` | **DIRECTLY VALIDATED (upgraded from NOT VALIDATED)** | Every write method on `ExecutionTrustRecordRepository`/`BusinessTransactionRepository` traced to exactly one caller, always inside `packages/runtime/src/services/*` or `ExecutionTrustApplication`; see `docs/VERIFICATION-GAPS.md`, "Gaps checked and found not applicable". **Not re-traced in pass 4** — carried forward as still valid since the removal did not touch storage-layer callers, not re-verified fresh. | -| Every production capability's authorization-relevant facts are independently verified before authorization is signed — **repository-wide, no exception** | **DIRECTLY VALIDATED (upgraded from PARTIALLY, pass 4)** | `packages/api/src/bootstrap/createConnectorRegistry.ts` registers exactly three connectors: `test-fixture` (`NODE_ENV=test`-only, unbound from `CapabilityPolicyBinding.ts`, no production implication), `razorpay`, `hubspot`. `payments:execute` has no connector to resolve to in any environment — `createVendorPaymentConnector.ts` and its dedicated credential provider no longer exist (`docs/VERIFICATION-GAPS.md` G-27). | `packages/api/tests/unit/bootstrap/create-connector-registry.test.ts`, "payments:execute has no connector to resolve to in any environment — vendor-payment was removed, not merely gated" — asserts this across `test`/`production`/`development` `NODE_ENV` values; re-run fresh in pass 4, 4/4 passing | -| `PARMANA_AUTH_DISABLED` does not disable action-level authorization | DIRECTLY VALIDATED, precisely scoped | `docs/VERIFICATION-GAPS.md` G-28; `docs/CLAIMS.md` §2.16 addendum — the flag removes caller identity/accountability only; `RuntimeEngine`'s gates have no dependency on it | -| Execution is bound to the exact authorized decision (no substitution after the fact) | DIRECTLY VALIDATED | `businessTransactionHash` recomputed and compared in `ExecutionGateway.verify()`; `SignalIntentBinder` proves signals describe the same action as `Intent` | -| Replay is prevented by database-enforced atomicity, not application logic | DIRECTLY VALIDATED | `SupabaseNonceStore`/`SupabaseApprovalNonceStore`/`SupabaseRazorpayDailyRefundLedger` — single `INSERT`/`UPSERT` gated by a primary key, proven under live concurrent Postgres load | -| Execution evidence is independently, cryptographically verifiable by a third party | DIRECTLY VALIDATED | `POST /audit/verify`, `POST /refusal/verify` — unauthenticated, need only Parmana's public key | - ---- - -## 3. What Remains Honestly Unresolved - -These are not failures of documentation — they are genuine scope boundaries the mechanism -itself has not yet earned the right to claim past. - -### 3.1 `payments:execute` (vendor-payment) — RESOLVED by removal (pass 4) - -**Historical record (true through pass 3):** real, committed code -(`policies/vendor-payment/2.0.0/policy.json`, `VendorPaymentConnector`) whose five -authorization-relevant signals (`vendorVerified`, `invoiceVerified`, `paymentApproved`, -`sufficientFunds`, `riskScore`) were pure caller-declared attestations with **no -independent verifier anywhere in this codebase**. Excluded from production only by -`createVendorPaymentConnector.ts:30-32`'s `NODE_ENV === "test"` gate — an environment -variable, not a property of the authorization mechanism. If enabled as it then existed, it -would have violated "only what you authorize should become real": a caller could declare -`paymentApproved: true, sufficientFunds: true` etc. with nothing real behind those claims, -and receive a signed, APPROVED execution. This was, through pass 3, the sole reason the -verdict read PARTIALLY rather than FULLY supported. - -**Current state (pass 4):** the capability was removed outright, not gated more tightly. -`createVendorPaymentConnector.ts` and its dedicated credential provider -(`createCredentialProvider.ts`) no longer exist. `createConnectorRegistry.ts` registers -exactly three connectors in production wiring: `test-fixture` (a generic, -`NODE_ENV=test`-only connector introduced solely so shared test infrastructure has -something to execute against, capability `test:fixture-execute`, unbound in -`CANONICAL_CAPABILITY_POLICY_BINDINGS`, no production implication), `razorpay`, `hubspot`. -`payments:execute` has no connector to resolve to in any environment — confirmed by -`create-connector-registry.test.ts`'s "payments:execute has no connector to resolve to in -any environment" case, which asserts this across `test`, `production`, and `development` -`NODE_ENV` values, re-run fresh in pass 4 (4/4 passing). - -**Decision, stated plainly:** this was not independently verified — it was never on the -product roadmap as a real capability, so building the `SignalStateVerifier` work that would -have closed it (the same closure Razorpay and HubSpot already received) was rejected in -favor of removal. **Full detail, including what was deliberately retained** (the policy -file and shared test fixtures, which carry no execution risk with no connector to pair -with): `docs/VERIFICATION-GAPS.md` G-27. - -**This is the reason §"Status" above now reads YES, not PARTIALLY.** - -### 3.2 Caller-type-agnosticism, mechanism vs. exercise - -§2's "DIRECTLY VALIDATED" entries prove the _mechanism_ contains no code path that could -distinguish an AI agent from a human, script, or third-party system. They do **not** prove -that a human, or a non-AI automated system, has actually submitted a production transaction -— no such exercise exists in this repository's test or deployment history as reviewed. The -distinction matters: this document claims "the mechanism cannot special-case caller kind," -not "every caller kind has been observed in the wild." - -### 3.3 Multi-tenant / cross-institution authority isolation - -Explicitly out of scope, not attempted, and not characterized as a gap — the positioning -speaks of "institutional authority" in the singular. This repository's `Authority` model -(`USER | ROLE | SERVICE | ORGANIZATION`) supports one institution's principal/role/service/ -organization hierarchy; no evidence was gathered bearing on isolation between multiple -institutions sharing one deployment, because nothing in the current architecture claims to -support that scenario either way. - ---- - -## 4. Contradictions - -No source-code behavior currently reachable in production contradicts the positioning. -**No conditional contradiction remains as of pass 4.** Through pass 3, this section named -one: `payments:execute` (§3.1), if ever enabled without first closing its -signal-verification gap, would have directly contradicted "only what you authorize should -become real." That capability no longer exists in this repository, so the contradiction it -would have produced cannot occur. Independently re-scrutinized in pass 4: the replacement -test-only connector (`test:fixture-execute`) does not reintroduce it — it is fail-closed by -the identical `NODE_ENV === "test"` mechanism every other connector uses, and unbound from -`CapabilityPolicyBinding.ts`'s governance entirely, so its own unverified signals (inherited -unchanged from the retained `vendor-payment/2.0.0` policy content) carry no production -implication. - ---- - -## 5. Evidence Trail (Where Each Finding Lives) - -| Finding | Document | Section | -| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -| Full claim-by-claim matrix, execution control path, enforcement points, bypass search, negative tests | This document's originating audit (delivered in-conversation; not separately filed, per that audit's own read-only rules) | — | -| Canonical capability-to-policy binding (TD-22) | `docs/CLAIMS.md` | §2.22 | -| Phase 3D authorization certification (narrower marketing claim, two live capabilities) | `docs/architecture/phase3d-independent-authorization-certification.md` | Full document | -| Certification-level positioning summary and carried-forward limitations | `docs/CLAIMS.md` | §2.23 | -| Caller-type-agnosticism, directly validated | `docs/CLAIMS.md` | §2.24 | -| `PARMANA_AUTH_DISABLED` precise scope | `docs/VERIFICATION-GAPS.md` | G-28; also `docs/CLAIMS.md` §2.16 addendum | -| vendor-payment removal (RESOLVED, was "gap-in-waiting" through pass 3) | `docs/VERIFICATION-GAPS.md` | G-27 (cross-references the original 2026-08-04 investigation inside the G-24 block, and its own "RESOLVED by removal" update) | -| `payments:execute` absence, repository-wide, independently re-confirmed (pass 4) | `packages/api/tests/unit/bootstrap/create-connector-registry.test.ts` | "payments:execute has no connector to resolve to in any environment" test | -| Direct-database-write bypass, ruled out | `docs/VERIFICATION-GAPS.md` | "Gaps checked and found not applicable" | -| Historical G-24 signal/intent decoupling bypass (found and fixed, independently re-confirmed by Phase 3D) | `docs/VERIFICATION-GAPS.md` | G-24, with its Phase 3D re-confirmation paragraph | - ---- - -## 6. Final Answer - -> **Based solely on the current repository implementation, does Parmana enforce the -> boundary that only institutionally authorized actions can become real-world execution, -> regardless of what kind of system requests them?** - -**YES — directly validated (pass 4, superseding the PARTIALLY verdicts of passes 1–3).** - -The _mechanism_ was already fully validated, with no remaining inferred or unvalidated -claims, for every capability the production system actually exposed as of pass 3 — proven, -not merely argued, to be blind to caller kind. The single remaining reason the verdict -stayed at PARTIALLY through pass 3 was `payments:execute`: dormant, disclosed, and honestly -characterized as a gap-in-waiting rather than swept into the "supported" column. Pass 4, -run fresh after that capability's outright removal, independently re-confirmed the -mechanism's other properties were unaffected and specifically re-verified — not assumed — -that no connector resolves for `payments:execute` in any environment, and that the -replacement test-only connector introduces no new bypass. Every other candidate concern -raised across this validation's history (`PARMANA_AUTH_DISABLED`, the second -`/transactions` entry point, in-process library use) was found to be a non-issue on direct -inspection or precisely scoped to what it actually affects, not left ambiguous. - -**What YES does not claim, stated precisely so the upgrade cannot be misread as broader -than it is:** pass 4 re-executed only 2 of the 10 negative tests this document's history -cites (`authority-type-agnostic-execution.integration.test.ts`, -`create-connector-registry.test.ts`'s new absence case); the other 8 are carried forward -from code paths confirmed structurally unchanged by the removal, not individually re-run in -that pass. The direct-database-write bypass finding (§2) and multi-tenant isolation (§3.3) -were explicitly left as "unchanged, not re-traced" in pass 4, not re-asserted clean. YES -means the specific invariant this positioning states — an action cannot become real-world -execution unless it satisfies the institution's authorization requirements — is now -supported without a known exception, for every capability this repository currently -exposes. It does not mean every property adjacent to that invariant was re-proven from -scratch in the same session. - ---- - -## 7. When to Re-Validate - -This document's verdict is only as current as the commit it was checked against. Re-check -(at minimum) the affected section, not the whole document, when any of the following occur: - -- **A new connector/capability is registered in production**, or `payments:execute` / - an equivalent unverified-signal capability is ever reintroduced (`createConnectorRegistry.ts` - gains a new entry) — re-check §2's authorization-enforcement rows for that specific - capability, and re-open §3.1/§6's YES verdict specifically if the new or reintroduced - capability lacks independent signal verification, the same way `payments:execute` once - did. -- **`RuntimeEngine`, `PolicyEngine`, `SignalIntentBinder`, or `CapabilityPolicyBinder` - changes** — re-run the caller-identity grep (§2's "no concept of AI" row) and re-run - `authority-type-agnostic-execution.integration.test.ts` to confirm it still passes. -- **A new authentication mechanism is added** (OAuth, mTLS, a second `CallerAuthenticator` - implementation) — re-check whether it introduces any caller-kind-specific authorization - branching, which would falsify §2's caller-agnosticism row. -- **`PARMANA_AUTH_DISABLED`'s wiring changes**, or any code begins reading caller identity - inside `RuntimeEngine` — re-check G-28's precise-scope claim; it would no longer hold. -- **A new repository write method is added** to `ExecutionTrustRecordRepository`, - `BusinessTransactionRepository`, or an equivalent store — re-check its caller(s) before - assuming the "no direct-write bypass" finding still holds. -- **Multi-tenancy is designed or implemented** — this document currently says nothing about - it either way; that silence stops being accurate the moment a real design exists to check - against. - -No section of this document should be treated as permanently true. Each row in §2 names the -exact file(s)/test that would need to be re-read to confirm it still holds. diff --git a/docs/architecture/system-architecture.md b/docs/architecture/system-architecture.md index 1a660509..14f481da 100644 --- a/docs/architecture/system-architecture.md +++ b/docs/architecture/system-architecture.md @@ -2,12 +2,11 @@ Canonical, implementation-derived overview of Parmana's architecture: what each package is responsible for, how a business action flows from HTTP request to audited receipt, and where the repository's architectural boundaries are enforced. -This document describes the system **as implemented**, verified against source as of Phase 1G. It does not describe intended or historical architecture. Where earlier design documents disagree with this one, this one — and the two documents it's built on — win: +This document describes the system **as implemented**, verified against source as of Phase 1G. It does not describe intended or historical architecture. Where earlier design documents disagree with this one, this one and the document it is built on win: -- [`execution-pipeline-report.md`](./execution-pipeline-report.md) — the Phase 1E execution-graph baseline (call graph, ownership map, static search results). - [`repository-invariants.md`](./repository-invariants.md) — the Phase 1F canonical list of automatically-enforced architectural invariants, with the exact test that enforces each one. -This document doesn't repeat their content; it's the entry point that ties them together with package responsibilities and the surrounding subsystems (authorization, signal verification, replay, audit) they don't individually cover end to end. For a class-by-class narrated trace of one request, see [`execution-walkthrough.md`](./execution-walkthrough.md). For how to extend the system safely, see [`../developer/extending-parmana.md`](../developer/extending-parmana.md). +This document doesn't repeat its content; it's the entry point that ties it together with package responsibilities and the surrounding subsystems (authorization, signal verification, replay, audit) it doesn't cover end to end. For a class-by-class narrated trace of one request, see [The Execution Pipeline](https://docs.parmanasystems.com/architecture/execution-pipeline) on the documentation site. For how to extend the system safely, see [`../developer/extending-parmana.md`](../developer/extending-parmana.md). --- @@ -63,7 +62,7 @@ HTTP request → ReceiptService.generate() ``` -Full narrated walkthrough with exact method-call ordering (including where `hookRunner` callbacks fire): [`execution-walkthrough.md`](./execution-walkthrough.md). +A class by class trace of one request, with every point where it can stop: [The Execution Pipeline](https://docs.parmanasystems.com/architecture/execution-pipeline) on the documentation site. **Production binding:** `ExecutionSystem` is bound to `ExecutionGateway` exactly once, in `packages/api/src/bootstrap/createExecutionSystem.ts` — its own doc comment calls this "the single architectural entry point for execution-system composition." No other `ExecutionSystem` implementation is constructed in production bootstrap. diff --git a/docs/architecture/unavoidable-execution.md b/docs/architecture/unavoidable-execution.md deleted file mode 100644 index e07c1904..00000000 --- a/docs/architecture/unavoidable-execution.md +++ /dev/null @@ -1,447 +0,0 @@ -# Parmana-Controlled Execution Architecture - -## 1. Executive Summary - -AI systems no longer only produce text. They can call APIs, move money, change cloud -infrastructure, update business records, operate databases, and control physical -systems. This changes the security question. It is no longer enough to ask whether an -AI was allowed to propose an action. An enterprise must control whether that action can -actually reach the system that performs it. - -Authorization is necessary, but it is not sufficient. A signed authorization proves -what Parmana approved. By itself, it does not prevent an AI that holds credentials from -calling the target directly. It also does not remove alternate network paths around the -authorized route. - -Parmana separates proposal from execution: - -> AI may propose work. -> -> Parmana decides what may execute. -> -> Only Parmana can release execution. - -The AI receives neither enterprise credentials nor a direct route to the target. -Parmana verifies the authorization and the exact content to be executed, then releases -that content through an authenticated Gateway session to a registered connector. Only -the connector can acquire the target credential. The target trusts the connector, not -the AI. - -For a target integrated under this deployment model, the AI workload has no target -credential and no network route to the target. The target accepts the connector's -workload identity only. Therefore the only reachable execution path is the Parmana -Gateway and its registered connector. - -This is a deployment-scoped claim, not a universal property of TypeScript. Operators -must remove legacy credentials and direct routes, isolate connector processes, enforce -target ingress policy, and protect the Gateway and connector workload identities. - -## 2. The Problem - -Today's AI platforms can invoke tools with consequences outside the model. A tool call -may create a payment, change a Salesforce record, deploy an application, delete data, -or send a command to a robot. These actions cross an important boundary: they turn a -model output into an enterprise side effect. - -Traditional authorization often assumes that the approved request is the request that -will be executed. That assumption can fail in several ways: - -- The AI can change its reasoning, plan, or parameters after approval. -- Middleware can transform or incorrectly reconstruct a payload. -- Data can drift between authorization and execution. -- A valid authorization can be replayed. -- An AI that possesses a credential can bypass the approved path entirely. -- A target with alternate ingress paths can be reached without the Gateway. - -Cryptographic authorization addresses some of these risks. It can prove that Parmana -approved an exact request and expose modifications or replay. But cryptography cannot -make a credential disappear from the AI process, close an alternate network route, or -force a target to accept traffic from only one execution path. - -That is why Parmana needs an execution-control layer. The system must control access to -the capability that causes the side effect, not only produce evidence that an action -was approved. - -## 3. The Architectural Principle - -Parmana does not attempt to constrain how an AI reasons. Models may reconsider, retry, -hallucinate, or generate different plans. Those behaviors are expected and do not form -the security boundary. - -The boundary is enterprise execution. Parmana controls the point at which an approved -proposal obtains the credentials and network access required to become a real action. - -The central principle is therefore: - -> AI may propose work. -> -> Parmana decides what may execute. -> -> Only Parmana can release execution. - -This principle turns authorization from advice that every caller is expected to follow -into a required step on the only reachable path to the target system. - -## 4. Design Goals - -Each design goal closes a specific route by which approved intent could diverge from -real execution: - -- **AI never owns execution credentials.** A component that holds a target credential - can act without returning to the authorization path. Target credentials are - therefore not placed in model context, agent memory, Runtime state, requests, - results, or audit records. -- **AI cannot execute directly.** The AI has no credential and no permitted network - route to the target or connector execution endpoint. Removing both forms of access - prevents a direct call from becoming an alternate execution path. -- **Connectors own credential use.** The component closest to the target is the final - place that can enforce target-specific authority. Credentials are isolated inside - the connector's trust boundary and are acquired only for a verified execution. -- **The Gateway is the single release point.** Only an authenticated - `ExecutionGateway` may submit work to the execution channel. This makes release a - distinct security decision rather than an incidental consequence of authorization. -- **Enterprise systems trust connectors, not AI.** Target IAM and ingress policy accept - the identity of the appropriate connector. The target therefore enforces the same - execution boundary even when an AI attempts to bypass upstream software. -- **Authorization remains independently verifiable.** The existing signed envelope and - canonical `EnvelopeVerifier` remain the source of cryptographic authorization truth. - Verification does not depend on trusting the AI's account of what was approved. -- **Executed content exactly matches authorized content.** The Gateway and connector - independently compare the canonical content hash with the signed authorization. - This prevents approval of one action from being reused for altered executable data. -- **Every release is single-use and attributable.** Authorization nonces and connector - sessions prevent replay; audit events link authorization, execution, session, - connector, credential use, and outcome. A valid approval cannot silently become - permission for repeated execution. -- **The Runtime remains stable.** The layer extends the existing `ExecutionSystem` seam - without changing policy evaluation, decisions, authorization format, cryptography, - or `EnvelopeVerifier` behavior. Execution control can therefore be added without - redefining the authorization model it is intended to enforce. -- **Connectors remain technology-neutral.** The same control model can support business - applications, cloud platforms, databases, internal APIs, robots, and IoT systems - through target-specific adapters. The security boundary stays consistent while the - target operation and credential mechanism vary. - -## 5. Architecture - -The architecture separates four zones. The AI operates in an untrusted proposal zone. -The Parmana control plane verifies and releases work. Credentials exist only inside the -credential zone. Side effects occur in the target zone. - -```text - UNTRUSTED / PROPOSAL ZONE PARMANA CONTROL PLANE CREDENTIAL ZONE TARGET ZONE - - AI Agent --> Runtime --> signed authorization --> ExecutionGateway - | canonical EnvelopeVerifier - | executable-content hash - | authenticated workload identity - v - ExecutionChannel --> ConnectorRegistry - | - one-time bound session - v - SecureConnector - | capability policy - | session + hash check - v - CredentialVault --> Target - (opaque lease) System -``` - -In business terms, the Runtime decides whether a proposal complies with policy. The -Gateway proves that the approval is valid and still applies to the exact action being -released. The execution channel permits only the Gateway to release work. The registry -selects an enterprise-owned connector. The connector checks its own authority, obtains -the least-privilege credential, and performs the action against the target. - -In implementation terms, the existing Runtime, authorization envelope, cryptography, -and `EnvelopeVerifier` remain canonical and unchanged. `ExecutionGateway` still -implements the existing `ExecutionSystem` port. Controlled execution is an additive -Gateway configuration; the legacy `connector` configuration remains compatible. - -### Package structure - -```text -packages/execution-gateway/src/ - ExecutionGateway.ts canonical verification + release point - execution-control/ - types.ts public ports and immutable messages - ConnectorRegistry.ts connector identity lookup - DefaultExecutionChannel.ts Gateway authentication + session creation - InMemoryGatewaySessionAuthority.ts reference one-time session authority - CapabilityConnectorPolicy.ts action/resource capability enforcement - DefaultSecureConnector.ts connector authentication + credential use - MemoryExecutionAuditSink.ts reference audit sink -``` - -The package keeps control-plane responsibilities distinct. `ExecutionGateway.ts` is -the release boundary. The `execution-control` package area contains the ports and -reference components needed to authenticate the Gateway, route to a connector, bind a -single-use session, apply connector capabilities, isolate credential use, and record -the outcome. - -Production adapters implement `GatewaySessionAuthority`, `CredentialVault`, -`CredentialedTarget`, and `ExecutionAuditSink`. Cloud IAM, OAuth, KMS, and concrete -vault products are deliberately outside this layer. - -### Interfaces and integration points - -Each interface exists to keep a security decision explicit and replaceable: - -- `GatewayIdentityProvider` presents the Gateway workload identity to the channel. It - prevents possession of request data from being treated as permission to release it. -- `ExecutionChannel` accepts verified releases only from that identity. It is the - authenticated boundary between verification and connector invocation. -- `ConnectorRegistry` resolves a stable connector identity; connectors are never - supplied by an AI request. This keeps routing under deployment control. -- `GatewaySessionAuthority` opens and atomically consumes sessions bound to connector, - execution, authorization, content hash, and expiry. This prevents reuse or movement - of a release to another execution context. -- `ConnectorPolicy` intersects the requested action and target with connector - capabilities. A connector cannot use its credential for an unsupported operation. -- `CredentialVault` returns an opaque, short-lived lease inside the connector. It - separates credential custody from the Runtime and Gateway. -- `CredentialedTarget` is the only code that sees the credential handle. It contains - the target-specific operation without exposing credentials to upstream components. -- `ExecutionAuditSink` records the session, credential use, rejection, and outcome. It - provides evidence without becoming the enforcement mechanism. - -The Runtime integration is unchanged: inject `ExecutionGateway` at the existing -`ExecutionSystem` seam. Configure either the existing `connector` option or the new -`executionControl` option. No policy, decision, authorization, or verifier type changes. - -## 6. Execution Flow - -The sequence below shows the complete path from a proposal to a target side effect. - -```text -AI -> Runtime: propose transaction -Runtime -> Gateway: ExecutionRequest + signed authorization -Gateway -> EnvelopeVerifier: signature, version, expiry, TTL -Gateway -> Gateway: recompute content hash; atomically consume authorization nonce -Gateway -> ExecutionChannel: frozen verified content + Gateway identity -ExecutionChannel -> Registry: resolve deployment-selected connector -ExecutionChannel -> SessionAuthority: open one-time bound session -ExecutionChannel -> SecureConnector: request + session -SecureConnector -> SecureConnector: recompute hash; authenticate/consume session -SecureConnector -> ConnectorPolicy: check action and target capabilities -SecureConnector -> CredentialVault: acquire opaque lease -SecureConnector -> Target: execute exact verified content with lease -SecureConnector -> CredentialVault: release lease -SecureConnector -> AuditSink: record outcome -``` - -The major steps exist for distinct reasons: - -1. **Proposal and policy decision.** The AI proposes a transaction; the Runtime applies - enterprise policy. The AI does not gain execution power by making the proposal. -2. **Signed authorization.** The Runtime sends the request with a content-bound - authorization. This creates independently verifiable evidence of exactly what was - approved. -3. **Gateway verification.** The Gateway checks signature, format version, expiry, TTL, - content hash, and nonce. Invalid, changed, expired, or replayed requests stop before - connector routing. -4. **Authenticated release.** The Gateway sends frozen content and its workload - identity to the execution channel. Correct data alone is insufficient; the caller - must also be the trusted Gateway. -5. **Deployment-owned routing.** The registry resolves the connector chosen by the - deployment. The AI cannot register or nominate a connector through request data. -6. **Single-use session binding.** The session authority binds the release to one - connector, execution, authorization, content hash, and expiry. A captured session - cannot be moved or used twice. -7. **Connector verification and policy.** The connector independently recomputes the - hash, consumes the session, and checks action and target capabilities. This protects - the credential boundary even if an upstream caller sends an invalid invocation. -8. **Credential acquisition and execution.** Only after every check passes does the - connector acquire an opaque lease and invoke the target with the exact verified - content. -9. **Credential release and audit.** The connector releases the lease and records the - outcome. Credential lifetime is limited, and the execution remains attributable. - -All decisions before credential acquisition are deterministic for the same request, -registry snapshot, capabilities, clock, and nonce/session state. Nonce and session -consumption are the two explicit atomic side effects. - -## 7. Trust Boundaries - -A trust boundary exists wherever data or authority moves between components with -different attacker assumptions. The AI is assumed to be able to produce arbitrary -requests. The Gateway and connectors are trusted enforcement components but remain -separately authenticated. The vault and target are protected resources. - -| Boundary | Why it exists | Required control | -| ----------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | -| AI to Runtime | Model output is untrusted input, not execution authority. | AI is untrusted and receives no target or vault credentials. | -| Gateway channel | An attacker may possess valid request data without being the authorized release service. | mTLS/workload identity; ingress permits the Gateway only. | -| Connector process | The component that can obtain credentials requires stronger isolation than the proposal or policy layers. | Separate identity/process; no public AI-reachable endpoint. | -| Credential vault | Credential custody must remain outside the AI, Runtime, and Gateway. | Connector identity may acquire only its named credential. | -| Target ingress | Credential isolation is ineffective if the target remains reachable through another path. | Network and IAM policy accept connector identities only. | -| Audit | Security investigations require durable evidence independent of volatile service state. | Append-only sink with authorization, session, connector, and result IDs. | - -The in-memory session authority and identity presentation are reference/test adapters, -not process or network security boundaries. A production deployment replaces them with -mutually authenticated transport and atomic shared replay storage. - -## 8. Threat Model and Mitigations - -The model treats the AI and its requests as untrusted. It also considers stolen -artifacts, replay, routing manipulation, and compromise of trusted components. - -| Attack | Why it matters | Mitigation and why it works | Required assumption | -| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | -| AI calls the target directly | If the AI can reach the target with a credential, it can ignore every Parmana decision. | AI has neither credential nor network route; target IAM trusts connector identity only. The call therefore cannot be authenticated or delivered. | Target credentials and ingress policy are exclusively controlled as described. | -| AI calls a connector directly | A connector is credential-adjacent, so a public invocation path would become a bypass. | Connector requires a Gateway-authenticated, one-time session. Request content alone cannot create that session. | Connector ingress and Gateway identity are protected. | -| Stolen authorization with changed amount/target | A valid signature must not approve a modified payment, resource, or command. | Gateway and connector recompute the canonical content hash. Modified content no longer matches the signed hash. | Canonical hashing and authorization verification remain trustworthy. | -| Authorization replay | A previously valid action may be harmful if performed more than once. | Existing nonce store atomically rejects the second use, even if the envelope remains unexpired. | All relevant instances share atomic nonce state. | -| Connector-session replay | Replaying a release after Gateway verification could repeat the target side effect. | Session authority atomically consumes the session once, so later invocations fail before credential acquisition. | All relevant instances share atomic session state. | -| Session moved to another connector or execution | A captured session must not authorize a different target path or transaction. | Session binds connector, execution, authorization, content hash, and expiry. Any changed binding fails authentication. | Session authority and connector correctly validate every binding. | -| Over-privileged connector invocation | A connector credential may cover more operations than the current request should use. | Connector capabilities and policy reject unsupported actions/target prefixes before credential acquisition. This narrows usable authority at the final enforcement point. | Connector policy and capability configuration are deployment-owned and correct. | -| Credential exfiltration through Runtime/AI response | A credential returned upstream would restore the direct-execution path Parmana is intended to remove. | Vault access exists only in the connector; requests, authorization, audit events, and results contain no credential. | Connector isolation and vault permissions prevent upstream access. | -| Registry/routing manipulation | Redirecting approved work to an attacker-controlled connector would bypass credential and policy controls. | Registry and route configuration are deployment-owned; AI data cannot select or register a connector. | Deployment configuration is protected from AI-controlled writes. | -| Compromised connector | A connector can legitimately acquire a scoped credential, so compromise may produce real side effects within that scope. | Limit blast radius with per-connector identity, least-privilege credential, egress policy, short leases, and target audit reconciliation. These controls contain rather than eliminate connector compromise. | IAM, egress, lease scope, and audit reconciliation are enforced outside the compromised process. | -| Compromised Gateway | The Gateway is the trusted release authority; compromise could turn valid policy results into malicious releases. | This layer cannot protect against its trusted release authority; isolate it, attest its workload, and monitor append-only audit records. | Gateway isolation, identity protection, attestation, and external audit monitoring remain trustworthy. | -| Legacy credential or alternate target route | Any unmanaged execution path disproves the claim that Parmana is unavoidable. | Remove it or the unavoidability claim does not hold; continuously reconcile target-native logs against Parmana executions to detect exceptions. | Operators remove or explicitly govern every bypass and retain target-native evidence. | - -## 9. Security Guarantees - -The architecture provides different kinds of guarantees. Keeping them separate avoids -claiming that cryptography can enforce deployment topology or that network controls can -prove request integrity. - -### Cryptographic guarantees - -- The authorization can be independently verified using the existing canonical - `EnvelopeVerifier` and Parmana public key. -- A modified action, target, parameter set, or business transaction identifier cannot - reuse the authorization because the executable-content hash will differ. -- An expired authorization fails verification. -- A consumed authorization nonce causes a replay attempt to fail. -- These guarantees preserve the existing authorization format and cryptography; this - layer does not redefine them. - -### Architectural guarantees - -- The Runtime and AI never receive the connector's credential handle. -- The execution channel accepts releases only with the configured Gateway identity. -- A secure connector rejects invocations without an authenticated, content-bound, - single-use Gateway session. -- Connector sessions cannot be reused or moved to another connector, execution, - authorization, content hash, or expiry context. -- Connector capability checks occur before credential acquisition. -- The credential lease exists only inside the secure connector-to-target path and is - released after use. -- Every accepted controlled execution can be linked to its authorization, session, - connector, credential acquisition event, and result. - -### Guarantees that depend on deployment - -- AI cannot execute directly only when it has no target credential and no alternate - network route. -- Enterprise execution always passes through Parmana only when target IAM and ingress - accept connector identities exclusively. -- Credential isolation is effective only when connector processes and vault permissions - are isolated from AI and Runtime workloads. -- Gateway and connector authentication is effective only when their workload identities - and mutually authenticated transport are protected. -- Cross-process replay protection requires atomic shared nonce and session storage. - -Under those deployment conditions, planning, retrying, hallucinating, or changing a -request cannot create a Gateway identity, connector session, vault lease, target -credential, or target network route. Only Parmana can release execution. - -## 10. Deployment Assumptions - -"Unavoidable execution" is a deployment property, not a property of TypeScript or the -Runtime alone. The claim holds for an integrated target only when all of the following -conditions are true: - -- The AI, agent tools, Runtime, and general middleware hold no target credentials. -- Legacy credentials are removed, disabled, or isolated as explicit exceptions. -- The target has no alternate network route reachable by the AI workload. -- Target IAM permits the registered connector identity and denies AI identities. -- Target ingress permits connector traffic and rejects direct AI traffic. -- Connector processes run under separate, protected workload identities. -- Vault policy permits each connector to acquire only its named, least-privilege - credential. -- The Gateway identity is protected and accepted only by the execution channel. -- Connector identities are protected and accepted only by their intended vault and - target scopes. -- Production transport uses mutual authentication, such as mTLS or equivalent workload - identity enforcement. -- Nonce and session consumption use atomic storage shared by all relevant instances. -- Registry and routing configuration remain deployment-owned and are not writable by - AI-controlled request data. -- Audit storage is append-only, and target-native activity is reconciled against - Parmana execution records. - -Break-glass users, vendor consoles, unmanaged credentials, or alternate network paths -are explicit bypasses. They must be removed, isolated, monitored, or reported as -exceptions rather than hidden by the claim. - -## 11. Why This Is Different - -Many security systems stop after deciding whether a caller is permitted and recording -what happened: - -```text -Identity - | - v -Authorization - | - v -Logging -``` - -That model can establish who asked, what policy allowed, and what the application says -occurred. It does not necessarily control the capability that reaches the enterprise -system. If the AI still holds a credential or can use another route, authorization and -logging can be bypassed. - -Parmana continues through the execution boundary: - -```text -Authorization - | - v -Verification - | - v -Execution Gateway - | - v -Credential Isolation - | - v -Connector Enforcement - | - v -Enterprise System -``` - -Cryptographic authorization proves what Parmana approved. The execution-control layer -controls whether approved work can obtain the capability to act. The capability is -released only after canonical verification, is scoped to one registered connector and -one exact execution, and is consumed before that connector acquires its credential. - -This is fundamentally different from recording execution. Logging observes or reports -a side effect after a component had the power to cause it. Parmana places verification, -credential custody, and connector enforcement before the side effect. Audit remains -important, but it supports evidence and detection rather than serving as the primary -control. - -## 12. Future Evolution - -The interfaces intentionally leave room for stronger production adapters without -changing the core Runtime or authorization model. Possible areas of future work include: - -- stronger process, container, and host isolation for connectors; -- hardware-backed Gateway and connector workload identities; -- confidential-computing environments for credential use and connector execution; -- distributed execution channels with shared atomic replay protection; -- additional connectors for enterprise applications, cloud platforms, databases, - internal APIs, robots, and IoT systems. - -These are architectural directions, not current guarantees or promised features. Each -would require its own implementation, deployment controls, tests, and evidence before -expanding the security claim. diff --git a/docs/connectors/CONNECTING_AN_AGENT.md b/docs/connectors/CONNECTING_AN_AGENT.md index e5ad332b..ff1a2d9d 100644 --- a/docs/connectors/CONNECTING_AN_AGENT.md +++ b/docs/connectors/CONNECTING_AN_AGENT.md @@ -89,8 +89,7 @@ JSON array of caller records). Adding it requires redeploying/restarting the API **If you're the deployment operator adding this key, not just the agent developer:** `PARMANA_API_KEYS` is a single environment variable holding a JSON **array**. Two real mistakes were made adding a new -caller to a live deployment (2026-09-15/16, `docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md` -item 6), both from hand-editing the value in a hosting provider's dashboard rather than scripting it: +caller to a live deployment (2026-09-15/16, item 6 of the KMS migration troubleshooting guide, in git history), both from hand-editing the value in a hosting provider's dashboard rather than scripting it: - **Pasting the new entry as the entire value**, not appended to the array — e.g. saving `{"callerId":"my-refund-agent",...}` instead of `[{"callerId":"existing-caller",...},{"callerId":"my-refund-agent",...}]`. @@ -325,7 +324,7 @@ Part 3 table exists to help a caller correctly _not_ blame on themselves: caller's request at all. See `examples/tutorials/114-signing-verification-key-agreement/` for a runnable reproduction. -Full account: `docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md`. +Full account: the 2026-09-15 KMS migration troubleshooting guide (in git history). ## What this guide does not cover diff --git a/docs/connectors/FULL_INTEGRATION_OVERVIEW.md b/docs/connectors/FULL_INTEGRATION_OVERVIEW.md index eb86351c..a6640c70 100644 --- a/docs/connectors/FULL_INTEGRATION_OVERVIEW.md +++ b/docs/connectors/FULL_INTEGRATION_OVERVIEW.md @@ -82,7 +82,7 @@ codebase's own history (several the same night, integrating the real Pfinite/Pay | Connector + caller key | Policy | `404 PolicyNotFoundError` — the request never reaches your connector at all. | | Policy + connector | Caller key / correct scoping | `401` (no key), or `403 CAPABILITY_NOT_ALLOWED` (key exists but isn't scoped to this capability), or `403` (`principalId` not permitted) — see `CONNECTING_AN_AGENT.md` Part 3's full table. | | All three | `SignalStateVerifier` | Works, but a caller that lies about a signal (`vendorVerified: true` when it isn't) is trusted at face value — the policy evaluates whatever it's told, nothing independently checks it's true. This is the residual gap `docs/site/handbook/05-signal-binding-and-state-verification.mdx` names directly; it's a real, accepted scope limit for any capability without a verifier wired up, not a hidden bug. | -| Everything, but the deployment's own signing/verification config is broken | — | A real, otherwise-correct request fails with an opaque `500` unrelated to anything the caller did — see `docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md` for a full real account of exactly this happening, and `examples/tutorials/114-signing-verification-key-agreement/` to see the failure mode reproduced directly. This is an operator-side deployment problem, not something any of the four pieces above can detect or fix from the outside. | +| Everything, but the deployment's own signing/verification config is broken | — | A real, otherwise-correct request fails with an opaque `500` unrelated to anything the caller did — see the 2026-09-15 KMS migration troubleshooting guide (in git history) for a full real account of exactly this happening, and `examples/tutorials/114-signing-verification-key-agreement/` to see the failure mode reproduced directly. This is an operator-side deployment problem, not something any of the four pieces above can detect or fix from the outside. | ## Where to go next diff --git a/docs/connectors/PAYTM_CONNECTOR.md b/docs/connectors/PAYTM_CONNECTOR.md index 6949a655..eceab481 100644 --- a/docs/connectors/PAYTM_CONNECTOR.md +++ b/docs/connectors/PAYTM_CONNECTOR.md @@ -45,12 +45,12 @@ connector was explicitly required not to blur it. A third layer (cryptographic a signing) was added by ADR-0009 Phase 2B (2026-09-13) after a code-level audit found the original two-layer design left a real gap — see the correction below the table. -| Layer | What it proves | Where it lives | What it is NOT | -| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **Parmana authorization** | This specific refund (this amount, this order, this transaction) was actually approved by policy, for a reason Parmana can show. | `customer-refund@1.0.0` policy, `SignalIntentBinder`, `CapabilityPolicyBinder`, `SignedTokenConnectorAuthenticator`, `ExecutionControlService` — all pre-existing, all unmodified by this connector. | Not something Paytm's API has any concept of. | -| **Authorization signature** _(added Phase 2B)_ | This exact `businessTransactionId`/`orderId`/`txnId`/`amount` combination is the one Parmana's policy engine actually approved — not merely that _some_ refund was approved at some point. | `GatewayPaytmAdapter` signs `canonicalPaytmAuthorizationString(...)` (`packages/connector-paytm/src/PaytmTypes.ts`) with the gateway's own key (`SignerBootstrap`/`DEFAULT_KEY_ID`) and attaches `signature`/`keyId`/`expiresAt` (60s TTL, `PAYTM_AUTHORIZATION_SIGNATURE_TTL_MS`) to the outbound `authorization` object. `parmana-paytm-agent` fetches the matching public key via `GET /keys/:keyId` and verifies before executing. | **Update (2026-09-16): now backed by AWS KMS in production** (`KEY_PROVIDER=aws-kms`, `docs/adr/ADR-0009-KMS-Secrets-And-Connector-Signature-Hardening.md`, `docs/operations/aws-kms-vercel-oidc-setup-guide.md`). Verified live against the real, deployed `parmana-paytm-agent` service: its own audit trail recorded `authorization.verified` for a real signed request, confirming the KMS-signed signature verifies correctly on the receiving side. Getting there required fixing a real bug where the gateway's signing and per-authorization verification paths had silently diverged (`docs/VERIFICATION-GAPS.md` G-48) — see `examples/tutorials/114-signing-verification-key-agreement/` for a reproduction. | -| **Connector transport authentication** | This HTTPS request actually came from Parmana's gateway, not an arbitrary caller. | `PAYTM_CONNECTOR_SHARED_SECRET`, sent as a Bearer token on the one call `GatewayPaytmAdapter` makes. | Not Paytm's merchant key/checksum. On its own (before Phase 2B), this was the _only_ thing standing between an arbitrary caller and a real refund — see below. | -| **Paytm's own authentication/checksum** | Paytm's API believes the connector service is a legitimate Paytm merchant integration. | Entirely inside `parmana-paytm-agent`. This codebase never sees it, never holds `PAYTM_MERCHANT_KEY`, and never constructs a Paytm checksum. | Not a substitute for Parmana authorization. | +| Layer | What it proves | Where it lives | What it is NOT | +| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Parmana authorization** | This specific refund (this amount, this order, this transaction) was actually approved by policy, for a reason Parmana can show. | `customer-refund@1.0.0` policy, `SignalIntentBinder`, `CapabilityPolicyBinder`, `SignedTokenConnectorAuthenticator`, `ExecutionControlService` — all pre-existing, all unmodified by this connector. | Not something Paytm's API has any concept of. | +| **Authorization signature** _(added Phase 2B)_ | This exact `businessTransactionId`/`orderId`/`txnId`/`amount` combination is the one Parmana's policy engine actually approved — not merely that _some_ refund was approved at some point. | `GatewayPaytmAdapter` signs `canonicalPaytmAuthorizationString(...)` (`packages/connector-paytm/src/PaytmTypes.ts`) with the gateway's own key (`SignerBootstrap`/`DEFAULT_KEY_ID`) and attaches `signature`/`keyId`/`expiresAt` (60s TTL, `PAYTM_AUTHORIZATION_SIGNATURE_TTL_MS`) to the outbound `authorization` object. `parmana-paytm-agent` fetches the matching public key via `GET /keys/:keyId` and verifies before executing. | **Update (2026-09-16): now backed by AWS KMS in production** (`KEY_PROVIDER=aws-kms`, `docs/adr/ADR-0009-KMS-Secrets-And-Connector-Signature-Hardening.md`, `docs/site/deployment/aws-kms-signing.mdx`). Verified live against the real, deployed `parmana-paytm-agent` service: its own audit trail recorded `authorization.verified` for a real signed request, confirming the KMS-signed signature verifies correctly on the receiving side. Getting there required fixing a real bug where the gateway's signing and per-authorization verification paths had silently diverged (`docs/VERIFICATION-GAPS.md` G-48) — see `examples/tutorials/114-signing-verification-key-agreement/` for a reproduction. | +| **Connector transport authentication** | This HTTPS request actually came from Parmana's gateway, not an arbitrary caller. | `PAYTM_CONNECTOR_SHARED_SECRET`, sent as a Bearer token on the one call `GatewayPaytmAdapter` makes. | Not Paytm's merchant key/checksum. On its own (before Phase 2B), this was the _only_ thing standing between an arbitrary caller and a real refund — see below. | +| **Paytm's own authentication/checksum** | Paytm's API believes the connector service is a legitimate Paytm merchant integration. | Entirely inside `parmana-paytm-agent`. This codebase never sees it, never holds `PAYTM_MERCHANT_KEY`, and never constructs a Paytm checksum. | Not a substitute for Parmana authorization. | **Correction to an earlier version of this document.** This section used to claim "a caller who somehow obtained a valid `PAYTM_CONNECTOR_SHARED_SECRET` still cannot invoke Paytm... there is no diff --git a/docs/developer/extending-parmana.md b/docs/developer/extending-parmana.md index 1722dd42..8337760a 100644 --- a/docs/developer/extending-parmana.md +++ b/docs/developer/extending-parmana.md @@ -38,7 +38,7 @@ Every rule below is backed by an automated test that fails the build if violated 2. Compare each declared signal against the independently-fetched value; return a `SignalStateViolation` for each mismatch. 3. Wire it into the composite verifier: `packages/api/src/bootstrap/createSignalStateVerifier.ts`, composed with the others via `CompositeSignalStateVerifier` in `packages/api/src/application.ts`. -See [`../architecture/execution-walkthrough.md` §3](../architecture/execution-walkthrough.md) for exactly where this runs in the request lifecycle (after policy evaluation, before authorization is signed). +See step 4 of [The Execution Pipeline](https://docs.parmanasystems.com/architecture/execution-pipeline) for exactly where this runs in the request lifecycle (after policy evaluation, before authorization is signed). ## Adding a policy diff --git a/docs/guides/e2e/01-starting-the-api.md b/docs/guides/e2e/01-starting-the-api.md deleted file mode 100644 index 280b7b6d..00000000 --- a/docs/guides/e2e/01-starting-the-api.md +++ /dev/null @@ -1,53 +0,0 @@ -\# Step 1 - Authentication Enforcement - -\## Request - -GET / - -No Authorization header. - -\## Result - -HTTP 401 - -{ - - "error": "authentication required" - -} - -\## Status - -PASS - -The API rejects anonymous callers before routing requests. - -\# Authenticated Request - -\## Request - -GET / - -Authorization: Bearer my-secret-api-key - -\## Response - -HTTP 200 OK - -```json - -{ - - "name": "Parmana", - - "status": "UP" - -} - -``` - -\## Status - -PASS - -Authenticated callers can successfully access protected endpoints after caller authentication. diff --git a/docs/guides/e2e/03-credential-enforcement.md b/docs/guides/e2e/03-credential-enforcement.md deleted file mode 100644 index 8d94602d..00000000 --- a/docs/guides/e2e/03-credential-enforcement.md +++ /dev/null @@ -1,191 +0,0 @@ -\# E2E Step 3 – Credential Enforcement - -\## Objective - -Verify that Parmana enforces credential isolation during execution and fails closed when the required connector credential is unavailable. - -\--- - -\## Preconditions - -\- API running - -\- Caller authentication configured - -\- Authenticated request accepted - -\- Valid Business Transaction submitted to `/execute` - -\--- - -\## Request - -```http - -POST /execute - -Authorization: Bearer - -Content-Type: application/json - -``` - -Business Transaction: - -\- Valid UUID - -\- Authority - -\- Authorization - -\- Intent - -\- Policy - -\- Signals - -\--- - -\## Runtime Progress - -The request successfully passed the following stages: - -``` - -\[ROUTE] before execute - -\[APP] 1 - accept - -\[APP] 2 - runtime - -``` - -This confirms the transaction entered the Runtime Engine and progressed into the execution pipeline. - -\--- - -\## Failure - -Execution stopped before connector invocation. - -``` - -Error: - -Environment variable "VENDOR\_PAYMENT\_TOKEN" - -for connector "vendor-payment" - -is not set. - -``` - -Stack trace (truncated): - -``` - -EnvironmentCredentialProvider.resolve() - - - -CredentialVaultAdapter.getCredential() - - - -SessionCredentialVault.issue() - - - -SessionCredentialSecureConnector.execute() - - - -ExecutionControlService.execute() - - - -ExecutionComponent.execute() - - - -RuntimePipeline.execute() - - - -RuntimeEngine.execute() - - - -Runtime.execute() - -``` - -\--- - -\## Expected Behaviour - -\*\*PASS\*\* - -Parmana intentionally refused to execute because the connector credential was unavailable. - -No connector credential was fabricated. - -No fallback credential was used. - -No execution was attempted. - -The platform failed closed. - -\--- - -\## Security Guarantee - -This validates Parmana's credential isolation architecture. - -The Runtime Engine cannot execute protected business actions unless the required connector credential is explicitly available. - -AI agents never possess execution credentials. - -Execution credentials are resolved only by the Credential Vault immediately before execution. - -\--- - -\## Evidence - -Authentication: PASS - -Business Transaction Mapping: PASS - -Runtime Pipeline: PASS - -Execution Control: PASS - -Credential Vault: PASS - -Connector Authentication: BLOCKED - -External Execution: NOT ATTEMPTED - -\--- - -\## Result - -\*\*PASS\*\* - -Credential isolation successfully prevented unauthorized execution. - -This demonstrates that Parmana enforces execution security even after a request has been accepted and authorized. - -\--- - -\## Next Step - -Configure the required connector credential: - -``` - -VENDOR\_PAYMENT\_TOKEN= - -``` - -Restart the API and repeat the `/execute` request to verify successful execution through the connector and generation of the Execution Trust Record. diff --git a/docs/guides/e2e/04-execute-transaction.md b/docs/guides/e2e/04-execute-transaction.md deleted file mode 100644 index ff207138..00000000 --- a/docs/guides/e2e/04-execute-transaction.md +++ /dev/null @@ -1,305 +0,0 @@ -\# 04 – Execute a Business Transaction - -This guide walks through the complete execution lifecycle in Parmana—from submitting a business transaction through execution, verification, and receipt generation. - -\--- - -\## Prerequisites - -The API is running: - -```text - -API running on http://localhost:3000 - -``` - -Authentication is configured: - -```env - -PARMANA\_API\_KEYS=\[{"callerId":"demo","keyHash":"325ededd6c3b9988f623c7f964abb9b016b76b0f8b3474df0f7d7c23b941381f"}] - -``` - -Execution connector credential is configured: - -```env - -VENDOR\_PAYMENT\_TOKEN=integration-test-token - -``` - -\--- - -\## Execute - -```powershell - -$body = Get-Content .\\docs\\guides\\e2e\\execute-request.json -Raw - - - -Invoke-RestMethod ` - - -Method POST ` - - -Uri http://localhost:3000/execute ` - - -Headers @{ - - Authorization = "Bearer my-secret-api-key" - - } ` - - -ContentType "application/json" ` - - -Body $body - -``` - -\--- - -\## Successful Response - -The API returned a complete Execution Trust Record. - -| Component | Status | - -|-----------|--------| - -| Authentication | ✅ Passed | - -| Request Validation | ✅ Passed | - -| Policy Evaluation | ✅ Completed | - -| Execution | ✅ Completed | - -| Trust Record | ✅ Created | - -| Signature | ✅ Created | - -| Verification | ✅ Verified | - -| Receipt | ✅ Issued | - -\--- - -\## Execution Summary - -Business Transaction - -``` - -b9404bdf-ad2a-4dce-8001-0eacd0974200 - -``` - -Trust Record - -``` - -3280db24-fdd3-4feb-ac04-97c3b07c8c41 - -``` - -Execution - -``` - -bf78f1c9-f007-40ab-9a14-92bc6f815f1f - -``` - -Verification - -``` - -999bedec-876d-4fdf-b1a3-91d01866ffcd - -``` - -Receipt - -``` - -a677de4e-472c-4b37-8a12-4199d2510b8b - -``` - -\--- - -\## Trust Record Hash - -``` - -f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4 - -``` - -\--- - -\## Digital Signature - -Algorithm - -``` - -ed25519 - -``` - -The Execution Trust Record was signed successfully. - -\--- - -\## Independent Verification - -Verification Status - -``` - -VERIFIED - -``` - -Message - -``` - -Execution Trust Record verified successfully. - -``` - -This confirms that the generated Trust Record is cryptographically valid and has not been modified. - -\--- - -\## Execution Receipt - -A signed Execution Receipt was issued. - -Receipt ID - -``` - -a677de4e-472c-4b37-8a12-4199d2510b8b - -``` - -Receipt Hash - -``` - -562410a8687a2771d2f50ae71811cff2822340c52f485cb58eed918823a67de1 - -``` - -Algorithm - -``` - -ed25519 - -``` - -\--- - -\## Execution Lifecycle - -```text - -Client - - │ - - ▼ - -Authenticate Caller - - │ - - ▼ - -Validate Request - - │ - - ▼ - -Evaluate Policy - - │ - - ▼ - -Authorize Execution - - │ - - ▼ - -Execute Connector - - │ - - ▼ - -Create Execution Trust Record - - │ - - ▼ - -Sign Trust Record - - │ - - ▼ - -Verify Trust Record - - │ - - ▼ - -Issue Execution Receipt - -``` - -\--- - -\## What This Demonstrates - -This walkthrough validates the complete Parmana execution pipeline: - -\- Caller authentication - -\- Business transaction creation - -\- Policy evaluation - -\- Authorized execution - -\- Connector invocation - -\- Execution Trust Record generation - -\- Cryptographic signing - -\- Independent verification - -\- Execution Receipt issuance - -The returned Execution Trust Record provides verifiable evidence of what was requested, what policy authorized it, what was executed, and the cryptographic proof needed to independently validate the outcome. - -\--- - -\## Next Guide - -Continue with: - -\- \*\*05 – Verify an Execution Trust Record\*\* - -This guide demonstrates how to independently verify an existing Execution Trust Record using the `/verify` endpoint. diff --git a/docs/guides/e2e/05-verify-execution-trust-record.md b/docs/guides/e2e/05-verify-execution-trust-record.md deleted file mode 100644 index 4cf71db4..00000000 --- a/docs/guides/e2e/05-verify-execution-trust-record.md +++ /dev/null @@ -1,181 +0,0 @@ -\# 05 – Verify an Execution Trust Record - -This guide demonstrates how to independently verify an Execution Trust Record after execution. - -Verification confirms that the Trust Record has not been modified since it was created and that its cryptographic signature is valid. - -\--- - -\## Prerequisites - -Complete: - -\- 01 – Starting the API - -\- 02 – Authentication - -\- 03 – Execute a Business Transaction - -You should have a Trust Record ID similar to: - -``` - -3280db24-fdd3-4feb-ac04-97c3b07c8c41 - -``` - -\--- - -\## Verify - -Send the Trust Record to the verification endpoint. - -```powershell - -$body = Get-Content .\\docs\\guides\\e2e\\verify-request.json -Raw - - - -Invoke-RestMethod ` - - -Method POST ` - - -Uri http://localhost:3000/verify ` - - -Headers @{ - - Authorization = "Bearer my-secret-api-key" - - } ` - - -ContentType "application/json" ` - - -Body $body - -``` - -\--- - -\## Successful Response - -A successful verification returns: - -```json - -{ - - "status": "VERIFIED", - - "message": "Execution Trust Record verified successfully.", - - "verificationId": "999bedec-876d-4fdf-b1a3-91d01866ffcd", - - "verifiedAt": "2026-07-18T03:59:45.798Z", - - "trustRecordHash": "f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4", - - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200" - -} - -``` - -\--- - -\## Verification Summary - -| Item | Value | - -|------|-------| - -| Status | VERIFIED | - -| Algorithm | ed25519 | - -| Trust Record Hash | f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4 | - -| Result | Signature Valid | - -\--- - -\## Verification Pipeline - -```text - -Execution Trust Record - - │ - - ▼ - -Canonical Serialization - - │ - - ▼ - -Hash Calculation - - │ - - ▼ - -Signature Verification - - │ - - ▼ - -Trust Record Validation - - │ - - ▼ - -VERIFIED - -``` - -\--- - -\## What Verification Confirms - -Verification provides independent evidence that: - -\- The Trust Record has not been altered. - -\- The cryptographic signature is valid. - -\- The recorded execution is authentic. - -\- The evidence is suitable for audit and compliance. - -Verification does not re-execute the business transaction. It validates the integrity and authenticity of the recorded execution. - -\--- - -\## Expected Failure Cases - -Verification should fail if: - -\- Any field in the Trust Record is modified. - -\- The Trust Record hash changes. - -\- The signature is altered. - -\- The signing key is unknown. - -\- The signature algorithm does not match the stored signature. - -These failure modes help detect tampering and protect the integrity of execution evidence. - -\--- - -\## Next Guide - -Continue with: - -\*\*06 – Retrieve an Execution Receipt\*\* - -This guide shows how to retrieve the signed Execution Receipt generated for a completed business transaction. diff --git a/docs/guides/e2e/06-generate-execution-receipt.m b/docs/guides/e2e/06-generate-execution-receipt.m deleted file mode 100644 index 8344cddf..00000000 --- a/docs/guides/e2e/06-generate-execution-receipt.m +++ /dev/null @@ -1,39 +0,0 @@ -## Successful Response - -The API returned: - -```json -{ - "receiptId": "6f1e7586-ae5d-4718-8d06-36aa541bdb8b", - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200", - "trustRecordHash": "f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4", - "receiptHash": "bd1280f038f0e9257b86f006fb023d90ae1f00d0b9f12451c72bf7d06c469ac6", - "issuedAt": "2026-07-18T04:05:47.378Z", - "algorithm": "ed25519", - "signature": "r4+YxAtVIL7BHJhEr5bPZSq1lvAKf50C3umPmIrMwfb9wAGks5Oxs4A+Ohae/6H7qddb3QxTvxcNtVR4Uo75CQ==" -} -``` - ---- - -## Receipt Summary - -| Property | Value | -|----------|-------| -| Receipt ID | 6f1e7586-ae5d-4718-8d06-36aa541bdb8b | -| Business Transaction ID | b9404bdf-ad2a-4dce-8001-0eacd0974200 | -| Trust Record Hash | f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4 | -| Receipt Hash | bd1280f038f0e9257b86f006fb023d90ae1f00d0b9f12451c72bf7d06c469ac6 | -| Algorithm | ed25519 | -| Status | Receipt Generated | - ---- - -## What This Validates - -This confirms that Parmana successfully: - -- Located the Execution Trust Record for the Business Transaction. -- Generated a deterministic receipt hash. -- Signed the receipt using the configured signature provider (Ed25519). -- Returned a portable cryptographic receipt that can be independently verified later. \ No newline at end of file diff --git a/docs/guides/e2e/06-generate-execution-receipt.md b/docs/guides/e2e/06-generate-execution-receipt.md deleted file mode 100644 index 53dd9725..00000000 --- a/docs/guides/e2e/06-generate-execution-receipt.md +++ /dev/null @@ -1,154 +0,0 @@ -# 06 – Generate an Execution Receipt - -This guide demonstrates how to generate an Execution Receipt for a completed Business Transaction. - -An Execution Receipt is a cryptographically signed artifact that provides verifiable evidence of a completed and verified execution. - ---- - -## Prerequisites - -Complete the following guides before proceeding: - -- 01 – Starting the API -- 02 – Authentication -- 03 – Execute a Business Transaction -- 04 – Execute Transaction - -Ensure you have a completed Business Transaction. - -Example: - -``` -Business Transaction ID - -b9404bdf-ad2a-4dce-8001-0eacd0974200 -``` - ---- - -## Request File - -``` -docs/guides/e2e/receipt-request.json -``` - -```json -{ - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200" -} -``` - ---- - -## Generate Receipt - -```powershell -$body = Get-Content .\docs\guides\e2e\receipt-request.json -Raw - -Invoke-RestMethod ` - -Method POST ` - -Uri http://localhost:3000/receipt ` - -Headers @{ - Authorization = "Bearer my-secret-api-key" - } ` - -ContentType "application/json" ` - -Body $body -``` - ---- - -## Successful Response - -The API returned the following receipt. - -```json -{ - "receiptId": "6f1e7586-ae5d-4718-8d06-36aa541bdb8b", - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200", - "trustRecordHash": "f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4", - "receiptHash": "bd1280f038f0e9257b86f006fb023d90ae1f00d0b9f12451c72bf7d06c469ac6", - "issuedAt": "2026-07-18T04:05:47.378Z", - "algorithm": "ed25519", - "signature": "r4+YxAtVIL7BHJhEr5bPZSq1lvAKf50C3umPmIrMwfb9wAGks5Oxs4A+Ohae/6H7qddb3QxTvxcNtVR4Uo75CQ==" -} -``` - -The complete response is also available in: - -``` -docs/guides/e2e/receipt-response.json -``` - ---- - -## Receipt Summary - -| Property | Value | -| ----------------------- | ---------------------------------------------------------------- | -| Receipt ID | 6f1e7586-ae5d-4718-8d06-36aa541bdb8b | -| Business Transaction ID | b9404bdf-ad2a-4dce-8001-0eacd0974200 | -| Trust Record Hash | f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4 | -| Receipt Hash | bd1280f038f0e9257b86f006fb023d90ae1f00d0b9f12451c72bf7d06c469ac6 | -| Signature Algorithm | Ed25519 | -| Status | Receipt Generated | - ---- - -## Receipt Lifecycle - -```text -Business Transaction - │ - ▼ -Execution Trust Record - │ - ▼ -Verification - │ - ▼ -Receipt Generation - │ - ▼ -Receipt Hash - │ - ▼ -Digital Signature - │ - ▼ -Execution Receipt -``` - ---- - -## What This Validates - -Generating an Execution Receipt confirms that Parmana successfully: - -- Located the completed Business Transaction. -- Retrieved the associated Execution Trust Record. -- Generated a deterministic Receipt Hash. -- Signed the receipt using the configured signature provider. -- Returned a portable cryptographic artifact that can be independently verified. - -The receipt serves as verifiable evidence that the execution completed successfully and is suitable for audit, compliance, and long-term record keeping. - ---- - -## Files Used - -| File | Purpose | -| -------------------------------------------------- | ------------------- | -| `docs/guides/e2e/receipt-request.json` | Request payload | -| `docs/guides/e2e/receipt-response.json` | Successful response | -| `docs/guides/e2e/06-generate-execution-receipt.md` | This guide | - ---- - -## Next Guide - -Continue with: - -**07 – Replay a Business Transaction** - -The next guide demonstrates deterministic replay of a previously executed Business Transaction to confirm that identical inputs produce identical authorization and execution outcomes. diff --git a/docs/guides/e2e/06-retrieve-execution-receipt.md b/docs/guides/e2e/06-retrieve-execution-receipt.md deleted file mode 100644 index 2a1c65cf..00000000 --- a/docs/guides/e2e/06-retrieve-execution-receipt.md +++ /dev/null @@ -1,185 +0,0 @@ -\# 06 – Retrieve an Execution Receipt - -This guide demonstrates how to retrieve the signed Execution Receipt for a completed business transaction. - -An Execution Receipt is cryptographically signed evidence that a business transaction completed successfully. - -\--- - -\## Prerequisites - -Complete: - -\- 01 – Starting the API - -\- 02 – Authentication - -\- 03 – Execute a Business Transaction - -\- 04 – Execute Transaction - -You should have a Receipt ID similar to: - -``` - -a677de4e-472c-4b37-8a12-4199d2510b8b - -``` - -\--- - -\## Retrieve the Latest Receipt - -```powershell - -Invoke-RestMethod ` - - -Method GET ` - - -Uri http://localhost:3000/receipt/latest ` - - -Headers @{ - - Authorization = "Bearer my-secret-api-key" - - } - -``` - -\--- - -\## Retrieve a Specific Receipt - -Replace the receipt ID with your own. - -```powershell - -$receiptId = "a677de4e-472c-4b37-8a12-4199d2510b8b" - - - -Invoke-RestMethod ` - - -Method GET ` - - -Uri "http://localhost:3000/receipt/$receiptId" ` - - -Headers @{ - - Authorization = "Bearer my-secret-api-key" - - } - -``` - -\--- - -\## Expected Response - -A successful request returns an Execution Receipt containing: - -\- Receipt ID - -\- Business Transaction ID - -\- Trust Record Hash - -\- Receipt Hash - -\- Signature - -\- Signature Algorithm - -\- Issue Timestamp - -Example: - -```json - -{ - - "receiptId": "a677de4e-472c-4b37-8a12-4199d2510b8b", - - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200", - - "trustRecordHash": "f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4", - - "receiptHash": "562410a8687a2771d2f50ae71811cff2822340c52f485cb58eed918823a67de1", - - "algorithm": "ed25519", - - "signature": "", - - "issuedAt": "2026-07-18T03:59:47.535Z" - -} - -``` - -\--- - -\## Receipt Contents - -Every Execution Receipt binds together: - -\- The completed business transaction - -\- The generated Execution Trust Record - -\- A cryptographic hash of the Trust Record - -\- A digital signature - -\- The issuance timestamp - -This allows independent verification of the execution evidence without re-running the transaction. - -\--- - -\## Receipt Lifecycle - -```text - -Business Transaction - - │ - - ▼ - -Execution Trust Record - - │ - - ▼ - -Hash - - │ - - ▼ - -Digital Signature - - │ - - ▼ - -Execution Receipt - -``` - -\--- - -\## What This Demonstrates - -Retrieving a receipt confirms that Parmana has produced a persistent, signed record of the completed execution. The receipt can be stored, shared, or verified later as evidence of what was executed and when. - -\--- - -\## Next Guide - -Continue with: - -\*\*07 – Replay a Business Transaction\*\* - -Replay demonstrates deterministic execution by re-evaluating a previously recorded transaction and confirming that the same inputs produce the same outcome. diff --git a/docs/guides/e2e/07-replay-business-transaction.md b/docs/guides/e2e/07-replay-business-transaction.md deleted file mode 100644 index 9261da0b..00000000 --- a/docs/guides/e2e/07-replay-business-transaction.md +++ /dev/null @@ -1,205 +0,0 @@ -\# 07 – Replay a Business Transaction - -This guide demonstrates how to replay a previously executed Business Transaction. - -Replay reconstructs the Execution Trust Record from the stored transaction and validates that Parmana can deterministically reproduce the recorded execution. - -\--- - -\## Prerequisites - -Complete: - -\- 01 – Starting the API - -\- 02 – Authentication - -\- 03 – Execute a Business Transaction - -\- 04 – Execute Transaction - -\- 05 – Verify an Execution Trust Record - -\- 06 – Generate an Execution Receipt - -Use the Business Transaction ID returned during execution. - -Example: - -``` - -b9404bdf-ad2a-4dce-8001-0eacd0974200 - -``` - -\--- - -\## Request File - -``` - -docs/guides/e2e/replay-request.json - -``` - -```json - -{ - - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200" - -} - -``` - -\--- - -\## Replay - -```powershell - -$body = Get-Content .\\docs\\guides\\e2e\\replay-request.json -Raw - - - -Invoke-RestMethod ` - - -Method POST ` - - -Uri http://localhost:3000/replay ` - - -Headers @{ - - Authorization = "Bearer my-secret-api-key" - - } ` - - -ContentType "application/json" ` - - -Body $body - -``` - -\--- - -\## Successful Response - -The API returned: - -```json - -{ - - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200", - - "trustRecordHash": "f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4", - - "verified": true - -} - -``` - -The complete response is also available in: - -``` - -docs/guides/e2e/replay-response.json - -``` - -\--- - -\## Replay Summary - -| Property | Value | - -|----------|-------| - -| Business Transaction ID | b9404bdf-ad2a-4dce-8001-0eacd0974200 | - -| Trust Record Hash | f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4 | - -| Verified | \*\*true\*\* | - -\--- - -\## What This Validates - -Replay confirms that Parmana successfully: - -\- Located the stored Business Transaction. - -\- Reconstructed the corresponding Execution Trust Record. - -\- Recomputed the Trust Record Hash. - -\- Verified that the reconstructed Trust Record matches the stored evidence. - -A successful replay demonstrates deterministic reconstruction of execution evidence without invoking external connectors or re-executing the original business operation. - -\## What Replay Validates - -Replay confirms that Parmana can reconstruct the execution using the stored Business Transaction and its associated evidence. - -A successful replay demonstrates that: - -\- The Business Transaction exists. - -\- The associated Execution Trust Record can be reconstructed. - -\- Stored evidence is sufficient for deterministic replay. - -\- The execution history remains auditable. - -Replay does \*\*not\*\* execute external connectors again. It reconstructs and validates the recorded execution. - -\--- - -\## Replay Lifecycle - -```text - -Business Transaction - - │ - - ▼ - -Load Stored Evidence - - │ - - ▼ - -Reconstruct Execution - - │ - - ▼ - -Replay Result - -``` - -\--- - -\## Files Used - -| File | Purpose | - -|------|---------| - -| `docs/guides/e2e/replay-request.json` | Replay request | - -| `docs/guides/e2e/replay-response.json` | Replay response | - -| `docs/guides/e2e/07-replay-business-transaction.md` | This guide | - -\--- - -\## Next Guide - -Continue with: - -\*\*08 – Query Stored Trust Records\*\* diff --git a/docs/guides/e2e/08-query-trust-record.md b/docs/guides/e2e/08-query-trust-record.md deleted file mode 100644 index 652aab83..00000000 --- a/docs/guides/e2e/08-query-trust-record.md +++ /dev/null @@ -1,203 +0,0 @@ -\# 08 – Query an Execution Trust Record - -This guide demonstrates how to retrieve the complete Execution Trust Record for a previously executed Business Transaction. - -The current implementation retrieves Trust Records using the \*\*Business Transaction ID\*\*. - -\--- - -\## Prerequisites - -Complete: - -\- 01 – Starting the API - -\- 02 – Authentication - -\- 03 – Execute a Business Transaction - -\- 04 – Execute Transaction - -\- 05 – Verify an Execution Trust Record - -\- 06 – Generate an Execution Receipt - -\- 07 – Replay a Business Transaction - -\--- - -\## Business Transaction ID - -Example - -``` - -b9404bdf-ad2a-4dce-8001-0eacd0974200 - -``` - -\--- - -\## Retrieve the Trust Record - -```powershell - -Invoke-RestMethod ` - - -Method GET ` - - -Uri "http://localhost:3000/trust-records/b9404bdf-ad2a-4dce-8001-0eacd0974200" ` - - -Headers @{ - - Authorization = "Bearer my-secret-api-key" - - } | - - ConvertTo-Json -Depth 100 - -``` - -\--- - -\## Successful Response - -The API returned the complete Execution Trust Record. - -The response includes: - -\- Trust Record metadata - -\- Business Transaction - -\- Intent - -\- Authority - -\- Authorization - -\- Policy - -\- Signals - -\- Execution Decision - -\- Connector Evidence - -\- Verifications - -\- Receipts - -\- Cryptographic Signature - -Save the response as: - -``` - -docs/guides/e2e/trust-record-response.json - -``` - -\--- - -\## Execution Trust Record Structure - -``` - -Execution Trust Record - -│ - -├── Transaction - -│ ├── Intent - -│ ├── Authority - -│ ├── Authorization - -│ ├── Policy - -│ └── Signals - -│ - -├── Executions - -│ ├── Policy Decision - -│ └── Connector Evidence - -│ - -├── Verifications - -│ - -├── Receipts - -│ - -└── Cryptographic Signature - -``` - -\--- - -\## What This Demonstrates - -Retrieving the Trust Record confirms that Parmana permanently stores the complete execution history of a Business Transaction. - -The record provides a complete audit trail including: - -\- Original request - -\- Policy evaluation - -\- Authorization - -\- Execution evidence - -\- Verification history - -\- Receipt history - -\- Cryptographic proof - -This allows an auditor to reconstruct and verify the execution without re-running the business operation. - -\--- - -\## Implementation Note - -Although the route is defined as: - -``` - -GET /trust-records/:id - -``` - -the current implementation expects the \*\*Business Transaction ID\*\* rather than the Trust Record ID. - -\--- - -\## Files Used - -| File | Purpose | - -|------|---------| - -| `docs/guides/e2e/trust-record-response.json` | Complete Execution Trust Record | - -| `docs/guides/e2e/08-query-trust-record.md` | This guide | - -\--- - -\## Next Guide - -Continue with: - -\*\*09 – Error Handling and Failure Scenarios\*\* - -This guide demonstrates how Parmana behaves when requests are invalid, unauthorized, malformed, or reference missing transactions. diff --git a/docs/guides/e2e/09-failure-scenarios.md b/docs/guides/e2e/09-failure-scenarios.md deleted file mode 100644 index 2aa9d3c7..00000000 --- a/docs/guides/e2e/09-failure-scenarios.md +++ /dev/null @@ -1,19 +0,0 @@ -\## Authentication Failure Behavior - -Parmana intentionally returns the same response for both missing and invalid credentials. - -| Scenario | HTTP Response | - -|----------|---------------| - -| Missing Bearer token | 401 `authentication required` | - -| Invalid Bearer token | 401 `authentication required` | - -Internally, Parmana records different audit events: - -\- `missing credential` - -\- `invalid credential` - -This prevents information disclosure while preserving complete audit evidence. diff --git a/docs/guides/e2e/10-verification-and-tamper-detection.md b/docs/guides/e2e/10-verification-and-tamper-detection.md deleted file mode 100644 index a50e5bc7..00000000 --- a/docs/guides/e2e/10-verification-and-tamper-detection.md +++ /dev/null @@ -1,93 +0,0 @@ -\# 10 – Verification and Tamper Detection - -Parmana verifies an Execution Trust Record using three independent checks. - -\## Verification Pipeline - -``` - -Execution Trust Record - - │ - - ▼ - -Integrity Check - - │ - - ▼ - -Signature Check - - │ - - ▼ - -Authorization Binding Check - - │ - - ▼ - -Verification Result - -``` - -\## Canonical Trust Record - -Parmana hashes and signs only the immutable portion of the Execution Trust Record. - -Included in the canonical representation: - -\- Trust Record ID - -\- Business Transaction ID - -\- Business Transaction - -\- Overrides - -\- Executions - -\- Created At - -Excluded from the canonical representation: - -\- Verification history - -\- Receipt history - -\- Trust Record hash - -\- Signature - -\- Updated At - -This design allows additional verification events and execution receipts to be appended over time without changing the cryptographically protected content of the Execution Trust Record. As a result, the original evidence remains stable while the audit history can continue to grow. - -\## 1. Integrity Check - -The Trust Record hash is recomputed from the stored record. - -Verification fails if the recomputed hash differs from the stored `trustRecordHash`. - -\## 2. Signature Check - -The Ed25519 signature is verified using the configured public key. - -Verification fails if the signature cannot be validated. - -\## 3. Authorization Binding - -Every execution whose decision is `APPROVED` must contain a valid `authorizationId`. - -Rejected executions are not required to contain an authorization binding. - -\## Failure Reporting - -All verification checks execute independently. - -Verification reports every failed check instead of stopping after the first failure. - -This produces a complete verification result suitable for forensic investigation. diff --git a/docs/guides/e2e/11-cryptographic-architecture.md b/docs/guides/e2e/11-cryptographic-architecture.md deleted file mode 100644 index 1fbafb3b..00000000 --- a/docs/guides/e2e/11-cryptographic-architecture.md +++ /dev/null @@ -1,145 +0,0 @@ -\# 11 – Cryptographic Architecture - -Parmana protects the integrity and authenticity of every Execution Trust Record using deterministic hashing and digital signatures. - -\## Architecture - -``` - -Execution Trust Record - - │ - - ▼ - -Canonical Record - - │ - - ▼ - -Trust Record Hasher - - │ - - ▼ - -SHA-256 Trust Record Hash - - │ - - ▼ - -Ed25519 Digital Signature - - │ - - ▼ - -Execution Trust Record - -``` - -\--- - -\## Canonicalization - -Before hashing or signing, Parmana creates a canonical representation of the Execution Trust Record. - -Only immutable evidence is included. - -Included: - -\- Trust Record ID - -\- Business Transaction ID - -\- Business Transaction - -\- Overrides - -\- Executions - -\- Created At - -Excluded: - -\- Verification history - -\- Receipt history - -\- Trust Record Hash - -\- Signature - -\- Updated At - -This ensures cryptographic evidence remains stable while lifecycle artifacts continue to grow. - -\--- - -\## Trust Record Hash - -Parmana computes a deterministic SHA-256 hash over the canonical Trust Record. - -Every verifier independently recomputes the same hash. - -If any protected field changes, the recomputed hash differs and verification fails. - -\--- - -\## Digital Signature - -After hashing, Parmana signs the canonical Trust Record using an Ed25519 private key. - -Verification uses the corresponding public key. - -If the Trust Record is modified after signing, signature verification fails. - -\--- - -\## Verification - -Verification performs three independent checks: - -1\. Integrity - - - Recompute Trust Record Hash - - - Compare with stored hash - -2\. Authenticity - - - Verify Ed25519 signature - -3\. Authorization Binding - - - Every APPROVED execution must contain an authorizationId - -All checks execute independently and every failure is reported. - -\--- - -\## Replay - -Replay uses the same canonical representation and cryptographic verification logic. - -Because replay reconstructs the same immutable evidence, verification remains deterministic. - -\--- - -\## Security Properties - -Parmana provides: - -\- Deterministic verification - -\- Tamper detection - -\- Cryptographic authenticity - -\- Immutable execution evidence - -\- Replayable audit history - -\- Stable signatures despite new receipts and verification events diff --git a/docs/guides/e2e/12-architecture-overview.md b/docs/guides/e2e/12-architecture-overview.md deleted file mode 100644 index dd0fb0a7..00000000 --- a/docs/guides/e2e/12-architecture-overview.md +++ /dev/null @@ -1,337 +0,0 @@ -\# 12 – Architecture Overview - -This guide provides an end-to-end view of the Parmana Execution Trust Platform, showing how a Business Transaction moves from request to cryptographically verifiable execution evidence. - -\--- - -\## End-to-End Architecture - -```text - - Client / AI Agent - - │ - - ▼ - - Caller Authentication - - │ - - ▼ - - Business Transaction API - - │ - - ▼ - - Business Transaction Validation - - │ - - ▼ - - Runtime Policy Evaluation - - │ - - ▼ - - Execution Authorization - - │ - - ▼ - - Connector / Target System - - │ - - ▼ - - Execution Evidence - - │ - - ▼ - - Execution Trust Record Builder - - │ - - ┌────────────────────┼────────────────────┐ - - ▼ ▼ ▼ - - Verification Receipt Generation Replay - - │ │ │ - - └────────────────────┴────────────────────┘ - - │ - - ▼ - - Immutable Execution Evidence - -``` - -\--- - -\# Execution Lifecycle - -Every Business Transaction follows the same deterministic lifecycle. - -1\. Authenticate the caller. - -2\. Accept the Business Transaction. - -3\. Evaluate the execution policy. - -4\. Execute the approved action. - -5\. Collect execution evidence. - -6\. Build the Execution Trust Record. - -7\. Verify the Trust Record. - -8\. Generate a cryptographic Receipt. - -9\. Store immutable execution evidence. - -10\. Support deterministic replay. - -\--- - -\# Core Components - -| Component | Responsibility | - -|-----------|----------------| - -| Authentication Middleware | Authenticates API callers before execution. | - -| Business Transaction Service | Stores and validates Business Transactions. | - -| Runtime | Executes policy evaluation and connector invocation. | - -| Verification Service | Verifies integrity, signature, and authorization binding. | - -| Receipt Service | Generates cryptographic execution receipts. | - -| Trust Record Repository | Stores immutable Execution Trust Records. | - -| Verification Crypto | Provides hashing, signing, and signature verification. | - -\--- - -\# Cryptographic Pipeline - -```text - -Business Transaction - - │ - - ▼ - -Execution - - │ - - ▼ - -Execution Trust Record - - │ - - ▼ - -Canonical Record - - │ - - ▼ - -SHA-256 Hash - - │ - - ▼ - -Ed25519 Signature - - │ - - ▼ - -Verification - - │ - - ▼ - -Receipt - -``` - -\--- - -\# Verification Pipeline - -Verification performs three independent checks. - -```text - -Execution Trust Record - - │ - - ├── Integrity Check - - │ - - ├── Signature Verification - - │ - - └── Authorization Binding - - │ - - ▼ - - Verification Result - -``` - -All verification checks execute independently. Every detected failure is reported. - -\--- - -\# Replay Pipeline - -Replay does not execute the Business Transaction again. - -Instead it: - -1\. Loads the stored Execution Trust Record. - -2\. Reconstructs the canonical representation. - -3\. Recomputes the Trust Record Hash. - -4\. Verifies the digital signature. - -5\. Returns the verification result. - -\--- - -\# Trust Model - -Parmana establishes trust through: - -\- Authenticated callers - -\- Deterministic execution - -\- Immutable execution evidence - -\- Cryptographic hashing - -\- Digital signatures - -\- Independent verification - -\- Replayable audit evidence - -\--- - -\# End-to-End Flow - -```text - -Caller - - │ - - ▼ - -Authentication - - │ - - ▼ - -Business Transaction - - │ - - ▼ - -Runtime - - │ - - ▼ - -Execution - - │ - - ▼ - -Execution Trust Record - - │ - - ├── Verify - - ├── Receipt - - └── Replay - - │ - - ▼ - -Auditable, Replayable, Cryptographically Verifiable Evidence - -``` - -\--- - -\# Related Guides - -\- 01 – Starting the API - -\- 02 – Authentication - -\- 03 – Execute Request - -\- 04 – Execute Transaction - -\- 05 – Verify an Execution Trust Record - -\- 06 – Generate an Execution Receipt - -\- 07 – Replay a Business Transaction - -\- 08 – Query an Execution Trust Record - -\- 09 – Failure Scenarios - -\- 10 – Verification and Tamper Detection - -\- 11 – Cryptographic Architecture - -\--- - -\# Summary - -Parmana transforms Business Transactions into immutable, cryptographically verifiable Execution Trust Records. - -Every execution can be independently verified, replayed, and audited without relying on the original execution environment, providing deterministic execution evidence suitable for enterprise governance, compliance, and forensic investigation. diff --git a/docs/guides/e2e/13-api-reference.md b/docs/guides/e2e/13-api-reference.md deleted file mode 100644 index 35193dcb..00000000 --- a/docs/guides/e2e/13-api-reference.md +++ /dev/null @@ -1,271 +0,0 @@ -\# 13 – REST API Reference - -This guide summarizes every REST endpoint exposed by the Parmana API. - -\--- - -\# Authentication - -Protected endpoints require: - -``` - -Authorization: Bearer - -``` - -Public endpoints: - -\- GET /health - -\- GET /openapi.yaml - -\- GET /documentation - -\--- - -\# Execute - -\## POST /execute - -Creates and executes a Business Transaction. - -\### Request - -```json - -{ - - "transaction": { ... } - -} - -``` - -\### Success - -Returns: - -\- Business Transaction ID - -\- Trust Record ID - -\- Trust Record Hash - -\- Execution Result - -\--- - -\# Verify - -\## POST /verify - -Verifies an existing Execution Trust Record. - -\### Request - -```json - -{ - - "businessTransactionId": "" - -} - -``` - -\### Success - -Returns: - -\- Verification ID - -\- Verification Status - -\- Verification Message - -\- Trust Record Hash - -\- Verified At - -\--- - -\# Receipt - -\## POST /receipt - -Generates a cryptographic execution receipt. - -\### Request - -```json - -{ - - "businessTransactionId": "" - -} - -``` - -\### Success - -Returns an Execution Receipt. - -\--- - -\# Replay - -\## POST /replay - -Performs deterministic replay verification. - -\### Request - -```json - -{ - - "businessTransactionId": "" - -} - -``` - -\### Success - -Returns: - -\- Business Transaction ID - -\- Trust Record Hash - -\- Verified - -\--- - -\# Trust Records - -\## GET /trust-records/{businessTransactionId} - -Returns the complete Execution Trust Record. - -\### Path Parameter - -``` - -businessTransactionId - -``` - -\### Success - -Returns the complete immutable Trust Record. - -\--- - -\# Health - -\## GET /health - -Returns API health. - -Example: - -```json - -{ - - "status": "UP" - -} - -``` - -\--- - -\# OpenAPI - -\## GET /openapi.yaml - -Returns the OpenAPI specification. - -\--- - -\# Swagger UI - -\## GET /documentation - -Interactive API documentation. - -\--- - -\# Authentication Errors - -All protected endpoints return: - -```json - -{ - - "error": "authentication required" - -} - -``` - -when authentication fails. - -\--- - -\# Validation Errors - -Typical validation failures include: - -\- Missing Business Transaction ID - -\- Invalid UUID - -\- Missing required fields - -\- Execution Trust Record not found - -\--- - -\# Execution Flow - -```text - -POST /execute - - │ - - ▼ - -POST /verify - - │ - - ▼ - -POST /receipt - - │ - - ▼ - -POST /replay - - │ - - ▼ - -GET /trust-records/{businessTransactionId} - -``` - -\--- - -\# Summary - -The Parmana REST API exposes deterministic execution, verification, receipt generation, replay, and immutable Execution Trust Record retrieval through a small set of focused endpoints secured by Bearer authentication. diff --git a/docs/guides/e2e/14-domain-model.md b/docs/guides/e2e/14-domain-model.md deleted file mode 100644 index ef3c46ef..00000000 --- a/docs/guides/e2e/14-domain-model.md +++ /dev/null @@ -1,207 +0,0 @@ -\# 14 – Domain Model - -This guide describes the core domain objects used by the Parmana Execution Trust Platform and how they relate to one another. - -\--- - -\# Domain Overview - -```text - -Business Transaction - - │ - - ▼ - -Policy Evaluation - - │ - - ▼ - -Execution - - │ - - ▼ - -Execution Trust Record - - │ - - ├── Decision - - ├── Authorization - - ├── Execution Evidence - - ├── Verification - - └── Receipt - -``` - -\--- - -\# Business Transaction - -A Business Transaction represents a business operation requested by a caller. - -Examples include: - -\- Vendor creation - -\- Invoice approval - -\- Payment - -\- Purchase Order - -\- Contract approval - -Each transaction receives a unique Business Transaction ID. - -\--- - -\# Execution - -An Execution represents an attempt to perform a business action. - -Each execution records: - -\- Execution ID - -\- Decision - -\- Metadata - -\- Connector information - -\- Evidence - -Multiple executions may belong to a single Business Transaction. - -\--- - -\# Decision - -A Decision represents the policy evaluation result. - -Possible outcomes include: - -\- APPROVED - -\- REJECTED - -Each execution contains exactly one decision. - -\--- - -\# Authorization - -Approved executions must contain an authorization identifier. - -The authorization binding proves that the execution was authorized before it occurred. - -\--- - -\# Execution Trust Record - -The Execution Trust Record is the immutable evidence package produced after execution. - -It contains: - -\- Transaction - -\- Executions - -\- Overrides - -\- Hash - -\- Signature - -\- Verification history - -\- Receipt history - -The Trust Record is the primary audit artifact. - -\--- - -\# Verification - -Verification validates the Trust Record by checking: - -\- Integrity - -\- Signature - -\- Authorization Binding - -Verification results are appended without modifying the protected evidence. - -\--- - -\# Receipt - -A Receipt is cryptographic proof that an Execution Trust Record existed in a verified state. - -Receipts are append-only lifecycle artifacts. - -\--- - -\# Relationships - -```text - -Business Transaction - - │ 1 - - │ - - ▼ - -Execution Trust Record - - │ - - ├── 1..N Executions - - ├── 0..N Verifications - - └── 0..N Receipts - -``` - -\--- - -\# Immutable vs Mutable Data - -Immutable: - -\- Business Transaction - -\- Executions - -\- Decisions - -\- Overrides - -\- Created At - -Mutable Lifecycle Artifacts: - -\- Verification History - -\- Receipt History - -Only immutable fields are included in the canonical representation used for hashing and signing. - -\--- - -\# Summary - -Parmana models execution as a sequence of immutable business evidence protected by cryptographic integrity and enriched over time with verification and receipt lifecycle events. diff --git a/docs/guides/e2e/15-repository-structure.md b/docs/guides/e2e/15-repository-structure.md deleted file mode 100644 index 15bd8490..00000000 --- a/docs/guides/e2e/15-repository-structure.md +++ /dev/null @@ -1,201 +0,0 @@ -\# 15 – Repository Structure - -This guide describes the organization of the Parmana monorepo and the responsibility of each package. - -\--- - -\# Repository Overview - -```text - -parmana-exp/ - -│ - -├── packages/ - -├── docs/ - -├── examples/ - -├── scripts/ - -├── keys/ - -├── .github/ - -└── package.json - -``` - -\--- - -\# packages/ - -The `packages` directory contains the implementation of the Parmana platform. - -| Package | Responsibility | - -|----------|----------------| - -| api | REST API and HTTP endpoints | - -| runtime | Business execution and orchestration | - -| crypto | Hashing, signing, verification, key management | - -| governance | Policy evaluation and execution authorization | - -| execution | Execution pipeline and execution models | - -| verifier | Trust Record verification | - -| sdk-client | TypeScript SDK | - -| audit-db | Audit persistence | - -| core | Shared runtime services | - -\--- - -\# docs/ - -Documentation for developers, operators, and users. - -```text - -docs/ - -├── guides/ - -│ └── e2e/ - -├── architecture/ - -├── api/ - -└── security/ - -``` - -\--- - -\# examples/ - -Reference implementations demonstrating common workflows. - -Examples include: - -\- Hello World - -\- Execute Transaction - -\- Verify - -\- Receipt - -\- Replay - -These examples are intended for learning and testing. - -\--- - -\# scripts/ - -Repository automation. - -Typical scripts include: - -\- Build - -\- Test - -\- OpenAPI generation - -\- Documentation generation - -\- Verification utilities - -\- CI validation - -\--- - -\# keys/ - -Development cryptographic keys used for local testing. - -Production deployments should replace these with secure key management solutions. - -\--- - -\# .github/ - -GitHub Actions workflows for: - -\- Build - -\- Test - -\- Lint - -\- Documentation validation - -\- Release automation - -\--- - -\# Package Relationships - -```text - - API - - │ - - ▼ - - Runtime - - │ - - ┌─────────┼─────────┐ - - ▼ ▼ ▼ - -Crypto Governance Execution - - │ - - ▼ - - Verifier - - │ - - ▼ - - Audit DB - -``` - -\--- - -\# Dependency Principles - -The repository follows these principles: - -\- Single responsibility per package - -\- Shared models through common libraries - -\- Deterministic runtime behavior - -\- Clear separation between API, runtime, cryptography, and persistence - -\- Reusable packages for SDKs and tooling - -\--- - -\# Summary - -The Parmana monorepo separates HTTP APIs, runtime execution, cryptographic services, governance logic, verification, and persistence into focused packages, making the platform easier to understand, maintain, and extend. diff --git a/docs/guides/e2e/16-runtime-architecture.md b/docs/guides/e2e/16-runtime-architecture.md deleted file mode 100644 index dd13f21d..00000000 --- a/docs/guides/e2e/16-runtime-architecture.md +++ /dev/null @@ -1,245 +0,0 @@ -\# 16 – Runtime Architecture - -This guide explains how the Parmana Runtime processes Business Transactions from API request through execution, evidence collection, and Trust Record generation. - -\--- - -\# Runtime Overview - -The Runtime is responsible for executing Business Transactions in a deterministic and auditable manner. - -Its responsibilities include: - -\- Processing Business Transactions - -\- Evaluating execution policies - -\- Authorizing execution - -\- Collecting execution evidence - -\- Building Execution Trust Records - -\- Persisting immutable evidence - -\--- - -\# Runtime Pipeline - -```text - -Business Transaction - - │ - - ▼ - -Input Validation - - │ - - ▼ - -Policy Evaluation - - │ - - ▼ - -Execution Decision - - │ - - ▼ - -Execution Authorization - - │ - - ▼ - -Connector Invocation - - │ - - ▼ - -Evidence Collection - - │ - - ▼ - -Execution Trust Record Builder - - │ - - ▼ - -Persistence - -``` - -\--- - -\# Step 1 – Business Transaction - -The Runtime receives a validated Business Transaction from the API layer. - -The transaction includes the information required to evaluate and perform the requested business operation. - -\--- - -\# Step 2 – Policy Evaluation - -The Runtime evaluates the transaction against execution policies. - -Possible outcomes include: - -\- APPROVED - -\- REJECTED - -The decision determines whether execution continues. - -\--- - -\# Step 3 – Execution Authorization - -For approved transactions, the Runtime records an authorization that binds the execution to the policy decision. - -This authorization is later verified during Trust Record verification. - -\--- - -\# Step 4 – Connector Invocation - -The Runtime invokes the appropriate connector or target system to perform the approved business action. - -Examples include: - -\- Payment systems - -\- ERP platforms - -\- Procurement systems - -\- Identity services - -Execution results are captured as evidence. - -\--- - -\# Step 5 – Evidence Collection - -The Runtime records execution evidence including: - -\- Execution identifiers - -\- Decision outcome - -\- Connector metadata - -\- Authorization reference - -\- Execution timestamps - -This evidence forms the basis of the Execution Trust Record. - -\--- - -\# Step 6 – Execution Trust Record - -The Runtime assembles all immutable execution evidence into an Execution Trust Record. - -The Trust Record is then: - -\- Canonicalized - -\- Hashed - -\- Digitally signed - -\- Stored - -\--- - -\# Runtime Components - -| Component | Responsibility | - -|-----------|----------------| - -| ExecutionTrustApplication | Coordinates runtime operations | - -| VerificationService | Verifies Trust Records | - -| VerificationCrypto | Hashing and signature verification | - -| TrustRecordRepository | Stores immutable Trust Records | - -| Receipt Service | Generates execution receipts | - -\--- - -\# Design Principles - -The Runtime is designed around the following principles: - -\- Deterministic execution - -\- Immutable execution evidence - -\- Cryptographic integrity - -\- Independent verification - -\- Replayability - -\- Separation of concerns - -\--- - -\# Relationship to Other Components - -```text - -API - - │ - - ▼ - -Runtime - - │ - - ├── Policy Evaluation - - ├── Execution - - ├── Evidence Collection - - ├── Trust Record Builder - - └── Persistence - - │ - - ▼ - -Verification - - │ - - ▼ - -Receipt - -``` - -\--- - -\# Summary - -The Parmana Runtime transforms Business Transactions into immutable Execution Trust Records through deterministic policy evaluation, controlled execution, evidence collection, and cryptographic protection. It forms the operational core of the Execution Trust Platform. diff --git a/docs/guides/e2e/17-security-architecture.md b/docs/guides/e2e/17-security-architecture.md deleted file mode 100644 index 6da2102e..00000000 --- a/docs/guides/e2e/17-security-architecture.md +++ /dev/null @@ -1,227 +0,0 @@ -\# 17 – Security Architecture - -This guide describes the security architecture of the Parmana Execution Trust Platform and the mechanisms used to protect execution, evidence, and verification. - -\--- - -\# Security Objectives - -Parmana is designed to ensure that: - -\- Only authenticated callers can request execution. - -\- Execution decisions are deterministic. - -\- Approved executions are cryptographically bound to their authorization. - -\- Execution evidence cannot be modified without detection. - -\- Verification is independent and reproducible. - -\- Audit evidence remains immutable. - -\--- - -\# Security Layers - -```text - - Client - - │ - - ▼ - - Bearer Authentication - - │ - - ▼ - - Request Validation - - │ - - ▼ - - Policy Evaluation - - │ - - ▼ - - Execution Authorization - - │ - - ▼ - - Connector Invocation - - │ - - ▼ - - Evidence Collection - - │ - - ▼ - - Canonical Trust Record - - │ - - ┌────────┴────────┐ - - ▼ ▼ - - SHA-256 Hash Ed25519 Signature - - │ │ - - └────────┬────────┘ - - ▼ - - Immutable Trust Record - - │ - - ▼ - - Verification - -``` - -\--- - -\# Authentication - -Protected API endpoints require a valid Bearer API key. - -Public endpoints include: - -\- GET /health - -\- GET /openapi.yaml - -\- GET /documentation - -Unauthorized requests receive: - -```json - -{ - - "error": "authentication required" - -} - -``` - -\--- - -\# Deterministic Policy Evaluation - -Execution decisions are deterministic. - -Given the same inputs and policy version, the Runtime always produces the same decision. - -\--- - -\# Authorization Binding - -Every approved execution must reference an authorization identifier. - -During verification, Parmana validates that each approved execution includes this binding. - -Missing authorization bindings cause verification to fail. - -\--- - -\# Integrity Protection - -Parmana computes a SHA-256 hash over the canonical Execution Trust Record. - -Any modification to protected data changes the hash and is detected during verification. - -\--- - -\# Digital Signatures - -The canonical Trust Record is signed using an Ed25519 private key. - -Verification uses the corresponding public key to confirm authenticity. - -\--- - -\# Immutable Audit Evidence - -The following data is immutable: - -\- Business Transaction - -\- Executions - -\- Decisions - -\- Overrides - -\- Created At - -Verification history and receipts are append-only lifecycle artifacts and are intentionally excluded from the canonical representation. - -\--- - -\# Replay Verification - -Replay reconstructs the canonical Trust Record and performs the same integrity and signature checks without re-executing the business action. - -\--- - -\# Security Properties - -Parmana provides: - -\- Authenticated execution requests - -\- Deterministic policy decisions - -\- Authorization binding - -\- Tamper detection - -\- Digital authenticity - -\- Immutable execution evidence - -\- Independent verification - -\- Deterministic replay - -\--- - -\# Threat Mitigations - -| Threat | Mitigation | - -|---------|------------| - -| Unauthorized API access | Bearer authentication | - -| Tampering with execution evidence | SHA-256 integrity verification | - -| Forged Trust Records | Ed25519 digital signatures | - -| Missing execution authorization | Authorization binding verification | - -| Undetected data modification | Independent verification service | - -| Loss of auditability | Immutable Execution Trust Records | - -\--- - -\# Summary - -Parmana combines authenticated access, deterministic execution, cryptographic integrity, digital signatures, authorization binding, and immutable audit evidence to ensure that every business execution can be independently verified and trusted. diff --git a/docs/guides/e2e/18-key-management.md b/docs/guides/e2e/18-key-management.md deleted file mode 100644 index b712e5c4..00000000 --- a/docs/guides/e2e/18-key-management.md +++ /dev/null @@ -1,261 +0,0 @@ -\# 18 – Key Management - -This guide describes how Parmana manages cryptographic keys used to sign and verify Execution Trust Records. - -\--- - -\# Overview - -Parmana uses asymmetric cryptography to provide authenticity for Execution Trust Records. - -The Runtime signs Trust Records using a private key, while verification uses the corresponding public key. - -``` - -Private Key - - │ - - ▼ - -Sign Trust Record - - │ - - ▼ - -Execution Trust Record - - │ - - ▼ - -Public Key - - │ - - ▼ - -Verify Signature - -``` - -\--- - -\# Key Components - -The cryptographic implementation consists of: - -| Component | Responsibility | - -|-----------|----------------| - -| FileKeyProvider | Loads cryptographic keys | - -| DEFAULT\_KEY\_ID | Default signing key identifier | - -| ArtifactSigner | Creates digital signatures | - -| SignatureVerifier | Verifies signatures | - -| VerificationCrypto | Coordinates hashing, signing, and verification | - -\--- - -\# Signing Process - -During execution: - -1\. Create the canonical Trust Record. - -2\. Compute the Trust Record hash. - -3\. Load the private key. - -4\. Sign the canonical Trust Record. - -5\. Store the signature with the Trust Record. - -``` - -Canonical Record - - │ - - ▼ - -Private Key - - │ - - ▼ - -Ed25519 Signature - - │ - - ▼ - -Execution Trust Record - -``` - -\--- - -\# Verification Process - -During verification: - -1\. Load the public key referenced by the Trust Record. - -2\. Reconstruct the canonical Trust Record. - -3\. Verify the digital signature. - -4\. Report the verification result. - -``` - -Execution Trust Record - - │ - - ▼ - -Canonical Record - - │ - - ▼ - -Public Key - - │ - - ▼ - -Signature Verification - -``` - -\--- - -\# Key Identification - -Every signature includes: - -\- Algorithm - -\- Key ID - -\- Signature Value - -\- Signed Timestamp - -The `keyId` identifies which public key should be used during verification. - -\--- - -\# Development Keys - -The development environment uses file-based keys loaded by the `FileKeyProvider`. - -These keys are intended for: - -\- Local development - -\- Testing - -\- Continuous Integration - -They should not be used in production. - -\--- - -\# Production Recommendations - -Production deployments should replace the file-based key provider with a secure key management solution such as: - -\- Hardware Security Module (HSM) - -\- Cloud Key Management Service (KMS) - -\- Enterprise secrets management platform - -The Runtime should never expose private keys outside the signing process. - -\--- - -\# Key Rotation - -Key rotation should follow these principles: - -\- Generate a new signing key pair. - -\- Assign a new `keyId`. - -\- Use the new private key for future signatures. - -\- Retain previous public keys to verify historical Trust Records. - -Historical signatures remain valid because each Trust Record references the `keyId` that was used when it was signed. - -\--- - -\# Security Considerations - -To protect signing keys: - -\- Restrict filesystem access. - -\- Limit key usage to the signing service. - -\- Monitor key access. - -\- Rotate keys periodically. - -\- Back up keys securely. - -\- Protect production keys with HSM or KMS. - -\--- - -\# Trust Model - -```text - -Private Key - - │ - - ▼ - -Sign - - │ - - ▼ - -Execution Trust Record - - │ - - ▼ - -Public Key - - │ - - ▼ - -Verify - -``` - -The Runtime requires the private key only for signing. Verification requires only the corresponding public key. - -\--- - -\# Summary - -Parmana separates signing and verification through asymmetric cryptography. Trust Records are signed with a private key and verified with the matching public key, enabling independent verification while supporting secure key rotation and long-term validation of historical execution evidence. diff --git a/docs/guides/e2e/19-deployment-guide.md b/docs/guides/e2e/19-deployment-guide.md deleted file mode 100644 index 86974175..00000000 --- a/docs/guides/e2e/19-deployment-guide.md +++ /dev/null @@ -1,249 +0,0 @@ -\# 19 – Deployment Guide - -This guide describes how to deploy the Parmana Execution Trust Platform in development and production environments. - -\--- - -\# Deployment Architecture - -```text - - Client / AI Agent - - │ - - ▼ - - Load Balancer - - │ - - ▼ - - Parmana API - - │ - - ▼ - - Parmana Runtime - - │ - - ┌───────────────┼───────────────┐ - - ▼ ▼ ▼ - - Governance Cryptography Connectors - - │ │ │ - - └───────────────┼───────────────┘ - - ▼ - - Trust Record Repository - - │ - - ▼ - - Audit Database - -``` - -\--- - -\# Deployment Components - -| Component | Purpose | - -|-----------|---------| - -| API | Exposes REST endpoints | - -| Runtime | Executes Business Transactions | - -| Governance | Evaluates execution policies | - -| Crypto | Hashes, signs, and verifies Trust Records | - -| Repository | Stores immutable Trust Records | - -| Database | Persists execution evidence | - -| Connectors | Integrate with external business systems | - -\--- - -\# Environment Configuration - -Typical configuration includes: - -\- API host and port - -\- Database connection - -\- Cryptographic key location - -\- Authentication configuration - -\- Logging level - -Store sensitive values using environment variables or a secure secrets manager. - -\--- - -\# Development Deployment - -Development deployments typically use: - -\- File-based keys - -\- Local database - -\- Local API server - -\- Sample connectors - -This configuration is intended for development and testing only. - -\--- - -\# Production Deployment - -Production deployments should use: - -\- HTTPS termination - -\- Managed database - -\- Secure key management (HSM or KMS) - -\- Centralized logging - -\- Monitoring and alerting - -\- Automated backups - -\- High availability - -\--- - -\# High Availability - -For resilient deployments: - -\- Run multiple API instances. - -\- Use a load balancer. - -\- Replicate the database. - -\- Monitor service health. - -\- Automate failover where appropriate. - -\--- - -\# Logging and Monitoring - -Monitor: - -\- API availability - -\- Authentication failures - -\- Execution throughput - -\- Verification failures - -\- Receipt generation - -\- Replay requests - -\- Database health - -Logs should include correlation identifiers such as the Business Transaction ID. - -\--- - -\# Backup and Recovery - -Regularly back up: - -\- Trust Record repository - -\- Audit database - -\- Configuration - -\- Public keys - -Protect private signing keys using secure key management and recovery procedures. - -\--- - -\# Security Checklist - -Before production: - -\- Enable HTTPS. - -\- Protect private keys with HSM or KMS. - -\- Rotate signing keys. - -\- Restrict API access. - -\- Secure environment variables. - -\- Enable monitoring and alerting. - -\- Verify backup procedures. - -\--- - -\# Deployment Workflow - -```text - -Configure Environment - - │ - - ▼ - -Deploy API - - │ - - ▼ - -Deploy Runtime - - │ - - ▼ - -Configure Keys - - │ - - ▼ - -Run Health Checks - - │ - - ▼ - -Verify Deployment - -``` - -\--- - -\# Summary - -A production Parmana deployment combines secure configuration, protected cryptographic keys, reliable persistence, monitoring, and high availability to provide deterministic and verifiable execution authorization for enterprise workloads. diff --git a/docs/guides/e2e/20-production-operations.md b/docs/guides/e2e/20-production-operations.md deleted file mode 100644 index 224a705a..00000000 --- a/docs/guides/e2e/20-production-operations.md +++ /dev/null @@ -1,337 +0,0 @@ -\# 20 – Production Operations - -This guide describes the operational practices for running the Parmana Execution Trust Platform in production. - -\--- - -\# Overview - -Production operations focus on maintaining the availability, integrity, security, and auditability of the platform. - -Operational responsibilities include: - -\- Monitoring - -\- Logging - -\- Incident response - -\- Backup and recovery - -\- Key rotation - -\- Software upgrades - -\- Capacity planning - -\--- - -\# Operational Architecture - -```text - - Monitoring - - │ - - ▼ - - Parmana API - - │ - - ▼ - - Parmana Runtime - - │ - - ┌───────────────┼───────────────┐ - - ▼ ▼ ▼ - - Trust Records Verification Receipts - - │ - - ▼ - - Audit Database - - │ - - ▼ - - Dashboards \& Alerts - -``` - -\--- - -\# Health Monitoring - -Monitor the health of: - -\- API availability - -\- Runtime services - -\- Database connectivity - -\- Trust Record repository - -\- Connector availability - -\- Cryptographic services - -Health checks should be integrated with enterprise monitoring platforms. - -\--- - -\# Metrics - -Recommended metrics include: - -| Metric | Description | - -|---------|-------------| - -| API Requests | Total REST API requests | - -| Authentication Failures | Unauthorized access attempts | - -| Executions | Successful Business Transactions | - -| Verification Success Rate | Percentage of successful verifications | - -| Verification Failures | Failed integrity or signature checks | - -| Receipt Generation | Generated execution receipts | - -| Replay Requests | Replay verification operations | - -| Database Latency | Repository response time | - -\--- - -\# Logging - -Application logs should include: - -\- Business Transaction ID - -\- Trust Record ID - -\- Verification ID - -\- Receipt ID - -\- Request correlation ID - -\- Timestamp - -\- Service name - -\- Log level - -Avoid logging sensitive business data or private cryptographic material. - -\--- - -\# Alerting - -Configure alerts for: - -\- API downtime - -\- Repeated authentication failures - -\- Verification failures - -\- Database connectivity issues - -\- Connector failures - -\- High error rates - -\- Resource exhaustion - -\--- - -\# Incident Response - -When an incident occurs: - -1\. Identify the affected component. - -2\. Review application logs. - -3\. Verify system health. - -4\. Inspect related Business Transaction IDs. - -5\. Validate affected Execution Trust Records. - -6\. Determine root cause. - -7\. Restore service. - -8\. Document the incident. - -\--- - -\# Backup Strategy - -Regularly back up: - -\- Trust Record repository - -\- Audit database - -\- Configuration - -\- Public keys - -\- Operational documentation - -Private signing keys should follow the organization's secure backup and recovery procedures. - -\--- - -\# Key Rotation - -When rotating signing keys: - -1\. Generate a new key pair. - -2\. Assign a new key identifier. - -3\. Update signing configuration. - -4\. Retain previous public keys. - -5\. Verify new signatures. - -6\. Monitor verification results. - -Historical Trust Records remain verifiable because they reference the key used when they were signed. - -\--- - -\# Software Upgrades - -Before upgrading: - -\- Review release notes. - -\- Back up critical data. - -\- Test in a staging environment. - -\- Validate API compatibility. - -\- Verify cryptographic functionality. - -\- Confirm successful deployment with health checks. - -\--- - -\# Capacity Planning - -Monitor: - -\- Request throughput - -\- Concurrent executions - -\- Database growth - -\- Storage consumption - -\- CPU utilization - -\- Memory utilization - -\- Connector response times - -Scale infrastructure proactively based on observed trends. - -\--- - -\# Operational Checklist - -Daily: - -\- Review health dashboards. - -\- Check alert status. - -\- Verify backup completion. - -\- Review authentication failures. - -Weekly: - -\- Review execution metrics. - -\- Analyze verification failures. - -\- Validate replay functionality. - -\- Inspect storage growth. - -Monthly: - -\- Review security configuration. - -\- Audit operational procedures. - -\- Test backup restoration. - -\- Review capacity forecasts. - -\--- - -\# Operational Workflow - -```text - -Monitor - - │ - - ▼ - -Detect Issue - - │ - - ▼ - -Investigate - - │ - - ▼ - -Mitigate - - │ - - ▼ - -Verify Recovery - - │ - - ▼ - -Document - -``` - -\--- - -\# Summary - -Operating Parmana in production requires continuous monitoring, structured incident response, secure key management, reliable backup procedures, and regular operational reviews. These practices help ensure that execution authorization remains available, trustworthy, and auditable throughout the platform's lifecycle. diff --git a/docs/guides/e2e/21-troubleshooting.md b/docs/guides/e2e/21-troubleshooting.md deleted file mode 100644 index 1646f5db..00000000 --- a/docs/guides/e2e/21-troubleshooting.md +++ /dev/null @@ -1,325 +0,0 @@ -\# 21 – Troubleshooting - -This guide describes common issues, their possible causes, and recommended resolutions when working with the Parmana Execution Trust Platform. - -\--- - -\# Overview - -Troubleshooting should begin by identifying: - -\- The affected component - -\- The Business Transaction ID - -\- The Trust Record ID (if available) - -\- The API endpoint involved - -\- Relevant application logs - -\--- - -\# Troubleshooting Workflow - -```text - -Problem - - │ - - ▼ - -Identify Component - - │ - - ▼ - -Collect Logs - - │ - - ▼ - -Review Trust Record - - │ - - ▼ - -Verify Cryptographic Evidence - - │ - - ▼ - -Resolve Issue - - │ - - ▼ - -Retest - -``` - -\--- - -\# Authentication Issues - -\## Symptom - -```json - -{ - - "error": "authentication required" - -} - -``` - -\### Possible Causes - -\- Missing Bearer token - -\- Invalid API key - -\- Incorrect Authorization header - -\- API key not configured - -\### Resolution - -\- Verify the `Authorization: Bearer ` header. - -\- Confirm the API key exists in the configured key list. - -\- Restart the service if configuration has changed. - -\--- - -\# Trust Record Not Found - -\## Symptom - -```json - -{ - - "error": "Execution Trust Record not found." - -} - -``` - -\### Possible Causes - -\- Incorrect Business Transaction ID - -\- Transaction was never executed - -\- Repository misconfiguration - -\### Resolution - -\- Confirm the Business Transaction ID. - -\- Verify the transaction completed successfully. - -\- Check repository connectivity. - -\--- - -\# Verification Failure - -\## Possible Causes - -\- Trust Record was modified - -\- Signature verification failed - -\- Authorization binding missing - -\### Resolution - -\- Inspect the verification message. - -\- Compare the recomputed Trust Record hash. - -\- Verify the public key matches the `keyId`. - -\- Confirm every approved execution contains an `authorizationId`. - -\--- - -\# Replay Failure - -\## Possible Causes - -\- Missing Trust Record - -\- Corrupted stored data - -\- Hash mismatch - -\- Invalid signature - -\### Resolution - -\- Retrieve the Trust Record. - -\- Run the `/verify` endpoint. - -\- Review the verification results. - -\--- - -\# Receipt Generation Failure - -\## Possible Causes - -\- Missing Business Transaction - -\- Trust Record unavailable - -\- Repository failure - -\### Resolution - -\- Verify the Business Transaction ID. - -\- Ensure execution completed successfully. - -\- Check repository availability. - -\--- - -\# Connector Failures - -\## Symptoms - -\- Execution timeout - -\- Connector unavailable - -\- Unexpected connector response - -\### Resolution - -\- Verify connector configuration. - -\- Check target system availability. - -\- Review connector logs. - -\--- - -\# Database Issues - -\## Symptoms - -\- Slow responses - -\- Repository errors - -\- Missing records - -\### Resolution - -\- Verify database connectivity. - -\- Check storage capacity. - -\- Review database logs. - -\- Restore from backup if necessary. - -\--- - -\# Signature Verification Failures - -\## Possible Causes - -\- Incorrect public key - -\- Corrupted Trust Record - -\- Incorrect `keyId` - -\- Data tampering - -\### Resolution - -\- Verify the configured public key. - -\- Confirm the Trust Record has not been modified. - -\- Validate key configuration. - -\--- - -\# Performance Issues - -\## Symptoms - -\- High API latency - -\- Slow verification - -\- Delayed execution - -\### Resolution - -\- Review system metrics. - -\- Check database performance. - -\- Monitor connector response times. - -\- Scale infrastructure if required. - -\--- - -\# Diagnostic Checklist - -Before reporting an issue, collect: - -\- Business Transaction ID - -\- Trust Record ID - -\- Verification ID (if applicable) - -\- API endpoint - -\- Request payload - -\- Response payload - -\- Timestamp - -\- Relevant log entries - -\--- - -\# Escalation Checklist - -If the issue cannot be resolved: - -\- Preserve logs. - -\- Preserve the affected Trust Record. - -\- Record the verification result. - -\- Capture environment details. - -\- Document reproduction steps. - -\--- - -\# Summary - -Most operational issues can be diagnosed by tracing the Business Transaction through execution, verification, and the associated Execution Trust Record. The platform's immutable evidence and deterministic verification simplify root cause analysis and support reliable incident investigation. diff --git a/docs/guides/e2e/22-glossary.md b/docs/guides/e2e/22-glossary.md deleted file mode 100644 index 05863426..00000000 --- a/docs/guides/e2e/22-glossary.md +++ /dev/null @@ -1,207 +0,0 @@ -\# 22 – Glossary - -This glossary defines the core terminology used throughout the Parmana Execution Trust Platform documentation. - -\--- - -\# API - -The REST interface exposed by Parmana for submitting Business Transactions, verifying Execution Trust Records, generating receipts, replaying transactions, and querying stored evidence. - -\--- - -\# Authorization Binding - -The association between an approved execution and the authorization that permitted it to occur. - -Every approved execution must contain an `authorizationId`, which is verified during Trust Record verification. - -\--- - -\# Business Transaction - -A request to perform a business operation. - -Examples include: - -\- Creating a vendor - -\- Approving an invoice - -\- Processing a payment - -\- Creating a purchase order - -\- Approving a contract - -Each Business Transaction is assigned a unique Business Transaction ID. - -\--- - -\# Business Transaction ID - -The unique identifier assigned to a Business Transaction. - -This identifier is used throughout the platform to retrieve Trust Records, perform verification, generate receipts, and execute replay operations. - -\--- - -\# Canonical Record - -The immutable representation of an Execution Trust Record used for cryptographic hashing and digital signatures. - -Lifecycle artifacts such as verification history and receipts are intentionally excluded from the canonical representation. - -\--- - -\# Connector - -A component responsible for executing approved business actions against external systems such as ERP platforms, payment providers, procurement systems, or identity services. - -\--- - -\# Decision - -The result of policy evaluation for a Business Transaction. - -Typical outcomes include: - -\- APPROVED - -\- REJECTED - -\--- - -\# Deterministic Verification - -A verification process that always produces the same result when evaluating the same immutable Execution Trust Record. - -\--- - -\# Digital Signature - -A cryptographic signature created using the Runtime's private key and verified using the corresponding public key. - -Digital signatures provide authenticity and detect unauthorized modifications. - -\--- - -\# Execution - -An attempt to perform a business action. - -Each execution records its decision, metadata, evidence, and authorization information. - -\--- - -\# Execution Evidence - -Immutable information collected during execution, including identifiers, decisions, metadata, and timestamps. - -Execution evidence forms the basis of the Execution Trust Record. - -\--- - -\# Execution Trust Record - -The immutable audit artifact produced after execution. - -It contains the evidence required to independently verify that a Business Transaction was executed as authorized. - -\--- - -\# Integrity - -The property that ensures protected execution evidence has not been modified since it was created. - -Integrity is validated by recomputing the Trust Record hash during verification. - -\--- - -\# Key ID - -The identifier associated with the cryptographic key used to sign an Execution Trust Record. - -It enables verifiers to select the correct public key. - -\--- - -\# Policy Evaluation - -The process of evaluating a Business Transaction against governance policies to determine whether execution is approved or rejected. - -\--- - -\# Receipt - -A cryptographic record confirming that an Execution Trust Record existed in a verified state at a specific point in time. - -Receipts are append-only lifecycle artifacts. - -\--- - -\# Replay - -The process of reconstructing and re-verifying a previously stored Execution Trust Record without executing the original business operation again. - -Replay validates that stored evidence remains authentic and unchanged. - -\--- - -\# Runtime - -The core execution engine responsible for policy evaluation, authorization, execution orchestration, evidence collection, and Trust Record generation. - -\--- - -\# Signature Verification - -The process of validating a digital signature using the corresponding public key to confirm the authenticity of an Execution Trust Record. - -\--- - -\# Trust Record Hash - -A deterministic cryptographic hash computed over the canonical representation of an Execution Trust Record. - -Any modification to protected data changes the hash and causes integrity verification to fail. - -\--- - -\# Trust Record ID - -The unique identifier assigned to an Execution Trust Record. - -It identifies the audit artifact associated with a Business Transaction. - -\--- - -\# Verification - -The process of validating an Execution Trust Record by performing integrity, signature, and authorization binding checks. - -\--- - -\# Verification ID - -The unique identifier assigned to a verification event. - -Each verification operation generates a new Verification ID and records the verification result. - -\--- - -\# Verification Status - -The outcome of a verification operation. - -Typical values include: - -\- VERIFIED - -\- FAILED - -\--- - -\# Summary - -Understanding these terms provides a consistent vocabulary for discussing execution authorization, cryptographic verification, immutable audit evidence, and the runtime architecture of the Parmana Execution Trust Platform. diff --git a/docs/guides/e2e/23-faq.md b/docs/guides/e2e/23-faq.md deleted file mode 100644 index fdbb8742..00000000 --- a/docs/guides/e2e/23-faq.md +++ /dev/null @@ -1,189 +0,0 @@ -\# 23 – Frequently Asked Questions (FAQ) - -This guide answers common questions about the Parmana Execution Trust Platform. - -\--- - -\# What is Parmana? - -Parmana is an Execution Trust Platform that transforms Business Transactions into cryptographically verifiable Execution Trust Records. - -\--- - -\# What problem does Parmana solve? - -Parmana enables organizations to independently verify that business actions were executed as authorized and that the underlying execution evidence has not been modified. - -\--- - -\# What is an Execution Trust Record? - -An Execution Trust Record is the immutable audit artifact produced after execution. - -It contains the evidence required to verify a Business Transaction. - -\--- - -\# What is verified? - -Verification performs three independent checks: - -\- Integrity - -\- Digital Signature - -\- Authorization Binding - -All checks execute independently and every failure is reported. - -\--- - -\# Why is the Trust Record hashed? - -Hashing provides tamper detection. - -Any modification to protected execution evidence changes the Trust Record hash. - -\--- - -\# Why is the Trust Record signed? - -Digital signatures prove that the Trust Record was produced by a trusted signer and has not been modified after signing. - -\--- - -\# What is the Canonical Record? - -The Canonical Record is the immutable subset of the Execution Trust Record used for hashing and signing. - -Lifecycle artifacts such as verification history and receipts are intentionally excluded. - -\--- - -\# Why are verification history and receipts excluded from signing? - -Verification events and receipts are append-only lifecycle artifacts. - -Excluding them allows the audit history to grow without changing the cryptographically protected execution evidence. - -\--- - -\# What is Replay? - -Replay reconstructs the canonical Trust Record and performs deterministic verification without executing the original business operation again. - -\--- - -\# Does Replay execute the Business Transaction again? - -No. - -Replay only verifies the stored execution evidence. - -No external business action is performed. - -\--- - -\# What is Authorization Binding? - -Authorization Binding ensures that every approved execution references the authorization that permitted it. - -Verification fails if an approved execution lacks an `authorizationId`. - -\--- - -\# How are API endpoints protected? - -Protected endpoints require a valid Bearer API key. - -Public endpoints are: - -\- GET /health - -\- GET /openapi.yaml - -\- GET /documentation - -\--- - -\# Can historical Trust Records still be verified after key rotation? - -Yes. - -Each signature includes a `keyId`, allowing historical Trust Records to be verified using the corresponding public key. - -\--- - -\# Can verification history grow over time? - -Yes. - -Verification events are append-only lifecycle artifacts and do not modify the signed execution evidence. - -\--- - -\# Can multiple receipts exist? - -Yes. - -Receipts are append-only and multiple receipts may exist for the same Execution Trust Record. - -\--- - -\# Is verification deterministic? - -Yes. - -The same immutable Execution Trust Record always produces the same verification result. - -\--- - -\# What happens if the Trust Record is modified? - -Verification fails because: - -\- The recomputed Trust Record hash changes. - -\- The digital signature no longer validates. - -\--- - -\# Does Parmana require the original execution environment for verification? - -No. - -Verification uses the stored immutable evidence and the corresponding public key. - -\--- - -\# How can I inspect the API? - -Parmana provides: - -\- OpenAPI specification (`GET /openapi.yaml`) - -\- Interactive Swagger UI (`GET /documentation`) - -\--- - -\# Where should I start? - -Recommended reading order: - -1\. Architecture Overview - -2\. Runtime Architecture - -3\. Cryptographic Architecture - -4\. Security Architecture - -5\. End-to-End Guides - -6\. API Reference - -\--- - -\# Summary - -Parmana provides deterministic execution evidence, independent verification, cryptographic integrity, and replayable audit capabilities. These properties enable organizations to verify what was executed, how it was authorized, and whether the execution evidence has remained unchanged. diff --git a/docs/guides/e2e/24-design-decisions.md b/docs/guides/e2e/24-design-decisions.md deleted file mode 100644 index a54ccd2d..00000000 --- a/docs/guides/e2e/24-design-decisions.md +++ /dev/null @@ -1,325 +0,0 @@ -\# 24 – Design Decisions - -This document records the key architectural decisions behind the Parmana Execution Trust Platform, the reasoning for each decision, and the associated trade-offs. - -\--- - -\# Purpose - -Architectural decisions are difficult to change after a platform is adopted. - -This document explains why Parmana was designed the way it is and serves as a reference for future contributors and maintainers. - -\--- - -\# Decision 1 – Deterministic Execution - -\## Decision - -Execution decisions must be deterministic. - -The same Business Transaction, policy version, and execution inputs must always produce the same decision. - -\## Rationale - -Deterministic behavior enables: - -\- Independent verification - -\- Reliable replay - -\- Predictable auditing - -\- Consistent compliance - -\## Trade-offs - -Pros - -\- Reproducible decisions - -\- Easier testing - -\- Easier auditing - -Cons - -\- Reduced flexibility for non-deterministic workflows - -\- Greater emphasis on controlled inputs - -\--- - -\# Decision 2 – Immutable Execution Evidence - -\## Decision - -Execution evidence is immutable after it is created. - -\## Rationale - -Audit evidence must remain stable throughout its lifetime. - -Verification relies on immutable execution data. - -\## Trade-offs - -Pros - -\- Strong audit guarantees - -\- Reliable replay - -\- Simplified verification - -Cons - -\- Corrections require new records rather than modifying existing ones - -\--- - -\# Decision 3 – Canonical Representation - -\## Decision - -Only immutable fields are included in the canonical representation used for hashing and signing. - -Included: - -\- Trust Record ID - -\- Business Transaction ID - -\- Transaction - -\- Overrides - -\- Executions - -\- Created At - -Excluded: - -\- Verification history - -\- Receipt history - -\- Signature - -\- Trust Record Hash - -\- Updated At - -\## Rationale - -Lifecycle artifacts grow over time and should not invalidate existing signatures. - -\## Trade-offs - -Pros - -\- Stable signatures - -\- Append-only audit history - -Cons - -\- Developers must clearly distinguish immutable evidence from lifecycle metadata - -\--- - -\# Decision 4 – Asymmetric Cryptography - -\## Decision - -Use asymmetric cryptography for signing and verification. - -\## Rationale - -Only the Runtime requires the private signing key. - -Verification requires only the corresponding public key. - -\## Trade-offs - -Pros - -\- Independent verification - -\- Secure key separation - -\- Supports key rotation - -Cons - -\- Additional operational requirements for key management - -\--- - -\# Decision 5 – Independent Verification - -\## Decision - -Verification is performed independently of execution. - -\## Rationale - -Verification should validate stored evidence rather than trust execution results. - -\## Trade-offs - -Pros - -\- Detects tampering - -\- Supports forensic analysis - -\- Enables replay - -Cons - -\- Additional verification processing - -\--- - -\# Decision 6 – Authorization Binding - -\## Decision - -Every approved execution must include an `authorizationId`. - -\## Rationale - -Execution must be traceable to the authorization that permitted it. - -\## Trade-offs - -Pros - -\- Strong accountability - -\- Improved auditability - -Cons - -\- Additional metadata requirements - -\--- - -\# Decision 7 – Append-Only Lifecycle Artifacts - -\## Decision - -Verification events and receipts are append-only. - -\## Rationale - -Execution evidence remains immutable while operational history continues to grow. - -\## Trade-offs - -Pros - -\- Preserves cryptographic integrity - -\- Complete audit history - -Cons - -\- Larger storage requirements over time - -\--- - -\# Decision 8 – Layered Architecture - -\## Decision - -Separate responsibilities into focused packages. - -Examples include: - -\- API - -\- Runtime - -\- Crypto - -\- Governance - -\- Repository - -\## Rationale - -Clear separation of concerns improves maintainability and testing. - -\## Trade-offs - -Pros - -\- Modular design - -\- Easier evolution - -\- Improved code ownership - -Cons - -\- More package boundaries to manage - -\--- - -\# Decision 9 – API-First Design - -\## Decision - -Expose platform capabilities through a REST API with an OpenAPI specification. - -\## Rationale - -An API-first approach simplifies integration, client generation, and testing. - -\## Trade-offs - -Pros - -\- Consistent integrations - -\- Interactive documentation - -\- Standard tooling support - -Cons - -\- API versioning must be managed carefully - -\--- - -\# Decision 10 – Replay Without Re-Execution - -\## Decision - -Replay verifies stored execution evidence instead of executing the original business action again. - -\## Rationale - -Replay should validate evidence without producing additional business side effects. - -\## Trade-offs - -Pros - -\- Safe verification - -\- Deterministic results - -\- No duplicate business operations - -Cons - -\- Replay cannot validate changes in external systems after the original execution - -\--- - -\# Summary - -The Parmana architecture emphasizes deterministic execution, immutable evidence, independent verification, and modular design. These decisions collectively support trustworthy execution authorization while balancing operational simplicity, security, and long-term maintainability. diff --git a/docs/guides/e2e/25-contributing.md b/docs/guides/e2e/25-contributing.md deleted file mode 100644 index a15c192e..00000000 --- a/docs/guides/e2e/25-contributing.md +++ /dev/null @@ -1,223 +0,0 @@ -\# 25 – Contributing - -This guide explains how to contribute to the Parmana Execution Trust Platform while maintaining its architectural principles and code quality. - -\--- - -\# Purpose - -Parmana is designed around deterministic execution, immutable evidence, and independent verification. - -Every contribution should preserve these principles. - -\--- - -\# Development Principles - -Contributors should follow these core principles: - -\- Deterministic behavior - -\- Immutable execution evidence - -\- Clear separation of concerns - -\- Security by default - -\- Comprehensive testing - -\- Well-documented changes - -\--- - -\# Development Workflow - -```text - -Fork Repository - - │ - - ▼ - -Create Feature Branch - - │ - - ▼ - -Implement Changes - - │ - - ▼ - -Run Tests - - │ - - ▼ - -Update Documentation - - │ - - ▼ - -Open Pull Request - - │ - - ▼ - -Code Review - - │ - - ▼ - -Merge - -``` - -\--- - -\# Branching - -Use descriptive branch names. - -Examples: - -\- feature/execution-gateway - -\- feature/key-rotation - -\- fix/verification-service - -\- docs/api-reference - -\--- - -\# Coding Standards - -Contributors should: - -\- Use TypeScript strict mode. - -\- Prefer small, focused classes. - -\- Keep functions concise. - -\- Avoid duplicated logic. - -\- Write meaningful comments where intent is not obvious. - -\--- - -\# Testing - -Every change should include appropriate tests. - -Recommended categories: - -\- Unit tests - -\- Integration tests - -\- API tests - -\- Cryptographic verification tests - -\- Replay tests - -All existing tests should pass before submitting a pull request. - -\--- - -\# Documentation - -Documentation should be updated whenever: - -\- APIs change - -\- Runtime behavior changes - -\- Security behavior changes - -\- New configuration is introduced - -\- New guides are added - -\--- - -\# Security - -Do not: - -\- Commit private keys - -\- Commit secrets - -\- Hard-code credentials - -\- Log sensitive information - -Report security issues through the project's responsible disclosure process rather than public issue trackers. - -\--- - -\# Pull Requests - -A pull request should include: - -\- Summary of changes - -\- Motivation - -\- Testing performed - -\- Documentation updates - -\- Any compatibility considerations - -\--- - -\# Code Review Checklist - -Reviewers should verify: - -\- Correctness - -\- Deterministic behavior - -\- Backward compatibility - -\- Test coverage - -\- Documentation updates - -\- Security implications - -\--- - -\# Architectural Principles - -Changes should preserve: - -\- Deterministic execution - -\- Immutable Trust Records - -\- Canonical hashing - -\- Independent verification - -\- Append-only lifecycle artifacts - -\- Clear package boundaries - -\--- - -\# Summary - -Successful contributions improve the platform while preserving Parmana's core architectural guarantees: deterministic execution, cryptographic integrity, immutable evidence, and independent verification. diff --git a/docs/guides/e2e/execute-request.json b/docs/guides/e2e/execute-request.json deleted file mode 100644 index dfac6a54..00000000 --- a/docs/guides/e2e/execute-request.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200", - "metadata": { - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200", - "correlationId": "bfe321d7-d9e7-4cef-a6da-eaeeba1c1d5b", - "sourceSystem": "vendor-payment-service", - "submittedBy": "ap-automation", - "submittedAt": "2026-07-07T16:38:59.285Z" - }, - "authority": { - "authorityId": "f5636910-84db-41d8-9a38-09e5b0181392", - "authorityType": "SERVICE", - "principalId": "ap-automation-svc", - "displayName": "Accounts Payable Automation", - "issuedAt": "2026-07-07T16:38:59.285Z" - }, - "authorization": { - "authorizationId": "2e87f670-631b-48a0-b278-835419841486", - "authorityId": "f5636910-84db-41d8-9a38-09e5b0181392", - "purpose": "Authorize vendor payment disbursement", - "issuedAt": "2026-07-07T16:38:59.285Z" - }, - "intent": { - "intentId": "f92bb2c3-5d96-45a7-b8f7-3a527f1b7e0d", - "authorizationId": "2e87f670-631b-48a0-b278-835419841486", - "action": "payments:execute", - "target": "vendor/V-100", - "parameters": { - "amount": 4500, - "currency": "USD" - }, - "createdAt": "2026-07-07T16:38:59.285Z" - }, - "policy": { - "name": "vendor-payment", - "version": "2.0.0", - "schemaVersion": "1.0.0" - }, - "signals": { - "vendorVerified": true, - "invoiceVerified": true, - "paymentApproved": true, - "sufficientFunds": true, - "paymentAmount": 4500, - "riskScore": 10 - } -} diff --git a/docs/guides/e2e/receipt-request.json b/docs/guides/e2e/receipt-request.json deleted file mode 100644 index 7387165b..00000000 --- a/docs/guides/e2e/receipt-request.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200" -} diff --git a/docs/guides/e2e/receipt-response.json b/docs/guides/e2e/receipt-response.json deleted file mode 100644 index 0c07349d..00000000 --- a/docs/guides/e2e/receipt-response.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "receiptId": "6f1e7586-ae5d-4718-8d06-36aa541bdb8b", - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200", - "trustRecordHash": "f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4", - "receiptHash": "bd1280f038f0e9257b86f006fb023d90ae1f00d0b9f12451c72bf7d06c469ac6", - "issuedAt": "2026-07-18T04:05:47.378Z", - "algorithm": "ed25519", - "signature": "r4+YxAtVIL7BHJhEr5bPZSq1lvAKf50C3umPmIrMwfb9wAGks5Oxs4A+Ohae/6H7qddb3QxTvxcNtVR4Uo75CQ==" -} diff --git a/docs/guides/e2e/replay-request.json b/docs/guides/e2e/replay-request.json deleted file mode 100644 index 7387165b..00000000 --- a/docs/guides/e2e/replay-request.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200" -} diff --git a/docs/guides/e2e/replay-response.json b/docs/guides/e2e/replay-response.json deleted file mode 100644 index 56748407..00000000 --- a/docs/guides/e2e/replay-response.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200", - "trustRecordHash": "f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4", - "verified": true -} diff --git a/docs/guides/e2e/trust-record-response.json b/docs/guides/e2e/trust-record-response.json deleted file mode 100644 index ffba4684..00000000 --- a/docs/guides/e2e/trust-record-response.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "trustRecordId": "3280db24-fdd3-4feb-ac04-97c3b07c8c41", - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200", - "transaction": { - "intent": { - "action": "payments:execute", - "target": "vendor/V-100", - "intentId": "f92bb2c3-5d96-45a7-b8f7-3a527f1b7e0d", - "createdAt": "2026-07-07T16:38:59.285Z", - "parameters": { - "amount": 4500, - "currency": "USD" - }, - "authorizationId": "2e87f670-631b-48a0-b278-835419841486" - }, - "policy": { - "name": "vendor-payment", - "version": "2.0.0", - "schemaVersion": "1.0.0" - }, - "status": "RECEIVED", - "signals": { - "riskScore": 10, - "paymentAmount": 4500, - "vendorVerified": true, - "invoiceVerified": true, - "paymentApproved": true, - "sufficientFunds": true - }, - "metadata": { - "submittedAt": "2026-07-07T16:38:59.285Z", - "submittedBy": "ap-automation", - "sourceSystem": "vendor-payment-service", - "correlationId": "bfe321d7-d9e7-4cef-a6da-eaeeba1c1d5b", - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200" - }, - "authority": { - "issuedAt": "2026-07-07T16:38:59.285Z", - "authorityId": "f5636910-84db-41d8-9a38-09e5b0181392", - "displayName": "Accounts Payable Automation", - "principalId": "ap-automation-svc", - "authorityType": "SERVICE" - }, - "createdAt": "2026-07-18T03:59:40.676Z", - "authorization": { - "purpose": "Authorize vendor payment disbursement", - "issuedAt": "2026-07-07T16:38:59.285Z", - "authorityId": "f5636910-84db-41d8-9a38-09e5b0181392", - "authorizationId": "2e87f670-631b-48a0-b278-835419841486" - }, - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200" - }, - "executions": [ - { - "mode": "SYNC", - "status": "COMPLETED", - "decision": { - "policy": { - "name": "vendor-payment", - "version": "2.0.0", - "schemaVersion": "1.0.0" - }, - "reason": "Vendor payment authorized. Vendor verification, invoice verification, payment approval, funding, and risk assessment requirements were satisfied.", - "outcome": "APPROVED", - "signals": { - "riskScore": 10, - "paymentAmount": 4500, - "vendorVerified": true, - "invoiceVerified": true, - "paymentApproved": true, - "sufficientFunds": true - }, - "intentId": "f92bb2c3-5d96-45a7-b8f7-3a527f1b7e0d", - "decisionId": "8e942c3e-6e4d-4929-b20b-961b646143f2", - "evaluatedAt": "2026-07-18T03:59:41.196Z" - }, - "evidence": { - "action": "payments:execute", - "target": "vendor/V-100", - "success": true, - "attributes": { - "connector": { - "startedAt": "2026-07-18T03:59:42.425Z", - "capability": "payments:execute", - "completedAt": "2026-07-18T03:59:42.425Z", - "connectorId": "vendor-payment", - "requestSummary": { - "action": "payments:execute", - "target": "vendor/V-100", - "parameters": { - "amount": 4500, - "currency": "USD" - }, - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200" - }, - "responseSummary": { - "success": true, - "metadata": {} - }, - "connectorVersion": "1.0.0", - "sanitizedEndpoint": "vendor/V-100", - "credentialProviderId": "environment", - "connectorEvidenceHash": "af3a14cfa844fba1336463a0146771720ed6378ef3d7c513c5ffddc1972c2db6" - } - }, - "executedAt": "2026-07-18T03:59:42.425Z", - "parameters": { - "amount": 4500, - "currency": "USD" - }, - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200" - }, - "metadata": { - "authorizationId": "180499d2-5f44-4838-8709-e92d07b04394" - }, - "startedAt": "2026-07-18T03:59:41.509Z", - "completedAt": "2026-07-18T03:59:43.541Z", - "executionId": "bf78f1c9-f007-40ab-9a14-92bc6f815f1f", - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200" - } - ], - "overrides": [], - "verifications": [ - { - "status": "VERIFIED", - "message": "Execution Trust Record verified successfully.", - "verifiedAt": "2026-07-18T03:59:45.798Z", - "verificationId": "999bedec-876d-4fdf-b1a3-91d01866ffcd", - "trustRecordHash": "f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4", - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200" - } - ], - "receipts": [ - { - "issuedAt": "2026-07-18T03:59:47.535Z", - "algorithm": "ed25519", - "receiptId": "a677de4e-472c-4b37-8a12-4199d2510b8b", - "signature": "ToH2J0UNANM4RfJeoV0BVbE5G3G3eh6BoOErQuacXpal7RpcvhRMg4tVG2ENITUCrF+TfNsrZIg3WsIg1E1rBg==", - "receiptHash": "562410a8687a2771d2f50ae71811cff2822340c52f485cb58eed918823a67de1", - "trustRecordHash": "f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4", - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200" - }, - { - "issuedAt": "2026-07-18T04:05:47.378Z", - "algorithm": "ed25519", - "receiptId": "6f1e7586-ae5d-4718-8d06-36aa541bdb8b", - "signature": "r4+YxAtVIL7BHJhEr5bPZSq1lvAKf50C3umPmIrMwfb9wAGks5Oxs4A+Ohae/6H7qddb3QxTvxcNtVR4Uo75CQ==", - "receiptHash": "bd1280f038f0e9257b86f006fb023d90ae1f00d0b9f12451c72bf7d06c469ac6", - "trustRecordHash": "f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4", - "businessTransactionId": "b9404bdf-ad2a-4dce-8001-0eacd0974200" - } - ], - "trustRecordHash": "f6b35361c5cf90fefef039c56cacafdb9cc030f0d2c1259e6eb3b565c58cc3e4", - "signature": { - "keyId": "default", - "value": "tMSQwuuXt7S4niSj6Z4Su8ThzbQwGtRHQ13CBL/ukHSGKnCyUHYz3USfDlcc9C9xXdjk9nHrqI+UWg9Bgy6OBQ==", - "signedAt": "2026-07-18T03:59:44.057Z", - "algorithm": "ed25519" - }, - "createdAt": "2026-07-18T03:59:44.053Z", - "updatedAt": "2026-07-18T04:05:47.680Z" -} diff --git a/docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md b/docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md deleted file mode 100644 index b29b64dc..00000000 --- a/docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md +++ /dev/null @@ -1,401 +0,0 @@ -# KMS Migration Troubleshooting Guide (2026-09-15 / 2026-09-16) - -> **Status of this document: a completed incident/debugging record, kept -> as a troubleshooting reference.** Every issue below was real, found -> against this project's actual production deployment -> (`parmana-api-real.vercel.app`), and fixed the same session. If you hit -> one of these symptoms again — in this project or a similar KMS -> migration elsewhere — start here before re-diagnosing from scratch. -> -> Companion document: `docs/operations/aws-kms-vercel-oidc-setup-guide.md` -> is the step-by-step "do this from scratch" guide. This document is the -> "here's what went wrong and why" postmortem. Full technical detail on -> each bug (root cause, fix, verification) lives in `docs/VERIFICATION-GAPS.md` -> G-48 and G-49; this document is the narrative, chronological version — -> useful for understanding the _order_ things were discovered in and why -> each fix was scoped the way it was. -> -> Runnable, hermetic proof of the three most significant bugs (no AWS -> credentials needed): `examples/tutorials/113-kms-key-id-resolution/`, -> `examples/tutorials/114-signing-verification-key-agreement/`, and -> `examples/tutorials/115-per-limiter-rate-limit-stores/` — each -> reproduces the real failure against the real code, then confirms the -> real fix. Run all three with `npm run examples` (part of the full -> tutorial suite) or individually with `npx tsx examples/tutorials//run.ts`. - -## Summary - -The task was: fix a bug where `KmsSigner` couldn't resolve AWS KMS keys by -their logical name, then get the gateway's Ed25519 signing key fully onto -AWS KMS in production, then unblock a real integration partner (Pfinite) -that was getting HTTP 401s calling Parmana. What actually happened: five -more real, previously-undiscovered bugs surfaced, each one only visible by -testing against live production traffic — none of them were visible from -code review or the existing 1800+ test suite, because the test suite -mocks exactly the boundary (AWS KMS, Vercel's OIDC runtime, real -concurrent cold starts) where each bug lived. - -**Lesson underlying most of this document:** a migration that changes -_where_ a system's trust boundary sits (local file → AWS KMS, -always-running process → serverless cold starts) needs to be verified -against the real target platform's actual runtime behavior, not just -against a mocked test double of it. Every bug below is a place where the -mock and the real platform disagreed. - -## Timeline and each issue - -### 1. `KmsSigner` couldn't resolve a logical keyId against real AWS KMS - -**Symptom:** none yet observed in production — found by code review before -deployment, while verifying `Signer`/`SignerBootstrap`/`KmsSigner` -(already implemented in a prior session, commit `43fd5dc`) actually worked -end-to-end. - -**Root cause:** every signing call site in this codebase passes a -_logical_ keyId (`DEFAULT_KEY_ID = "default"`) to `Signer.sign(keyId, data)`. -`KmsSigner` passed that string straight through as AWS KMS's `KeyId` -parameter. AWS KMS requires `KeyId` to be a real key ID (UUID), a full key -ARN, or an alias name/ARN (which must carry the `alias/` prefix) — a bare -`"default"` is none of those and is rejected outright. - -**Fix:** `KmsSigner` now maps `keyId` → `alias/` (mirroring -`FileKeyProvider`'s own `keyId` → `.private.pem` convention), -passing an already-qualified alias, ARN, or raw key UUID through -unchanged. This is why `alias/default` must exist in AWS (see the setup -guide, Part 2) — it's not a naming convenience, it's what the logical -keyId `"default"` resolves to. - -**Verified:** `packages/crypto/tests/unit/kms-signer.test.ts` (mocked -AWS SDK) plus a real, one-time end-to-end test against actual AWS KMS -(sign/verify/getPublicKey/getMetadata, deleted after confirming — see -`packages/crypto/tests/unit/kms-signer.test.ts`'s permanent mocked -coverage for the regression test that stayed). - -### 2. `createGatewayPublicKey()` read a local file unconditionally - -**Symptom:** every request to production 500'd immediately after -`KEY_PROVIDER=aws-kms` was first set, including `/health`. - -**Root cause:** this call site was outside ADR-0009's original -seven-call-site audit. It read `default.public.pem` from -`PARMANA_KEY_DIR` directly, with no `KEY_PROVIDER` branch at all — under -`aws-kms`, no local key directory is materialized for the default key, so -this threw `Gateway public key not found` at the first line of module -construction. - -**Fix:** resolve through `SignerBootstrap`, same as every other call site. - -**Cascading consequence:** `createGatewayPublicKey()`/`createExecutionGateway()`/ -`createExecutionSystem()` all had to become `async` (KMS calls are -inherently network I/O, unlike a synchronous file read) — which meant -~20 call sites across `server.ts`, `api/index.ts`, test files, and -tutorial scripts needed `await` added. Mechanical but wide-reaching; see -the actual diff for the full file list. Two categories: module-top-level -calls in ESM files (just add `await`, top-level await already works — -`packages/api/src/server.ts` already used it for -`assertKmsSigningKeyReachable()`), and calls inside a function that -itself needed to become `async`. - -### 3. Vercel Functions can't get the OIDC token at cold start - -**Symptom:** after fixing #2, a _different_ crash: `VercelOidcTokenError: -The 'x-vercel-oidc-token' header is missing from the request.` - -**Root cause:** `@vercel/oidc`'s `getVercelOidcToken()` reads the OIDC -token from the current HTTP request's `x-vercel-oidc-token` header — this -is only available while Vercel's runtime is actually handling a request. -`api/index.ts` built the entire `ExecutionSystem` (including the KMS -`GetPublicKey` call) **eagerly at module top level**, before Vercel's -runtime had ever handed it a request to read a header from. This is a -hard platform constraint, documented by Vercel, not something to route -around with more `await`s — see `docs/oidc/reference`: "you cannot execute -`getVercelOidcToken()` directly at the module level." - -**Fix:** restructured `api/index.ts` to export a lazy handler function. -The app is built once, on the **first real incoming request** (inside -`handler()`, with an actual request in flight and therefore a real OIDC -header available), memoized (`appPromise`) for reuse by subsequent -requests to the same warm container. A build failure resets the memo so -the next request gets a fresh attempt rather than a permanently-poisoned -rejected promise. - -**Note for anyone repeating this on a different platform:** this -constraint is specific to Vercel's request-scoped OIDC token delivery. A -different serverless platform (or a long-running process like `server.ts`, -which is not subject to this — it calls `assertKmsSigningKeyReachable()` -at real process startup, which is fine because that process never needs a -_request's_ header, just the environment's long-lived `VERCEL_OIDC_TOKEN`) -may not have this restriction at all. - -### 4. `assertSigningKeyMaterialConfigured()` silently broke a _different_ key - -**Symptom:** after fixing #3, yet another crash: `Gateway private key not -found: /tmp/keys/gateway.private.pem`. - -**Root cause:** this function's `KEY_PROVIDER=aws-kms` branch returned -early, skipping `materializeFromEnvIfConfigured()` entirely. That function -writes _every_ keyId present in `PARMANA_KEY_MATERIAL_JSON` to local disk -— not just the `"default"` signing key ADR-0009 actually moved to KMS. -The gateway's separate attestation key (`createGatewayKeyPair.ts`, -keyId `"gateway"` — a deliberately distinct key from `"default"`, "different -trust domain" per its own doc comment) was never meant to move to KMS at -all, but returning early meant it never got materialized to disk either, -even though `PARMANA_KEY_MATERIAL_JSON` still contained it. - -**Fix:** `materializeFromEnvIfConfigured()` now always runs (as long as a -key directory is configured); only the `"default"`-key-specific _existence -check_ is skipped for `aws-kms` (that specific key genuinely has no local -file under that provider — `assertKmsSigningKeyReachable()`, called -separately, is its fail-closed equivalent). - -**Lesson:** "does this env var control one key's custody, or every key -that happens to flow through the same materialization function" is easy -to get wrong when a fail-fast check's scope silently changes. - -### 5. `express-rate-limit`'s `ERR_ERL_STORE_REUSE` (unrelated to KMS — and NOT actually the cause of the 500s it was found next to) - -**Symptom:** a `ValidationError: A Store instance must not be shared across -multiple rate limiters` log line appeared next to a request that returned -HTTP 500, on a Vercel cold start. - -**Root cause:** pre-existing bug, exposed (not caused) by this migration. -`createApp()` passed one shared `PostgresRateLimitStore` instance to both -the `/execute` and `/health`/`/ready` rate limiters, which -`express-rate-limit`'s documentation says a Store must never back more than -one of. It was always latent; it surfaced (as a log line) now because -Vercel's serverless model constructs the app fresh on every cold start, far -more often than the previous always-running deployment target did. - -**Correction — verified directly against the installed library's source -(`node_modules/express-rate-limit/dist/index.mjs`, `wrappedValidations`): -this validation does not throw.** Every validation in `express-rate-limit` -v8 is wrapped in a try/catch that catches `ValidationError` and only logs -it (`console.error` by default) — it never crashes the process. **The -original version of this document (and of `docs/VERIFICATION-GAPS.md` -G-49) stated this validation "crashed the whole request" — that was an -unverified assumption from seeing the log line next to a 500, not a -confirmed mechanism, and it does not hold up.** The 500 that request -actually returned was caused by item #7 below (the signing/verification -key divergence), which was still unfixed at that point in the session and -was logged in the same request. `examples/tutorials/115-per-limiter-rate-limit-stores/run.ts` -reproduces the real, corrected behavior directly: constructing two -limiters against one shared store logs a warning and returns normally, it -does not throw. - -Still worth fixing regardless of the corrected causal story: sharing one -`Store` instance violates the library's own documented contract, which -could become fatal in a future version or under a stricter `validate` -config, and may cause subtler non-crashing bookkeeping issues that weren't -separately investigated once the crash theory was corrected. - -**Fix:** two separate `PostgresRateLimitStore` instances (one per -limiter), each with a distinct `prefix` — matching `express-rate-limit`'s -own documented `Store.prefix` field (used by its own double-count -detection), not an invented mechanism. `RateLimitOption.store` (singular) -became `executeStore`/`healthStore` (plural, distinct) at the type level, -so a future call site literally cannot pass one shared instance again -without a type error. - -**Non-obvious TypeScript wrinkle hit while fixing this:** adding a -`private readonly prefix: string` field to `PostgresRateLimitStore` broke -its structural assignability to `express-rate-limit`'s `Store` type with -`Property 'prefix' is private in type 'X' but not in type 'Store'` — not -because private fields are generally incompatible with plain object -types, but because `Store` itself declares an optional **public** -`prefix?: string` field for exactly this purpose. Making the field public -(not a private implementation detail) fixed the type error and was the -semantically correct choice anyway. - -### 6. Pfinite's HTTP 401 — nothing to do with KMS - -**Symptom:** the original ask. Pfinite calling Parmana got a 401. - -**Root cause:** `PARMANA_API_KEYS` (the caller-authentication credential -store — entirely separate from the Ed25519 _signing_ key; one gates -inbound requests, the other signs outbound records) had no entry at all -for a `pfinite` caller. This was never a KMS issue. - -**Fix:** generated a new caller key (`scripts/generate-api-key.ts ---caller-id pfinite --credential-holder-type SERVICE --allowed-capabilities -"paytm:refund"`) and added its hash-only entry to `PARMANA_API_KEYS`. - -**Follow-on issue:** manually editing the Vercel dashboard's raw JSON -value for `PARMANA_API_KEYS` is error-prone — one edit replaced the -_entire_ array with a single un-wrapped object (not an array at all), -breaking JSON parsing for every request; another edit lost the -pre-existing `charak1987` entry entirely because "add an entry" was -interpreted as "replace the value" rather than "append to the array." -**When editing this value by hand, always paste the complete array back, -never a single entry.** - -**Follow-on issue 2:** even after authenticating successfully, Pfinite got -a 403 (`Caller is not permitted to assert this authority.principalId`). -`ApiKeyEntry.allowedPrincipalIds` defaults, when unset, to "this caller -may only assert itself as principal" — Pfinite was asserting -`principalId: "parmana-refund-agents"`, not `"pfinite"`. Fixed by adding -`allowedPrincipalIds: ["parmana-refund-agents"]` to its entry. This is -the system's fail-closed default working as designed, not a bug — a new -caller integration should expect to hit this and know to ask "what -principalId will this caller actually assert." - -### 7. The "ambiguous outcome" bug — signing and verification silently used different keys - -**Symptom:** after everything above was fixed, a _real_ Pfinite refund -request still got a 500, this time from deep inside the execution -pipeline: `Execution Gateway rejected request: failed checks -[signatureVerified, businessTransactionHashMatches, nonceUnseen]`. - -**This looked like three independent failures. It was one.** -`businessTransactionHashMatches` and `nonceUnseen` both hard-default to -`false` inside `ExecutionGateway.verify()` whenever `signatureVerified` is -`false` (see the `passed`/`priorChecksPassed` short-circuit) — they never -actually got evaluated. The only real failure was `signatureVerified`. - -**Root cause:** `createExecutionGateway.ts` passed an unconditional `new -FileKeyProvider()` as `ExecutionGateway`'s `keyProvider` option, regardless -of `KEY_PROVIDER`. `EnvelopeVerifier.resolveKey()` uses `keyProvider` — -when supplied at all — to resolve the verification key for **every** -authorization it checks, not only ones signed under a non-default -(tenant-scoped) keyId. This exact call site had been flagged the night -before as a "known gap," explicitly assessed as _"currently inert under -KEY_PROVIDER=aws-kms... no tenant key has been provisioned"_ — **that -assessment was wrong.** It is exercised on every single request, because -`"default"` is itself a keyId that flows through the same resolution -path. Parmana was signing every authorization with the real KMS key -(`RuntimeAuthorizationSigner` → `SignerBootstrap` → `KmsSigner`) but -verifying every authorization against whichever stale local -`default.public.pem` happened to still exist on disk from before the KMS -migration (kept alive, ironically, by the fix in #4 above, which -correctly resumed materializing _other_ keys from `PARMANA_KEY_MATERIAL_JSON` -but had the side effect of also keeping this now-wrong key file present). -Signing key and verifying key silently diverged — permanently, on every -request, with no startup error (`assertKmsSigningKeyReachable()` only -checks that the KMS key itself is reachable, not that every consumer is -actually configured to use it). - -**Fix:** `SignerKeyProviderAdapter` (new, -`packages/crypto/src/providers/SignerKeyProviderAdapter.ts`) adapts a -`Signer` to the read-only surface of `KeyProvider` (`getPrivateKey()` -always throws — a `Signer` never releases private key material by -design). `createExecutionGateway.ts` now resolves **one** `Signer` -instance and shares it between the static `publicKey` and the -`keyProvider` adapter, so both paths are structurally guaranteed to agree -on the same backend, rather than being two independent resolutions that -happen to usually match. - -**Why the earlier assessment was wrong, specifically, for anyone doing a -similar audit:** "is this code path exercised" needs to be checked against -what the _interface_ does by default, not just against what values are -currently configured. `keyProvider` being _supplied at all_ changes -`EnvelopeVerifier`'s behavior for every authorization, regardless of which -specific keyId shows up in practice — the presence of the option matters, -not just the diversity of keyIds seen so far. - -**How it was actually found:** not by code review — by directly querying -the shared `execution_audit_events` Postgres table (both this repo and -the separate `parmana-paytm-agent` repo write to the same table, -correlated by `businessTransactionId`) for a real failed transaction ID -pulled straight from Pfinite's own error message, and reading the exact -recorded failure reason. - -## What's still open after this session - -- **`ExecutionGateway.execute()`'s uncaught-exception-to-500 pattern for - verification failures is deliberate, documented design** — see - `packages/api/src/middleware/error-handler.ts`'s own comment: nonce - replay gets a distinguishable response; every other Gateway - verification failure (forged signature, expired envelope, tampered - content) deliberately falls through to an opaque 500, apparently to - avoid giving an attacker a signature-verification oracle. This was - **not changed** in this session — it's a real security tradeoff someone - made on purpose, not obviously a bug, and changing it needs its own - deliberate review of the threat model, not a quick patch bundled into a - KMS migration. -- **`parmana-paytm-agent`'s own `verifyPaytmAuthorizationSignature()` has - the identical pattern** (throws on any failure, uniformly surfaced as 500) — same reasoning applies, not touched, and it's a separate - repository/deployment. -- **Tenant-scoped KMS keys (`tenant.`) are not automated.** The - `SignerKeyProviderAdapter` fix in #7 makes tenant-key verification - _correct_ under KMS (it was already correct under local-file mode), but - provisioning a real per-tenant KMS key/alias is still a fully manual - `aws kms create-alias --alias-name alias/tenant. ...` step per - tenant, same as it was manual for local files before. -- **The `GatewayAttestationSigner`/`createGatewayKeyPair()` path stays - local-file-only**, deliberately not migrated to KMS in this session. - `GatewayAttestationSigner.sign()` is synchronous by design (`ConnectorAuthenticator.authenticateGateway()` - is a synchronous interface), and KMS calls are inherently async — moving - this to KMS means changing that interface's signature and following the - ripple, a larger, separate piece of work. -- **`PARMANA_KEY_MATERIAL_JSON` is still set** in both `.env` and Vercel. - ADR-0009 explicitly treats deleting it as a final, separate step, only - after the KMS path has been live and stable for a real period — not - rushed the same night it was first deployed. -- **Paytm merchant credentials are not yet configured** in - `parmana-paytm-agent`'s environment. Once Parmana's own signing/ - verification chain was fixed (item #7), real refund requests correctly - reach the actual Paytm API and fail there with an HTTP 503 (non-JSON - response) — this is external to both repositories and needs real - `PAYTM_MERCHANT_ID`/`PAYTM_MERCHANT_KEY`/`PAYTM_ENVIRONMENT` values from - an actual Paytm merchant account before real refunds can execute. - -## Issue found 2026-09-20: KMS rejects a message over 4096 bytes - -**Symptom.** `POST /execute` returns `500 {"error":"Internal Server Error"}`. The runtime log shows: - -``` -ValidationException: 1 validation error detected: Value at 'message' failed to satisfy -constraint: Member must have length less than or equal to 4096 - at async KmsSigner.sign (.../providers/signer/KmsSigner.js) - at async VerificationCrypto.sign (.../VerificationCrypto.js) -``` - -**Cause.** AWS KMS refuses a raw Ed25519 message over 4096 bytes, and `KmsSigner.sign()` sent the full -canonical bytes of the Execution Trust Record, which with its bound authorization, connector evidence -and governance anchor is larger. It did not show up in earlier KMS testing because the -`test:fixture-execute` record is small. A refund through the real Paytm connector is not. - -**Fix.** ADR-0010. A message over 4096 bytes is signed as a fixed 97 byte commitment (the prefix -`PARMANA-ED25519-LARGE-MESSAGE-V1`, a NUL byte, and the SHA-512 digest), and verifiers accept it. -Nothing needs re-signing, and signatures issued earlier verify unchanged. - -**Since ADR-0011 this is prevented and made explicit.** Before release the runtime proves signing works and returns `503 SIGNING_UNAVAILABLE` if not, and a failure after release is `500 EXECUTION_RECORD_INCOMPLETE`. On a build without that change, the following applied: the connector had already been called when the failure happened -(the Paytm connector service logged `POST /connector/paytm-refund` before `/execute` returned `500`). -The action was released and no signed trust record was produced, see G-52 in -`docs/VERIFICATION-GAPS.md`. For any `500` on `/execute` after this class of failure, query -`execution_audit_events` by `business_transaction_id` to see whether the connector executed, and do not -assume nothing happened. - -**Confirm the fix is live.** After deploying, run one small `paytm:refund` through `/execute` and check -that the response is a full `ExecutionTrustRecord` with `verifications[0].status` of `VERIFIED` and a -signed receipt, and that the log contains no `ValidationException`. - -## Quick diagnostic checklist for "something's wrong with KMS signing" - -1. `curl https://YOUR-PROJECT.vercel.app/keys/default` — 200 with a real - key means the basic KMS path (auth + key resolution) works. 500 means - start with Vercel's runtime logs (`vercel logs `). -2. If the log shows `VercelOidcTokenError` — something is calling AWS - _before_ a real request is being handled (cold-start/module-top-level - issue, see #3 above). -3. If the log shows `Gateway public key not found` / `Gateway private key -not found: .../gateway.private.pem` — a materialization gap, see #2/#4. -4. If a _real_ execution reaches the Gateway and fails with `failed checks -[signatureVerified, ...]` — check `createExecutionGateway.ts`'s - `keyProvider` construction first (see #7); this is the highest-signal - symptom for "signing and verification are using different keys." -5. If you see `ERR_ERL_STORE_REUSE` in the logs — it's a real, worth-fixing - contract violation (see #5), but it only logs a warning, it does not - throw or cause the request to fail. Don't stop investigating a 500 just - because this line is nearby; find the actual thrown error in the same - log output. -6. To find the _exact_ reason a real cross-repository transaction failed - (when Parmana's own logs only show a generic "connector service failed - with HTTP 500"), query the shared `execution_audit_events` table by - `business_transaction_id` — both repos write detailed `reason` fields - there even when the HTTP-level error is opaque. -7. If the log shows `ValidationException ... length less than or equal to 4096` from - `KmsSigner.sign` means a message over the KMS raw limit was signed without the commitment scheme, - see the 2026-09-20 section above. Check that the deployed build includes ADR-0010, and check - `execution_audit_events` for whether the connector already ran. -8. If `/execute` returns `503 SIGNING_UNAVAILABLE`, the pre release signing probe failed and nothing was executed. Read the cause in the message, check the KMS key state and the role's `kms:Sign` and `kms:GetPublicKey` permissions, then retry with a new `businessTransactionId`. -9. If `/execute` returns `500 EXECUTION_RECORD_INCOMPLETE`, the action was released and the record failed. Do not retry as a new transaction. Query `execution_audit_events` by `business_transaction_id` and check the connector's own record first. diff --git a/docs/operations/aws-kms-vercel-oidc-setup-guide.md b/docs/operations/aws-kms-vercel-oidc-setup-guide.md deleted file mode 100644 index 4a06f9d8..00000000 --- a/docs/operations/aws-kms-vercel-oidc-setup-guide.md +++ /dev/null @@ -1,445 +0,0 @@ -# AWS KMS + Vercel OIDC Setup Guide (Gateway Signing Key) - -> **Status of this document: a how-to, verified against real commands run -> 2026-09-15/16.** Every command below was actually executed against this -> project's real AWS account (`013659367671`, `ap-south-1`) and real Vercel -> project (`pavan-dev-singh-charaks-projects/parmana-api-real`) while -> standing this up for the first time. It is not a theoretical writeup. If -> you are doing this for a **new** project/account, substitute your own -> account ID, region, and Vercel team/project slugs throughout — the shape -> of every command stays the same. -> -> Companion document: `docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md` -> covers what went wrong doing this the first time and how each issue was -> diagnosed — read that one if something here doesn't work as described. -> Design rationale lives in `docs/adr/ADR-0009-KMS-Secrets-And-Connector-Signature-Hardening.md`. - -## What this achieves - -Parmana's gateway signing key (the Ed25519 key that signs every Execution -Authorization, Trust Record, Refusal Record, and Attestation) moves from a -plaintext PEM file on disk / in `PARMANA_KEY_MATERIAL_JSON` to AWS KMS, -which never releases private key material — a fully compromised process can -request signatures but cannot exfiltrate the key. The deployed Vercel -Function authenticates to AWS with **no static AWS credentials at all**: -it exchanges Vercel's own per-request OIDC token for short-lived AWS -credentials via `AssumeRoleWithWebIdentity`. - -Two separate AWS identities get created in this guide, deliberately: - -1. **A local-admin IAM user** (`parmana-kms-operator` in this project) — - for you, the human operator, to run AWS CLI commands from your own - machine (create the key, inspect it, etc.). Scoped narrowly, never used - by the deployed application. -2. **An IAM role assumed via Vercel OIDC** (`parmana-vercel-kms-signer` in - this project) — this is what the deployed Vercel Function actually - uses at runtime. No access keys, no secrets stored in Vercel at all for - this purpose. - -Do not conflate the two. Production should never authenticate to AWS with -the local-admin user's credentials. - -## Prerequisites - -- AWS CLI **2.32.0 or later** (`aws --version`) — required for `aws login`'s - short-term-credential flow. If you're below this, update first; do not - fall back to `aws configure` with long-term access keys unless you have - no other option (see the `signing-in-to-aws` guidance on why). -- Vercel CLI (`npm i -g vercel` if not already installed). -- You (or whoever runs this) needs sufficient IAM permissions in the AWS - account to create IAM users, policies, roles, OIDC providers, and KMS - keys. In this project that meant using the AWS account **root** user for - the one-time IAM-creation steps, then never using it again. - -## Part 1 — Local admin identity (`parmana-kms-operator`) - -All of Part 1 runs as the AWS account root user (or an existing -administrator identity if you have one — root was used here only because -no other admin identity existed yet in a brand-new account). - -### 1.1 Sign in - -``` -aws login --profile parmana -``` - -(`aws login` opens a browser; short-term credentials, auto-rotating every -15 minutes, valid up to 12 hours — never long-term access keys. See -`aws --version` note above.) - -``` -aws sts get-caller-identity --profile parmana --region ap-south-1 -``` - -Confirm the `Arn` ends in `:root` (or your admin identity) before -proceeding. - -### 1.2 Create the IAM user and its policy - -``` -aws iam create-user --user-name parmana-kms-operator --profile parmana \ - --tags Key=purpose,Value=parmana-kms-signing -``` - -Write a least-privilege policy document (adjust the account ID/region; -`YOUR_KEY_ARN` doesn't exist yet at this point, so the first version is -intentionally broader — Part 1.4 tightens it once the key exists): - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "CreateEd25519SigningKeyOnly", - "Effect": "Allow", - "Action": "kms:CreateKey", - "Resource": "*", - "Condition": { - "StringEquals": { - "kms:KeySpec": "ECC_NIST_EDWARDS25519", - "kms:KeyUsage": "SIGN_VERIFY" - } - } - }, - { - "Sid": "KeyManagement", - "Effect": "Allow", - "Action": [ - "kms:CreateAlias", - "kms:TagResource", - "kms:ListAliases", - "kms:ListKeys" - ], - "Resource": "*" - }, - { - "Sid": "UseSigningKeyTemporary", - "Effect": "Allow", - "Action": [ - "kms:DescribeKey", - "kms:GetPublicKey", - "kms:Sign", - "kms:Verify" - ], - "Resource": "*" - } - ] -} -``` - -> Sid values must be alphanumeric only (no `-`/`_`) — `MalformedPolicyDocument` -> otherwise. - -``` -aws iam create-policy --policy-name parmana-kms-signer-policy \ - --policy-document file://policy.json --profile parmana - -aws iam attach-user-policy --user-name parmana-kms-operator \ - --policy-arn "arn:aws:iam::YOUR_ACCOUNT_ID:policy/parmana-kms-signer-policy" \ - --profile parmana - -aws iam attach-user-policy --user-name parmana-kms-operator \ - --policy-arn "arn:aws:iam::aws:policy/SignInLocalDevelopmentAccess" \ - --profile parmana -``` - -The `SignInLocalDevelopmentAccess` managed policy is required for this user -to be usable with `aws login` at all (root doesn't need it; any non-root -identity does). - -### 1.3 Set a console password and switch to the user - -`aws iam create-login-profile` prints a plaintext password — **do this -yourself in the AWS Console** (IAM → Users → parmana-kms-operator → -Security credentials → Console password), not via a script, so the -password never passes through any automation/transcript. Check "require -reset on next sign-in". - -Then: - -``` -aws login --profile parmana -``` - -**Important:** if your browser has an active root session, `aws login` -may silently reuse it instead of prompting you to choose an identity. If -it doesn't prompt, use an incognito/private window and sign in explicitly -as the IAM user (account ID + username + password). Confirm with: - -``` -aws sts get-caller-identity --profile parmana --region ap-south-1 -``` - -The `Arn` should now be `arn:aws:iam::YOUR_ACCOUNT_ID:user/parmana-kms-operator`. - -## Part 2 — Create the KMS key - -Run as `parmana-kms-operator` (Part 1.3's identity). - -``` -aws kms create-key --key-spec ECC_NIST_EDWARDS25519 --key-usage SIGN_VERIFY \ - --description "Parmana Ed25519 signing key" \ - --profile parmana --region ap-south-1 -``` - -Note the returned `KeyId` (a UUID) and construct the key ARN: -`arn:aws:kms:REGION:ACCOUNT_ID:key/KEY_ID`. - -Create **two** aliases pointing at the same key: - -``` -aws kms create-alias --alias-name alias/parmana-ed25519-signer \ - --target-key-id YOUR_KEY_ID --profile parmana --region ap-south-1 - -aws kms create-alias --alias-name alias/default \ - --target-key-id YOUR_KEY_ID --profile parmana --region ap-south-1 -``` - -**`alias/default` is not optional or cosmetic.** `KmsSigner` -(`packages/crypto/src/providers/signer/KmsSigner.ts`) maps every logical -keyId this codebase uses (`DEFAULT_KEY_ID = "default"`, see -`packages/crypto/src/KeyProvider.ts`) to `alias/` before calling -AWS — without `alias/default` existing, every signing/verification call -fails with `NotFoundException`. See the troubleshooting guide's first -entry for the full story of why this mapping exists at all. - -## Part 3 — Tighten the operator's policy (requires root again) - -Now that the key exists, remove `kms:CreateKey` and scope everything to -the specific key ARN. This step needs root/admin again (`parmana-kms-operator` -deliberately cannot modify its own IAM policy — that would be a privilege -escalation vector). - -``` -aws login --profile parmana # sign in as root this time -``` - -Update the policy document to: - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "ManageThisSigningKey", - "Effect": "Allow", - "Action": [ - "kms:TagResource", - "kms:UntagResource", - "kms:CreateAlias", - "kms:DeleteAlias", - "kms:DescribeKey" - ], - "Resource": [ - "arn:aws:kms:REGION:ACCOUNT_ID:key/YOUR_KEY_ID", - "arn:aws:kms:REGION:ACCOUNT_ID:alias/default", - "arn:aws:kms:REGION:ACCOUNT_ID:alias/parmana-ed25519-signer" - ] - }, - { - "Sid": "ListKeysReadOnly", - "Effect": "Allow", - "Action": ["kms:ListAliases", "kms:ListKeys"], - "Resource": "*" - }, - { - "Sid": "UseThisSigningKeyOnly", - "Effect": "Allow", - "Action": ["kms:GetPublicKey", "kms:Sign", "kms:Verify"], - "Resource": "arn:aws:kms:REGION:ACCOUNT_ID:key/YOUR_KEY_ID" - } - ] -} -``` - -> `kms:CreateAlias` needs the **alias** ARN in `Resource`, not just the key -> ARN — a common source of a confusing `AccessDeniedException` if you scope -> only the key. - -``` -aws iam create-policy-version --policy-arn "arn:aws:iam::ACCOUNT_ID:policy/parmana-kms-signer-policy" \ - --policy-document file://policy.json --set-as-default --profile parmana --region ap-south-1 - -aws iam delete-policy-version --policy-arn "arn:aws:iam::ACCOUNT_ID:policy/parmana-kms-signer-policy" \ - --version-id v1 --profile parmana --region ap-south-1 # delete the superseded version - -aws login --profile parmana # switch back to parmana-kms-operator -``` - -## Part 4 — Vercel → AWS OIDC role (what production actually uses) - -This is the identity the **deployed application** uses. No static -credentials get stored in Vercel for this. - -### 4.1 Find your Vercel OIDC issuer - -Vercel project → Settings → Security → "Secure backend access with OIDC -federation". Note: - -- **Issuer Mode**: Team or Global -- **Owner slug** (shown in the same panel) - -In this project: Team mode, owner slug `pavan-dev-singh-charaks-projects`. - -### 4.2 Create the IAM OIDC identity provider (as root/admin) - -``` -aws iam create-open-id-connect-provider \ - --url "https://oidc.vercel.com/YOUR_OWNER_SLUG" \ - --client-id-list "https://vercel.com/YOUR_OWNER_SLUG" \ - --profile parmana --region ap-south-1 -``` - -No `--thumbprint-list` needed — AWS auto-verifies via trusted CAs for -providers using well-known certificate authorities. - -For **Global** issuer mode instead, use `--url "https://oidc.vercel.com"` -(no owner-slug suffix) and adjust the trust policy's `Federated` ARN and -condition keys accordingly (drop the `/OWNER_SLUG` suffix throughout). - -### 4.3 Create the trust policy - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/oidc.vercel.com/YOUR_OWNER_SLUG" - }, - "Action": "sts:AssumeRoleWithWebIdentity", - "Condition": { - "StringEquals": { - "oidc.vercel.com/YOUR_OWNER_SLUG:aud": "https://vercel.com/YOUR_OWNER_SLUG", - "oidc.vercel.com/YOUR_OWNER_SLUG:sub": "owner:YOUR_OWNER_SLUG:project:YOUR_PROJECT_NAME:environment:production" - } - } - } - ] -} -``` - -**Scope the `sub` condition to `environment:production` only unless you -deliberately want preview/development deployments to also be able to -assume this role.** `project:YOUR_PROJECT_NAME` must match the exact -Vercel "Project Name" (Settings → General), not the deployment URL/domain. - -### 4.4 Create the role and its runtime-only policy - -``` -aws iam create-role --role-name parmana-vercel-kms-signer \ - --assume-role-policy-document file://trust-policy.json \ - --description "Assumed by Parmana's Vercel deployment via OIDC to sign with the gateway KMS key" \ - --profile parmana --region ap-south-1 -``` - -This role's permissions policy is deliberately **narrower** than the local -operator's — signing/verification only, no key management: - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "UseThisSigningKeyOnly", - "Effect": "Allow", - "Action": ["kms:Sign", "kms:GetPublicKey", "kms:DescribeKey"], - "Resource": "arn:aws:kms:REGION:ACCOUNT_ID:key/YOUR_KEY_ID" - } - ] -} -``` - -``` -aws iam create-policy --policy-name parmana-vercel-kms-runtime-policy \ - --policy-document file://runtime-policy.json --profile parmana --region ap-south-1 - -aws iam attach-role-policy --role-name parmana-vercel-kms-signer \ - --policy-arn "arn:aws:iam::ACCOUNT_ID:policy/parmana-vercel-kms-runtime-policy" \ - --profile parmana --region ap-south-1 -``` - -Note the role's ARN: `arn:aws:iam::ACCOUNT_ID:role/parmana-vercel-kms-signer`. - -## Part 5 — Vercel-side configuration - -### 5.1 Confirm the OIDC credentials package is installed - -``` -grep -r "@vercel/oidc-aws-credentials-provider" --include="package.json" . -``` - -Should already be a dependency of `packages/api` and `packages/crypto` -(`KmsSigner.ts` dynamically imports it only when `AWS_ROLE_ARN` is set). -If missing: `npm install @vercel/oidc-aws-credentials-provider` in both -packages. - -### 5.2 Link the repo and set environment variables - -``` -vercel login -vercel link --yes --project YOUR_PROJECT_NAME -``` - -Set three env vars, scoped to **Production only** (matching the trust -policy's `environment:production` condition): - -``` -vercel env add AWS_ROLE_ARN production -# value: arn:aws:iam::ACCOUNT_ID:role/parmana-vercel-kms-signer - -vercel env add AWS_REGION production -# value: ap-south-1 (or your region) - -vercel env add KEY_PROVIDER production -# value: aws-kms -``` - -> If `KEY_PROVIDER` already exists (common if the project predates this -> migration), `vercel env add` fails with `branch_not_found` / "variable -> already exists" — you must `vercel env rm KEY_PROVIDER production` then -> re-add it, or edit the value directly in the dashboard. **Check what it's -> currently set to first if you're not sure it's safe to change** — see -> the troubleshooting guide for why this specific value flip caused a full -> production outage the first time it was done here. - -### 5.3 Deploy and verify - -``` -vercel deploy --prod -``` - -Then, from any machine (no auth needed for this endpoint by design): - -``` -curl https://YOUR-PROJECT.vercel.app/keys/default -``` - -Expect a `200` with a real PEM-encoded Ed25519 public key and `algorithm: -"ed25519"`. A `500` here means something in Parts 1-5 isn't wired -correctly — go to the troubleshooting guide. - -``` -curl https://YOUR-PROJECT.vercel.app/health -``` - -Expect `{"status":"UP"}`. - -## What you should NOT do - -- Do not put `AWS_ROLE_ARN`/`AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY` - static credentials in Vercel for the deployed application's runtime - signing. The entire point of Part 4 is to avoid that. -- Do not delete `PARMANA_KEY_MATERIAL_JSON` from Vercel/`.env` immediately - after this guide. Keep it as a fallback until `KEY_PROVIDER=aws-kms` has - been live and verified stable for a real period of time — ADR-0009 - explicitly calls this out as a last, separate step, not part of the - cutover itself. -- Do not skip Part 3 (tightening the operator's policy after key creation) - — `kms:CreateKey` with no resource constraint is broader than the - operator needs for ongoing work. -- Do not assume `alias/default` is optional because `alias/parmana-ed25519-signer` - (or whatever descriptive name you chose) already exists — the code - specifically resolves the logical keyId `"default"`, not your - descriptive alias name. diff --git a/docs/operations/phase2a-deployment-readiness.md b/docs/operations/phase2a-deployment-readiness.md deleted file mode 100644 index c843ab79..00000000 --- a/docs/operations/phase2a-deployment-readiness.md +++ /dev/null @@ -1,216 +0,0 @@ -# Phase 2A.1 — Operational Readiness Review Before Production Redeploy - -An operational review, not a continuation of implementation. Phase 2A (commit `6eff8ec`) is implemented, tested, and documented in the repository; this review asks whether _deploying_ it is safe, using evidence gathered independently of the implementation work itself. No implementation code was modified during this review. Two temporary, read-only diagnostic scripts were created under `scripts/_tmp-audit-query*.mjs` to query a database (§10) and deleted immediately after use; `git status` at the end of this review shows no changes. - -**Reviewed against:** commit `6eff8ec` (`fix(production): remove MockConnector from production bootstrap (Phase 2A)`), the tip of `main`. Working tree was clean before this review began (verified: `git status`, `git diff --stat`, `git log --oneline -5`). - ---- - -## 1. Historical MockConnector Rationale (Task 1) - -**Repository evidence.** `git show ddf4bc5 -- packages/api/src/bootstrap/createVendorPaymentConnector.ts` shows the file's _original_, first-commit content (2026-07-09), already carrying this exact docstring: - -> "This is the production bootstrap for the vendor-payment connector. The current implementation uses MockConnector until the real enterprise connector is introduced." - -This is unambiguous, contemporaneous, first-commit evidence: **MockConnector was intentionally used as a temporary placeholder, self-documented as such from the moment it was written — not accidental, not undocumented technical debt that crept in silently.** - -Corroborating repository evidence for _why_ it was placed in production specifically (as opposed to only in tests): `docs/site/roadmap.mdx` (§"Move 2: Credential brokering") documents that `vendor-payment` was wired into the default server on commit `651497a` (2026-07-11) specifically to demonstrate the credential-isolation/brokering architecture claim — "This is real, tested, and running in the default server for the one connector currently registered (`vendor-payment`)." The roadmap frames `vendor-payment` as a **demonstration vehicle for an architectural claim (credential isolation)**, not as a committed real payment-processing feature. No roadmap entry, commit, or design document found commits to building a real vendor-payment integration on any timeline — unlike Razorpay and HubSpot, which have dedicated integration guides (`docs/site/integrations/{razorpay,hubspot}.mdx`) documenting real, credential-backed integrations. - -**Conclusion:** intentional, temporary, self-documented technical debt, used as an architecture demonstration vehicle. Not accidental. - -## 2. Production Usage Analysis (Task 2) - -**Code path, traced directly in source (repository evidence):** - -``` -POST /execute (packages/api/src/routes/execute.ts) ─┐ -POST /transactions (packages/api/src/routes/transactions.ts) ─┴─→ application.execute(transaction) - → ExecutionTrustApplication.execute() → Runtime.execute() → RuntimeEngine.execute() - → RuntimePipeline → ExecutionComponent → (injected) ExecutionSystem.execute() - → ExecutionGateway.execute() → ExecutionControlService.execute() - → registry.resolveCapability("payments:execute") ← the fail-closed point, Phase 2A - → SessionCredentialSecureConnector → SdkConnectorExecutor → MockConnector.execute() -``` - -Both HTTP routes require caller authentication (enforced on every route except health checks — confirmed by `README.md`/`docs/CLAIMS.md` and unchanged by Phase 2A). No unauthenticated path reaches this capability. - -**No scheduled job, cron, or worker invokes `payments:execute`.** Searched `scripts/` and `packages/api/src/webhooks/`: the only out-of-band worker is `RazorpaySettlementProcessor.ts` (Razorpay-specific, confirmed in Phase 1H certification), which never touches `vendor-payment`. `scripts/process-razorpay-settlements.ts` is likewise Razorpay-only. - -**A second, dead routing mechanism exists and was traced to rule it out as a second production path:** `createConnectorRoute.ts` (`packages/api/src/bootstrap/`) builds a `switch (action) { case "payments:execute": return "vendor-payment"; }` mapping, constructed in `createExecutionGateway.ts` and passed as `executionControl.route`. Read `ExecutionGateway.ts` directly (lines 230–260): `this.executionControl.route(...)` is only consulted inside the `this.executionControl.channel.release(...)` branch, reached only when `this.executionControl.service === undefined`. Production always sets `executionControl.service` (`createExecutionGateway.ts:51-52`), so production always takes the `service.execute(...)` branch — `createConnectorRoute()`'s mapping is constructed at every boot but never consulted. Confirmed: the fail-closed point Phase 2A fixed (`GatewayConnectorRegistry.resolveCapability`) is the **only** live capability-resolution mechanism. - -**Callers:** any holder of a valid API key in `PARMANA_API_KEYS` (confirmed present as a Fly secret on the production app, §10) submitting a `BusinessTransaction` with `intent.action: "payments:execute"`. - -**Expected frequency / business importance — repository evidence only, see §4 for why production evidence could not fully corroborate this:** `docs/site` never markets `vendor-payment` as a real integration; every reference to it is qualified ("a MockConnector," "the current implementation... until the real enterprise connector is introduced," "reference mocks, not integrations"). No customer-facing documentation, pricing page, or integration guide references it as a usable payment capability. This is architecturally and documentarily consistent with "demo/reference capability, not a marketed production feature," but is not, by itself, proof that zero real calls were ever made against it — see §4. - -## 3. Fail-Closed Review (Task 3) - -**Question: does any production workflow currently depend on `payments:execute` succeeding?** - -**Repository evidence, not speculation:** - -- Full test suite (`npm test -- --maxWorkers=2`) passes cleanly against the Phase 2A commit — 941 tests passed, 40 skipped, 0 failed (confirmed independently in this review, §9). If any _test_ depended on `payments:execute` unconditionally succeeding outside `NODE_ENV=test`, it would have failed; none did. -- No scheduled job or worker depends on it (§2). -- No other package's production code imports or calls anything vendor-payment-specific (`grep -rln "payments:execute|vendor-payment" packages/*/src` outside `packages/api/src` returns nothing). -- §4's Historical Data Integrity Assessment found zero evidence, across every `payments:execute` record in the one database this review could examine, of a caller identity other than this repository's own test suites. - -**Answer: no evidence found of any production workflow depending on `payments:execute` succeeding.** This conclusion rests on repository evidence (no code dependent on it) plus the database evidence in §4, which is strong but explicitly not 100%-certain proof about the live production environment specifically — see §4's classification and §13. - -## 4. Capability Readiness (Task 4) - -**Does a real production connector exist? No.** Searched `packages/execution-gateway/src/connector-execution/` (where every real vendor adapter lives, per the architecture Phase 1A–1H established and Phase 2A did not change): contains `GatewayRazorpayAdapter.ts`, `GatewayHubSpotAdapter.ts`, `GatewayHttpAdapter.ts` — no `GatewayVendorPaymentAdapter.ts` or equivalent. No real implementation exists anywhere in the repository. - -**Missing implementation:** a `GatewayVendorPaymentAdapter` (or similarly named class) implementing `connector-sdk`'s `Connector` interface, following the pattern `docs/developer/extending-parmana.md` documents under "Adding a new vendor." **Repository location it would go:** `packages/execution-gateway/src/connector-execution/`. **Planned future phase:** none found in-repo. `docs/site/roadmap.mdx` discusses `vendor-payment` only as the demonstration vehicle for the credential-brokering claim (itself flagged `[PARTIAL]`), not as a capability with a committed implementation timeline. - -**Confirmed: removing MockConnector does not silently disable an intended, functioning production capability** — there was never a functioning real capability behind it. Phase 2A's fail-closed behavior surfaces the true, pre-existing state (`payments:execute` has no real backing) rather than concealing it behind a scripted success. - -## 4.5 Historical Data Integrity Assessment (Task 4.5) - -**Evidence sources attempted, in order of strength:** - -1. **Production Trust Record / Receipt store — attempted, with a material caveat.** This session had access to Fly.io (`flyctl`, authenticated) and found `flyctl secrets list -a parmana-api` confirms the production app has `DATABASE_URL`, `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, and `PARMANA_STORAGE` configured as deployed secrets — i.e., production almost certainly persists to a real Postgres/Supabase-backed store, not in-memory. **Fly does not expose secret values** (`flyctl secrets list` shows names and digests only), so this session could not directly obtain or confirm the production `DATABASE_URL`. -2. **A Supabase database this session _could_ directly query** — credentials present in the local `.env` file (`DATABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`), explicitly documented by that file's own comment as backing "Live integration tests... Live tests create persistent rows" (i.e., a real, non-mocked Postgres database this repository's own test suite writes to when `ALLOW_LIVE_SUPABASE=1`). This session connected to it read-only (no writes, no schema changes) and queried it directly. -3. **Fly deployment logs** — `flyctl logs -a parmana-api` was checked (Phase 2A's live exposure assessment); it showed live traffic (Razorpay settlement polling) but the log window available did not extend back to cover the full 2026-07-09–2026-08-03 period, and does not itself distinguish real vs. test `payments:execute` calls. -4. **SSH into the running container** — attempted, failed on a network/tunnel limitation (documented in Phase 2A's report), not retried here. - -**What the queried database showed (direct query results, not inference):** - -| Finding | Evidence | -| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Total `payments:execute` records in `business_transactions` | 18, spanning 2026-07-11 through 2026-08-03 | -| Records with `authority.principalId = "ordering-test"` | 15 (transaction IDs prefixed `test-txn-` / `test-txn-race-`) | -| Records with `authority.principalId` / `metadata.createdBy = "integration-test"` | 3 (dated 2026-08-03 13:27–13:34 UTC — before that day's `v39` deploy at 14:14 UTC; `authority.displayName: "Integration Test"`, `authorization.purpose: "Integration Test"`) | -| Records attributable to any other identity | **0** | -| For comparison: `createdBy = "parmana-live-demo-client-server"` records in the same database | 18, but **none** with `action = "payments:execute"` — all are Razorpay actions | -| Trust Records for the 5 non-test-ID `EXECUTED` transactions | 5 found; 10 Receipts found (2 each) | -| Trust Records for the 3 `integration-test` `APPROVED` transactions | 3 found; **0 Receipts** (consistent with `ExecutionTrustApplication` only reaching `ReceiptService.generate()` after a successfully completed execution — these three show no `execution` sub-object in their Trust Record JSON at all, consistent with a test that exercises the policy/authorization stage without driving a full pipeline run) | - -**Every single `payments:execute` record found, without exception, carries an explicit, self-declared test identity.** This is real, direct, evidence-based finding — not an inference from implementation behavior. - -**What this evidence does not establish:** this session could not confirm that the database queried is the _same_ database the production Fly app's `DATABASE_URL`/`SUPABASE_URL` secrets point to. The `.env` file's own documentation frames this database's sanctioned purpose as "live integration tests," a different documented role than "production." No independent mechanism (secret-value comparison, a production audit-log cross-reference, a production-side query) was available to bridge that gap. - -**Classification: C. Unable to Certify.** - -- **Evidence available:** direct, positive evidence from one real, persistent database, evidence-based and specific, showing 100% test-attributed origin for every `payments:execute` record found in it; corroborating repository evidence (§1, §2, §3) that no code path depends on this capability and no marketing/documentation ever presented it as production-ready. -- **Evidence missing:** confirmation that the queried database is the production database; direct production database access; production access/request logs covering the full exposure window (2026-07-09 introduction through 2026-08-03, the last pre-fix deploy); Fly-side request logs old enough to cover that window. -- **Additional access that would resolve this:** a way to compare the production Fly app's `DATABASE_URL`/`SUPABASE_URL` secret values against the `.env` credentials used here (e.g., a `fly ssh console` session with a working tunnel, or a deliberate one-time confirmation from whoever manages the Supabase project that `db.REDACTED-PROJECT-REF.supabase.co` is or isn't the production instance). -- **Operational uncertainty:** if the queried database _is_ the production database, this review's finding is effectively "Confirmed Clean" in substance (zero non-test records found). If it is a _separate_ database, the production database's contents remain genuinely unexamined, and this classification of "Unable to Certify" stands on its literal terms. This review does not know which is true and states that directly rather than assuming either. - -## 5. Deployment Risks (Task 5) - -| Risk | Severity | Description | Affected capability | Operational consequence | Mitigation | -| ------------------------------------------------------------------------------------------------------------ | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Unknown real caller depends on `payments:execute` silently succeeding | **Medium** | §4.5's residual uncertainty (database identity unconfirmed) means this cannot be fully ruled out | `payments:execute` / `vendor-payment` | A real caller's request would start failing with `"No connector registered for capability 'payments:execute'"` instead of a fabricated success | Rollback plan (§6/Task 6) is fast and low-risk (Fly image redeploy, no data migration); monitor logs immediately post-deploy for `vendor_payment_connector_unavailable` warnings and any `payments:execute` 4xx/5xx spike | -| `docs/site` now describes fail-closed behavior that production hasn't shipped yet | **Low** | Phase 2A updated public docs to describe the fixed behavior before the fix is deployed | Documentation accuracy only | A reader could be briefly misled about current production behavior between commit and deploy | Self-resolving on deploy; short window, no user-facing safety impact (the docs describe a _safer_ state than what's currently live, not a more dangerous one) | -| Local/demo environments not setting `NODE_ENV=test` now fail closed where they previously silently succeeded | **Low** | Matches every other bootstrap factory's existing convention exactly (Phase 2A didn't introduce a new pattern) | Local dev / ad hoc demo runs of `payments:execute` specifically | A developer relying on implicit MockConnector behavior without `NODE_ENV=test` would now see a clear, named failure instead of silent success | Already documented in Phase 2A's report and updated `docs/site` pages; failure is explicit and immediately diagnosable, not a silent behavior change | -| Fly deploy itself (mechanical) | **Low** | Standard `fly deploy` of a codebase that passes `tsc -b`, full test suite, lint, typecheck | Whole app, not just this capability | Standard deploy risk (brief connection draining during machine replacement, per `fly.toml`'s `min_machines_running: 1` / 2-machine topology) | Existing Fly rolling-deploy behavior, unchanged by this fix; 2 machines already running, `auto_start_machines: true` | -| Historical Data Integrity classification is "Unable to Certify," not "Confirmed Clean" | **Medium** (procedural, not technical) | Per this review's own success criterion, this alone changes the required recommendation from READY TO DEPLOY to READY WITH CONDITIONS or NOT READY | N/A — governs the recommendation, not the code | See §9 for the recommendation and conditions | Conditions specified in §9 | - -No risk found rises to **High** — no evidence of active exploitation, no evidence of a real dependent workflow, and the fix's mechanism (fail closed, matching pre-existing patterns) is itself low-risk and already fully tested. - -## 6. Operational Impact - -- **In production, immediately after deploy:** any `payments:execute` request will fail with a named `500`-class error (`"No connector registered for capability 'payments:execute'."`) instead of a `200` with a fabricated Trust Record/Receipt. This is the entire intended effect of Phase 2A. -- **All other capabilities (Razorpay, HubSpot, every other route) are unaffected** — Phase 2A's diff touches exactly two files, both scoped to `vendor-payment` registration only (confirmed in Phase 2A's own report and re-verified here: `git diff --stat -- packages/*/src/` shows exactly `createConnectorRegistry.ts` and `createVendorPaymentConnector.ts`). -- **No data migration, schema change, or backfill is required or performed.** This is a behavior-only change in what gets registered at boot. - -## 7. Rollback Plan (Task 6 — prepared, not executed) - -**Rollback trigger:** any of — - -- A real caller reports a `payments:execute` failure they weren't expecting (i.e., evidence emerges that §3/§4's "no dependent workflow" conclusion was wrong). -- Elevated error rates or `vendor_payment_connector_unavailable` log volume inconsistent with expected (near-zero) traffic. -- Any unrelated regression surfaces post-deploy that correlates with this release. - -**Symptoms to watch for:** `payments:execute` requests returning errors where a caller expects success; the new `console.warn({ event: "vendor_payment_connector_unavailable", ... })` log line appearing at a rate inconsistent with "expected to be silent" (§3). - -**Rollback procedure:** - -1. Identify the current (post-Phase-2A-deploy) release version: `flyctl releases -a parmana-api`. -2. Redeploy the last known-good pre-Phase-2A image, confirmed by this review to be **`registry.fly.io/parmana-api:deployment-01KZ3ZJEXK4RFV440R84E4E2S9`** (release `v39`, deployed 2026-08-03 14:14 UTC — the image currently live at the time of this review): `flyctl deploy -a parmana-api --image registry.fly.io/parmana-api:deployment-01KZ3ZJEXK4RFV440R84E4E2S9`. -3. No database rollback is needed — Phase 2A made no schema change and no data migration; rolling back the application image is sufficient. - -**Verification after rollback:** - -- `flyctl status -a parmana-api` — both machines `started`, health checks passing. -- `curl https://parmana-api.fly.dev/health` — `{"status":"UP"}`. -- Confirm `payments:execute` again resolves to `vendor-payment` (MockConnector) as before Phase 2A, via a caller-authenticated request or the existing `create-connector-registry.test.ts` suite run against the rolled-back commit. - -**Expected recovery time:** a Fly image redeploy of an already-built image (no rebuild needed) typically completes in the low single-digit minutes; this app's `min_machines_running: 1` and 2-machine topology mean the service should remain available throughout the rollback (rolling replacement), not experience a hard outage. - -## 8. Deployment Checklist (Task 7) - -- [ ] **Production backup** — confirm the Supabase/Postgres backing store has automatic backups enabled (Supabase managed backups) before deploying; Phase 2A itself requires no data backup (no migration), but standard practice for any production deploy. -- [ ] **Deployment approval** — explicit user/operator sign-off to redeploy `parmana-api`, obtained before running `fly deploy` (this assistant will not deploy without it, per its own operating guidelines and Phase 2A's own report). -- [ ] **Rollout steps** — `git push` the Phase 2A commit(s) if not already pushed; `fly deploy` from the reviewed commit (`6eff8ec` or later, including this review's non-code documentation, if committed). -- [ ] **Health verification** — `flyctl status -a parmana-api` shows both machines `started` with passing checks; `curl https://parmana-api.fly.dev/health` returns `{"status":"UP"}`. -- [ ] **Connector registration verification** — check post-deploy logs for the new `vendor_payment_connector_unavailable` warning at startup (expected, confirms the fix is live) and confirm Razorpay/HubSpot registration logs (or absence of their unavailable-warnings, if credentials are configured) are unchanged from pre-deploy behavior. -- [ ] **`payments:execute` verification** — submit one authenticated `POST /execute` (or `/transactions`) request with `intent.action: "payments:execute"` against the live app and confirm it now fails closed with `"No connector registered for capability 'payments:execute'"` rather than succeeding. -- [ ] **Logs verification** — `flyctl logs -a parmana-api` monitored for at least one deploy cycle after rollout for any unexpected error-rate change, especially anything correlating with `payments:execute` or `vendor-payment`. -- [ ] **Rollback decision point** — if any checklist item above fails, or if real-caller impact is reported within the first monitoring window, execute §7's rollback procedure immediately rather than attempting a forward fix under live pressure. - -## 9. Deployment Recommendation - -**READY WITH CONDITIONS.** - -Per this review's own success criterion, the Historical Data Integrity Assessment result (§4.5: **C. Unable to Certify**) precludes a bare READY TO DEPLOY recommendation, regardless of how strong the supporting evidence is. It does not, on the evidence gathered, warrant NOT READY either: - -**Why READY WITH CONDITIONS, not NOT READY:** - -- Every piece of repository, code-path, and database evidence gathered (§1–§4.5) points the same direction: this was intentional, self-documented, temporary demo-only technical debt; no code path depends on it succeeding; no real connector was ever displaced by fixing it; and every historical record of its use found in a real, persistent database is explicitly test-attributed. -- The residual uncertainty is narrow and specific: whether the one database this review could query is the _same_ database production uses. It is not a broad, unresolved question about the fix's correctness or safety — the fix itself is fully tested and behaves identically to the already-proven Razorpay/HubSpot fail-closed pattern. -- The operational risk of the fix behaving _differently than intended_ is separately and independently near-zero (§9's supporting evidence is code-level, not just database-level). - -**Required conditions before or immediately upon deploy:** - -1. Complete the deployment checklist in §8 in full, in order, including the rollback decision point. -2. Treat the first monitoring window post-deploy as the resolution mechanism for the §4.5 uncertainty: if `payments:execute` traffic appears at any real volume post-deploy (via the new warning log or error-rate monitoring) where §3 concluded none was expected, that is new evidence this review did not have, and should trigger the rollback procedure (§7) pending investigation, not a forward fix. -3. If practical, resolve the database-identity question directly (compare `DATABASE_URL`/`SUPABASE_URL` values, or ask whoever manages the Supabase project) before or shortly after deploy, to convert this review's "Unable to Certify" into either a documented "Confirmed Clean" (closing this condition) or a "Confirmed Exposure" follow-up (if the databases differ and the production one shows different history). - -## 10. Repository Evidence - -- `git log --follow`, `git show ddf4bc5` — origin and original intent of `createVendorPaymentConnector.ts`. -- `docs/CLAIMS.md` §3, `docs/VERIFICATION-GAPS.md` G-24 — documents a "live" adversarial PoC against `vendor-payment/2.0.0`, explicitly run against "an isolated disposable clone (no production system, no real credentials, no live traffic)" and "again against this repository directly after the fix... via a freshly built, isolated clone" — corroborating, not contradicting, this review's finding that documented use of this capability has been test/demo-scoped. -- `docs/site/roadmap.mdx` — `vendor-payment`'s role as a credential-brokering demonstration vehicle, no committed real-implementation timeline. -- `packages/execution-gateway/src/connector-execution/` directory listing — confirms no real adapter exists. -- `packages/api/src/bootstrap/{createConnectorRoute,createExecutionGateway}.ts`, `packages/execution-gateway/src/ExecutionGateway.ts` lines 230–260 — confirms the dead second routing mechanism is never live. -- `packages/storage/schemas/*.sql` — table schemas used to construct the read-only diagnostic queries in §4.5. -- Full test suite run against `6eff8ec` (this review, §9 below) — 941 passed, 40 skipped, 0 failed. - -## 11. Deployment Evidence - -- `flyctl auth whoami` — authenticated access confirmed (`charak1987@gmail.com`). -- `flyctl status -a parmana-api` — 2 machines `started`, checks passing, current image `deployment-01KZ3ZJEXK4RFV440R84E4E2S9` (release `v39`). -- `flyctl releases -a parmana-api` / `flyctl releases --image -a parmana-api` — full release history and image references, used to identify the exact rollback target (§7). -- `flyctl secrets list -a parmana-api` — confirms `DATABASE_URL`, `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, `PARMANA_STORAGE`, `VENDOR_PAYMENT_TOKEN`, `PARMANA_API_KEYS`, and the Razorpay secrets are all configured in production (names/digests only — values not retrievable via this tool). -- `flyctl logs -a parmana-api` — confirms live traffic (Razorpay settlement polling) at review time; did not extend back far enough to cover the full historical exposure window. -- `flyctl ssh console` — attempted, failed (network/tunnel limitation, not a permissions denial), not retried. - -## 12. Production Evidence - -- A direct, read-only query against a real, persistent Postgres/Supabase database whose connection credentials were found in the local `.env` file, documented there as backing "live integration tests." Full findings in §4.5. **This review could not independently confirm this database is the production database** — it is reported here as the strongest evidence obtained, with that caveat stated explicitly rather than assumed away. -- No other production-side evidence (production request logs for the full historical window, production-side query access confirmed as _the_ production instance, APM/telemetry) was available to this review. - -## 13. Remaining Uncertainties - -- **Database identity** (§4.5, §9): whether the queried Supabase database is the same one backing the deployed `parmana-api` Fly app. This is the single uncertainty driving the READY WITH CONDITIONS (rather than READY TO DEPLOY) recommendation. -- **Historical window coverage**: even if the queried database is confirmed as production, this review's queries cover whatever rows currently exist in that database as of this review's execution — not a guarantee that no row was ever created and later deleted, though nothing in the repository's data model suggests any deletion path exists for `business_transactions`/`execution_trust_records`/`receipts` (all `ON DELETE RESTRICT` foreign keys, no delete routes found in `packages/api/src/routes/`). -- **Fly SSH access**: unavailable in this session's network environment; would have allowed direct filesystem/env inspection of the running production container, closing the database-identity question directly. -- **Real-world caller behavior between this review and an actual deploy**: this review reflects a point-in-time snapshot; if a real caller begins using `payments:execute` between this review and the eventual deploy, §3's conclusion would need re-verification — the checklist's monitoring step (§8) exists specifically to catch this. - ---- - -## Final Verification - -| Item | Status | -| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -| Historical MockConnector rationale understood | ✓ — intentional, temporary, self-documented (§1) | -| Production usage analysis complete | ✓ — exactly two HTTP routes, no workers/jobs, one dead alternate routing path ruled out (§2) | -| Fail-closed operational acceptability assessed | ✓ — no dependent workflow found (§3) | -| Capability readiness confirmed | ✓ — no real connector exists or is silently disabled; none was ever functioning (§4) | -| Historical Data Integrity Assessment | **C. Unable to Certify** (§4.5) — strong exculpatory evidence from one database, database identity vs. production unconfirmed | -| Deployment risks assessed | ✓ — no High risk found (§5) | -| Rollback plan prepared (not executed) | ✓ (§7) | -| Deployment checklist produced | ✓ (§8) | -| Implementation modified during this review | **No** — read-only review; two temporary diagnostic scripts created and deleted, no repository files changed | - -**Final Recommendation: READY WITH CONDITIONS** — see §9 for the specific conditions required before and immediately after deploy. diff --git a/docs/operations/phase2a-deployment-verification.md b/docs/operations/phase2a-deployment-verification.md deleted file mode 100644 index d2aaa9e5..00000000 --- a/docs/operations/phase2a-deployment-verification.md +++ /dev/null @@ -1,160 +0,0 @@ -# Phase 2A.2 — Controlled Production Deployment & Post-Deployment Verification - -Executes and verifies the production deployment of Phase 2A, approved after Phase 2A.1's independent operational readiness review (`docs/operations/phase2a-deployment-readiness.md`, recommendation: READY WITH CONDITIONS, explicitly accepted by Pavan for this deployment). This is a deployment and verification phase, not an implementation phase — no application code was modified during this phase. - ---- - -## Preconditions Verified Before Deploying - -**Repository state, confirmed clean:** - -``` -git status → clean, no uncommitted changes -git log --oneline -5: - 10c8064 docs(operations): independent readiness review before Phase 2A redeploy - 6eff8ec fix(production): remove MockConnector from production bootstrap (Phase 2A) - ... -``` - -Phase 2A (`6eff8ec`) and Phase 2A.1 (`10c8064`) both committed. - -**Phase 2A.1's three explicit conditions, quoted and individually addressed:** - -1. _"Complete the deployment checklist in §8 in full, in order, including the rollback decision point."_ — This condition is executed by this very phase: §8's checklist items map directly onto the Post-Deployment Verification sections below (build identity, connector registration, fail-closed check, logs, rollback decision), all completed in order and recorded here. -2. _"Treat the first monitoring window post-deploy as the resolution mechanism for the §4.5 uncertainty..."_ — Executed: see §6 (Production Monitoring) below; no `payments:execute` traffic appeared in the observation window, consistent with §3's prediction. -3. _"If practical, resolve the database-identity question directly..."_ — Attempted: see §7 (Historical Data Integrity Status) below. Narrowed but not fully closed. - -**Other preconditions:** - -- Rollback plan: exists (Phase 2A.1 §7), with the exact pre-deploy image identified. -- Production backup: not independently re-verified this session (Supabase-managed backups assumed baseline, as in Phase 2A.1); irrelevant to deploy risk since Phase 2A makes no schema/data change. -- **Deployment approval: obtained.** Pavan's message explicitly states: _"I have reviewed the Phase 2A.1 Operational Readiness Report... I accept the documented deployment conditions and residual operational risk... You are approved to proceed with Phase 2A.2."_ - -## 1. Deployment Summary - -Deployed the exact reviewed Phase 2A commit to `parmana-api` on Fly.io via a dedicated git worktree checked out at that commit (not the repository's working directory, which was one commit ahead — see §2), to guarantee byte-for-byte artifact identity with what Phase 2A.1 reviewed. Both production machines updated via Fly's standard rolling-deploy strategy; one machine additionally restarted post-deploy to capture direct startup-log evidence (§4). - -## 2. Commit Deployed - -**Deployed commit:** `6eff8ecbc67b2610ffd1cbcf8a94676bdec31981` (`fix(production): remove MockConnector from production bootstrap (Phase 2A)`) — the exact commit Phase 2A.1 reviewed. - -**Note on repository HEAD vs. deployed commit:** at deploy time, `main`'s HEAD was `10c8064` (Phase 2A.1's report, one commit ahead of `6eff8ec`). Rather than deploying HEAD and treating the discrepancy as approximate, a git worktree was created at the exact commit `6eff8ec` (`git worktree add 6eff8ec`), verified clean (`git status --porcelain=v1 -uall` → empty) and at the exact SHA, and the deploy was run from inside that worktree. `git diff --stat 6eff8ec..HEAD -- . ':!docs/operations'` was also run beforehand and confirmed zero difference outside `docs/operations/` — i.e., even had HEAD been deployed instead, the built artifact would have been identical, since documentation changes don't enter the Docker build. This is stated for completeness, not because it was relied upon: the deploy was performed from the exact reviewed SHA specifically, not from HEAD. - -**No additional commits included; no uncommitted changes** — confirmed by the worktree's clean `git status` immediately before running `flyctl deploy`. - -## 3. Deployment Evidence - -| Field | Value | -| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Deployment target | Fly.io app `parmana-api` (`fly.toml`: `app = 'parmana-api'`) | -| Deployment command | `flyctl deploy --app parmana-api`, run from the `6eff8ec` worktree | -| Deployment start | 2026-08-05T02:31:17Z (pre-deploy state captured) | -| Deployment completion | 2026-08-05T02:33:58Z (both machines confirmed `started`, health checks passing) | -| New release | `v40`, `registry.fly.io/parmana-api:deployment-01KZ7W5ZZF13WV46E6TBNTVFE6` | -| Previous release (rollback target) | `v39`, `registry.fly.io/parmana-api:deployment-01KZ3ZJEXK4RFV440R84E4E2S9` (unchanged from Phase 2A.1's identification) | -| Deployment result | **Success** — both machines (`2867e1ef14d648`, `6830243f236d98`) reached `started` state, 1/1 health checks passing, confirmed via `flyctl status` and `flyctl image show` (both machines report identical image digest `sha256:72ef1f662b852de6fdc7492ccbeac011d3847882865da8d8727c9cb55754e859`) | - -One transient warning appeared during the rolling update (`"The app is not listening on the expected address..."`) immediately followed by `"Machine ... is now in a good state"` for the same machine — consistent with a health-check race during the brief window before the new process bound its port, not a persistent condition; both machines were confirmed healthy seconds later and remained so through the entire verification window. - -Build identity is additionally supported by direct chain-of-custody: this session personally created the worktree, verified its SHA and clean status, invoked the deploy from inside it, and observed the full Docker build log (`COPY packages ./packages`, `RUN npx tsc -b`, etc.) execute against that exact tree. - -## 4. Connector Registration Verification - -**MockConnector is NOT registered — confirmed by direct production runtime evidence**, not inference. `flyctl logs`'s buffer did not retain logs back to the original rolling-deploy boot time, so machine `6830243f236d98` was restarted (`flyctl machine restart`, a routine, low-risk operation already proven safe seconds earlier during the deploy's own rolling restart) specifically to capture a fresh startup log. The captured production log, verbatim: - -``` -event: 'vendor_payment_connector_unavailable', -reason: 'No real vendor-payment implementation exists outside NODE_ENV=test; -payments:execute is not registered in production rather than being served by -MockConnector (Phase 2A — see docs/architecture/phase2a-production-connectors.md).' -``` - -This is the exact `console.warn` call written into `createConnectorRegistry.ts` by Phase 2A, now confirmed firing in the live production process. **✓ MockConnector is not registered. ✓ `payments:execute` has no production mock — confirmed directly, not inferred from source code alone.** - -Both machines run the identical image (§3), so this finding applies to both, though only one was directly observed at boot (restarting the second machine solely to duplicate this observation was judged unnecessary — the least-destructive-verification principle argues against redundant production restarts once the image identity is already confirmed). - -## 5. Fail-Closed Verification - -**No live authenticated `payments:execute` HTTP request was made against production.** This session does not hold a valid production caller API key (`PARMANA_API_KEYS` is a Fly secret, not retrievable), and obtaining or guessing one to test this would itself be an inappropriate, unauthorized production action — explicitly the kind of "unsafe production operation" this phase's instructions prohibit. This is stated directly rather than glossed over. - -**The least-destructive, legitimately-available verification was used instead: direct log evidence combined with deterministic code tracing, not speculation.** - -1. §4's production log confirms `vendor-payment` is not present in the connector registry. -2. `GatewayConnectorRegistry.resolveCapability()` (`packages/execution-gateway/src/connector-execution/GatewayConnectorRegistry.ts:152-168`) is a closed, deterministic loop over registered connectors; with `vendor-payment` absent (confirmed, §4), any call with `capability: "payments:execute"` **must** reach the function's final `throw new Error("No connector registered for capability 'payments:execute'.")` — there is no other code path. -3. `ExecutionControlService.execute()` calls this synchronously with no surrounding `try`/`catch` (traced and quoted in Phase 2A's own report, re-confirmed unchanged by this deploy since `packages/execution-control/src` was not touched by Phase 2A), so the throw occurs before session creation, before credential issuance, before any audit "session.created" event. -4. `RuntimeEngine.execute()`'s only catch block re-throws rather than swallowing (also unchanged). -5. This exact mechanism was mutation-tested in Phase 2A (`git stash` the fix, confirm tests fail, restore, confirm they pass) and is the same mechanism already relied on for Razorpay/HubSpot's fail-closed behavior in this same production process — HubSpot's `hubspot_connector_unavailable` warning fired in the very same startup log (§4), and Razorpay's connector registered successfully (no warning), demonstrating the identical registration-guard code executing correctly for all three connectors side by side, in production, at the same boot. - -**✓ No Trust Record can be created** (persisted only after `RuntimeEngine.execute()` returns successfully; it cannot, given 2-4 above). **✓ No Receipt can be created** (reached only after a persisted Trust Record). **✓ No vendor evidence can be created** (no connector is ever invoked). **✓ No fabricated success is possible** (the request throws before any success artifact exists). This conclusion is deterministic given §4's confirmed registry state, not an assumption about behavior — the same standard of evidence Phase 2A's own tests were held to. - -## 6. Production Monitoring - -Reviewed post-deploy logs across both machines (live tail and `--no-tail` buffer) for: deployment logs, application logs, startup warnings, connector warnings, runtime errors. - -- **Startup warnings:** `vendor_payment_connector_unavailable` (expected, §4) and `hubspot_connector_unavailable` (pre-existing, confirmed unrelated to this deploy — `flyctl secrets list` shows no `HUBSPOT_PRIVATE_APP_TOKEN` configured, identical to Phase 2A.1's finding). -- **Runtime errors:** none found (`grep -iE "error|fail|warn|500|payments:execute"` across the live log window, excluding the routine settlement-poller's own `failed: 0` counter field, returned no matches). -- **One transient, self-correcting anomaly:** immediately after the machine restart (§4), the first `razorpay_settlement_poll_tick` reported `failed: 17` (vs. the steady-state `failed: 0`); the next tick, 7 seconds later, and every tick after it through the end of the observation window reported `failed: 0`, matching both the other machine's continuous behavior and the pre-deploy baseline from Phase 2A.1. Consistent with a cold-start settlement-queue re-evaluation, not a regression. -- **`payments:execute` traffic:** none found. No request, successful or failed, referencing this capability appeared anywhere in the observed log window (live tail from ~02:35:47Z through ~02:39:40Z, plus the full restart/boot sequence of `6830243f236d98`). This is the specific signal §3 of Phase 2A.1 predicted ("no evidence found of any production workflow depending on `payments:execute`") and the specific signal this deploy's condition #2 asked to be monitored for. - -**No caller, no endpoint hit, no outcome to document for `payments:execute` — because none occurred in the observation window.** - -## 7. Historical Data Integrity Status - -Phase 2A.1 classified this **C. Unable to Certify**, because the Supabase database this session could query (credentials in `.env`, documented there as backing "live integration tests") could not be confirmed as the same database backing production's `DATABASE_URL`/`SUPABASE_URL` Fly secrets. - -**New evidence obtained this session, attempting to resolve condition #3:** - -- The Supabase CLI (`supabase`, authenticated) was available and was not used in Phase 2A.1. `supabase projects list` returned exactly two projects on this account: `REDACTED-PROJECT-REF`, named **"REDACTED-PROJECT-NAME"**, region `ap-southeast-2`, created `2026-06-17` — and `REDACTED-SANDBOX-PROJECT-REF`, named **"REDACTED-SANDBOX-NAME"**, region `ap-south-1`, created `2026-08-04` (the day before this deploy), not linked to this repository. -- `REDACTED-PROJECT-REF`'s host (`db.REDACTED-PROJECT-REF.supabase.co`) is the **exact same host** as the `DATABASE_URL` found in the local `.env` (already known from Phase 2A.1) — this CLI lookup independently corroborates that hostname's project identity and name for the first time. -- This repository's own `supabase/.temp/linked-project.json` (CLI-generated local state, not committed) confirms this repository's Supabase CLI is linked specifically to `REDACTED-PROJECT-REF` ("REDACTED-PROJECT-NAME"), not the sandbox project. -- `flyctl ssh console` was retried (attempting to read production's actual resolved `DATABASE_URL`/`SUPABASE_URL` environment variables directly, redacting credentials before display) and **failed again**, identical error to Phase 2A.1 (`websocket: failed to WebSocket dial... connection attempt failed`) — confirmed as a persistent network/tunnel limitation of this environment across two independent sessions, not a transient or permissions issue. Not retried a third time, per this review's own guidance against rabbit-holing on a repeatedly-failing tool call. -- No mechanism was found to compare Fly's secret digests (`flyctl secrets list` shows digests, not values, with an undocumented hashing scheme) against a locally-computed value, so this avenue remains closed. - -**Classification: Still Unable to Certify.** The uncertainty is narrower than Phase 2A.1 left it — this is now known to be the one Supabase project this repository's own tooling associates with itself, distinctly named apart from the separate, newer "sandbox" project, rather than an anonymous database of unknown relationship — but "narrower" is not "closed." No mechanism available to this session could produce a byte-for-byte confirmation that Fly's production `DATABASE_URL` secret resolves to this project. The database's _content_ was not re-queried this session (nothing in this deployment would have changed it, and Phase 2A.1's findings — 18 `payments:execute` records, 100% test-attributed — stand unchanged). - -**Recommended follow-up to close this fully:** a one-step confirmation from whoever manages the Fly/Supabase organization (`REDACTED-ORG-SLUG`) that project `REDACTED-PROJECT-REF` ("REDACTED-PROJECT-NAME") is or isn't what `parmana-api`'s `DATABASE_URL`/`SUPABASE_URL` secrets point to — now a much narrower, specific question than Phase 2A.1 could pose. - -## 8. Rollback Decision - -**Decision: Continue operating. No rollback.** - -Evidence supporting this decision: - -- Build identity confirmed (§3). -- Connector registration confirmed correct — MockConnector absent, exactly as intended (§4). -- Fail-closed behavior confirmed deterministic, given §4's evidence (§5). -- Existing production behavior (health, Razorpay connector registration, HubSpot's pre-existing unavailable state, settlement poller) all unchanged from pre-deploy baseline except the one intended change (§6). -- Zero `payments:execute` traffic, zero errors, zero unexpected warnings in the observation window (§6). -- No rollback trigger from Phase 2A.1 §7 fired (no real-caller impact reported, no elevated error rate, no unrelated regression). - -None of the three rollback triggers defined in Phase 2A.1 §7 have occurred. The rollback plan (image `deployment-01KZ3ZJEXK4RFV440R84E4E2S9`, `flyctl deploy -a parmana-api --image registry.fly.io/parmana-api:deployment-01KZ3ZJEXK4RFV440R84E4E2S9`) remains documented and ready if needed later. - -## 9. Remaining Operational Risks - -- **Database-identity question (§7)** remains open, narrower but unresolved. If it's later confirmed that the queried database is _not_ production, the Historical Data Integrity Assessment reverts to fully unexamined for the real production store, and a fresh assessment against the actual production database would be warranted. -- **Observation window is short** (this session's monitoring covers roughly the 10 minutes following deploy, not a full business cycle). Continued passive monitoring for `payments:execute` traffic and the `vendor_payment_connector_unavailable` warning's rate over the following days is prudent, consistent with Phase 2A.1's condition #2, though nothing in this session's evidence suggests it's necessary to block on. -- **`VENDOR_PAYMENT_TOKEN` Fly secret is now orphaned** — it was never consumed by `MockConnector` even before this fix (MockConnector ignores credentials entirely) and now backs a capability that isn't registered at all. No security or operational risk (unused secrets don't grant capability), just housekeeping; not addressed in this phase as it's outside this phase's scope (deployment verification, not cleanup). - -## 10. Final Recommendation - -**DEPLOYMENT VERIFIED WITH FOLLOW-UP.** - -Not an unconditional **DEPLOYMENT VERIFIED**, specifically because one item from the Final Recommendation's required list cannot be fully demonstrated: - -| Requirement | Status | -| ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Deployed commit exactly matches the reviewed commit | ✓ — `6eff8ec`, deployed from a verified-clean worktree at that exact SHA (§2, §3) | -| Deployment succeeded | ✓ (§3) | -| MockConnector absent from production | ✓ — confirmed by direct production log evidence, not inference (§4) | -| Fail-closed behavior verified | ✓ — deterministically, from confirmed registry state plus unchanged, previously-tested code paths (§5); **not** verified by an actual live authenticated HTTP call, which this session correctly declined to attempt without legitimate credentials | -| No unexpected operational impact | ✓ — no errors, no unexpected traffic, one transient self-correcting anomaly explained (§6) | -| No production security regression | ✓ — no security-relevant code touched; connector-registration behavior change only | -| No rollback criteria triggered during the observation window | ✓ (§8) | - -**Why "WITH FOLLOW-UP" rather than an unconditional verified:** the Historical Data Integrity Assessment (§7) remains **Still Unable to Certify** — narrowed by new evidence this session (the Supabase CLI project-identity finding), but not closed. This mirrors Phase 2A.1's own instruction precedent (an Unable to Certify classification changes the recommendation tier) and this phase's explicit rule not to infer success where evidence is incomplete. Every other requirement is fully satisfied with direct evidence; this is the one specific, narrow, named gap. - -**Required follow-up:** the one-step organizational confirmation named in §7. No code, configuration, or deployment action is required — this is a documentation/confirmation step that, once obtained, converts this report's §7 classification to Confirmed Clean (expected, given all `payments:execute` records found are test-attributed) and would let the record be updated accordingly without any further deployment action. - ---- - -**Addendum (2026-08-05, Phase 2A.4):** the follow-up named above has since been completed. Phase 2A.3 (`docs/operations/phase2a-historical-integrity-verification.md`) resolved the database-identity question directly against production evidence (production's own `/ready` endpoint's `SELECT 1` observed, timestamp-correlated, in the historically-queried database) and classified the historical record **HISTORICAL INTEGRITY CONFIRMED** — zero non-test-attributed records. TD-1 is now closed; canonical evidence chain in `docs/operations/td1-closure-summary.md`. This report's own findings and evidence above remain accurate and unedited as the record of what Phase 2A.2 itself verified. diff --git a/docs/operations/phase2a-historical-integrity-verification.md b/docs/operations/phase2a-historical-integrity-verification.md deleted file mode 100644 index 411b6c5f..00000000 --- a/docs/operations/phase2a-historical-integrity-verification.md +++ /dev/null @@ -1,115 +0,0 @@ -# Phase 2A.3 — Historical Audit Database Identity Verification - -Resolves the single remaining follow-up from Phase 2A.2 (`DEPLOYMENT VERIFIED WITH FOLLOW-UP`): whether the "REDACTED-PROJECT-NAME" Supabase database inspected during Phases 2A.1 and 2A.2 is the same database currently configured for the deployed production `parmana-api` application. This is a verification-only phase; no code, configuration, or deployment was changed. All database interaction was read-only (`SELECT` and metadata inspection only — confirmed below). - ---- - -## Preconditions Verified - -``` -git status → clean -git log --oneline -5: - 0c19f78 docs(operations): verify Phase 2A production deployment (Phase 2A.2) - 10c8064 docs(operations): independent readiness review... (Phase 2A.1) - 6eff8ec fix(production): remove MockConnector from production... (Phase 2A) - ... -``` - -Phase 2A, 2A.1, and 2A.2 all committed; working tree clean before this phase began. - -## Method, and the Read-Only Constraint - -Three approaches were attempted, in order of increasing directness, all read-only: - -1. **Fly secret digest comparison.** `flyctl secrets list -a parmana-api` exposes a `DIGEST` per secret (16 hex characters) but not the value. Computed SHA-256, SHA-1, MD5, and SHA-512 (full digest, first-16, and last-16 hex) of the local `.env`'s `DATABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, and constructed candidate `SUPABASE_URL` values, comparing against Fly's displayed digests. **No match** — Fly's digest algorithm is not a plain hash of the secret value reproducible without Fly's internal implementation, so this avenue could not establish identity (a negative result, not evidence either way). -2. **Passive connection-pattern inspection.** Queried `pg_stat_activity` on the database reachable via the local `.env` credentials (project `REDACTED-PROJECT-REF`, "REDACTED-PROJECT-NAME") for connections correlating in time with observed production events (a Razorpay settlement-poller tick, captured from live Fly logs). Inconclusive — the application's Postgres pool (`pg.Pool`, default `idleTimeoutMillis: 10000`) closes idle connections faster than the poller's 15-second tick interval, so passive snapshots could plausibly miss a real connection even if one existed. -3. **Active, self-triggered correlation via the public `/ready` endpoint — conclusive.** `packages/api/src/routes/ready.ts` is explicitly exempt from caller authentication (`packages/api/src/app.ts:113`, the same exemption list as `/health`) and, when `PARMANA_STORAGE=supabase`, executes a literal `SELECT 1` against `PostgresPoolFactory.create()` — the exact same pool `DATABASE_URL` configures for the whole application, including the Razorpay webhook event store. This is public, unauthenticated, and reveals no secret — only a readiness boolean. - -**No write operation was performed or required at any point.** Every query executed against the Supabase database was a `SELECT` (`pg_stat_activity`, `business_transactions`, `execution_trust_records`, `receipts`) or a metadata read. No `INSERT`/`UPDATE`/`DELETE`, no schema change, no secret modification. `curl .../ready` and `curl .../health` are both side-effect-free GET requests. - -## 1. Production Database Identity - -**Repository/infrastructure evidence:** `flyctl secrets list -a parmana-api` confirms `DATABASE_URL`, `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, and `PARMANA_STORAGE` are configured as deployed Fly secrets (names/digests only, unchanged from Phase 2A.1/2A.2). - -**Direct production evidence, newly obtained this phase:** - -- `curl https://parmana-api.fly.dev/ready` → `{"status":"READY"}` (not `{"status":"READY","storage":"not-supabase-backed"}`) — proves, from the route's own conditional logic (`ready.ts:30`), that production has `PARMANA_STORAGE === "supabase"` and that a real `SELECT 1` against the configured `DATABASE_URL` succeeded, at the moment of the call. -- Immediately after that call (two independent, back-to-back trials), `pg_stat_activity` on the "REDACTED-PROJECT-NAME" database (`REDACTED-PROJECT-REF`) showed a connection whose query text was the literal string `'SELECT 1'`, with a `backend_start` timestamp matching the `curl` call to the second. The same snapshot also captured connections executing a query beginning `SELECT event_id, event_type, payload, received_at FROM ...` — consistent with the Razorpay webhook event store the settlement poller reads every 15 seconds (`razorpay_settlement_poll_tick`, observed continuously in production logs across Phases 2A.2 and this phase). - -**Recorded identity:** project ref `REDACTED-PROJECT-REF`, project name (from `supabase projects list`) **"REDACTED-PROJECT-NAME"**, host `db.REDACTED-PROJECT-REF.supabase.co`, region `ap-southeast-2`. - -## 2. Historical Database Identity - -**Repository evidence:** the database inspected in Phase 2A.1 and re-referenced in Phase 2A.2 was reached via the `DATABASE_URL` present in the local `.env` file, documented there as backing "Live integration tests... Live tests create persistent rows." - -**Recorded identity:** project ref `REDACTED-PROJECT-REF`, project name **"REDACTED-PROJECT-NAME"**, host `db.REDACTED-PROJECT-REF.supabase.co` — identical fields to §1, because it is the same connection string used throughout Phases 2A.1–2A.3. - -## 3. Identity Comparison - -**Classification: IDENTICAL.** - -Supporting operational evidence (not repository or deployment history — direct, live, infrastructure-level evidence): - -- The literal `SELECT 1` query issued by production's own `/ready` route, at the exact moment it was triggered via the public production endpoint, was observed executing against the "REDACTED-PROJECT-NAME" database. -- The Razorpay webhook-event-store query pattern, matching the production settlement poller's own known 15-second cadence (independently confirmed live in Fly logs across two phases), was observed executing against the same database. - -This satisfies Task 3.5's evidence bar without exposing any secret: no password, token, secret value, or credential-bearing connection string was read or disclosed at any point. Identity was established purely through observed, self-triggered operational behavior — the strongest form of evidence this phase's instructions describe ("infrastructure control-plane evidence," "equivalent operational metadata") — not through inference, naming similarity, or repository history. - -## 4. Historical Trust Record Assessment - -Re-queried (read-only) the now-confirmed production database for every `payments:execute` `business_transaction` and its associated `execution_trust_records`: - -| Metric | Count | -| ------------------------------------------------------------------------------------ | ----- | -| Total `payments:execute` business transactions | 18 | -| Trust Records associated with them | 8 | -| Records with `authority.principalId` outside `{"ordering-test", "integration-test"}` | **0** | - -Identical to Phase 2A.1's findings, byte-for-byte — no new record appeared (consistent with Phase 2A.2's monitoring finding of zero `payments:execute` traffic since deployment). Every Trust Record's `authority.principalId`/`authority.displayName` field — part of the record's own immutable, hashed content, not an external label applied after the fact — reads `"ordering-test"` (15 records, dated 2026-07-11) or `"integration-test"` (3 records, dated 2026-08-03, `authority.displayName: "Integration Test"`). - -## 5. Historical Receipt Assessment - -| Metric | Count | -| --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Receipts associated with the 18 `payments:execute` transactions | 10 | -| Receipts for the 3 `integration-test`-attributed transactions | 0 (those three never progressed past `APPROVED`/Trust-Record stage — no `execution` sub-object, no receipt, consistent with a test that exercises authorization without driving a full pipeline run) | -| Receipts with any non-test caller/authority attribution | **0** | - -Every Receipt traces back to one of the 5 `ordering-test`-attributed, `EXECUTED`-status transactions (2 receipts each). - -**Literal vs. substantive finding, stated precisely:** 8 Trust Records and 10 Receipts do exist in the confirmed production database, and they were generated via `MockConnector` (that was, at the time, the only registered handler for `payments:execute`). But every one of them is self-identified, within the record's own cryptographically-hashed content, as originating from this repository's own test tooling — not from any real caller, customer, or external party. No consumer of these records (human or system) would encounter `authority.principalId: "ordering-test"` or `"integration-test"` and read it as a genuine, real-world approval. This is the substantive question TD-1 and every phase since have been tracking — whether a _real_ party was ever given fabricated evidence of a _real_ execution — and the answer, now backed by confirmed production-database evidence, is no. - -## 6. Remaining Uncertainty - -None material to the identity question itself — it is resolved (§3). Residual, narrower points: - -- This phase confirms the database identity _as of this phase's execution_ (2026-08-05). It does not retroactively prove the _same_ identity held at every point across the full historical window (2026-07-09 onward) — though nothing in repository or deployment history suggests `DATABASE_URL` was ever rotated to point at a different Supabase project, and the record timestamps (2026-07-11 through 2026-08-03) are fully accounted for within this one project. -- The Fly secret digest algorithm remains unidentified — not needed now that §3 was resolved by a stronger method, but noted for completeness since it was attempted and failed. - -## 7. Operational Impact - -Because §4–5 found **zero** non-test-attributed records, the operational-impact assessment Task 7 calls for is correspondingly minimal: - -- **Affected period:** 2026-07-11 (single day, 15 records) and 2026-08-03 13:27–13:34 UTC (3 records) — both entirely attributable to this repository's own test-suite runs against its "live integration test" database, not a sustained or ongoing exposure window. -- **Approximate record count:** 18 business transactions, 8 Trust Records, 10 Receipts — all test-attributed, none requiring remediation. -- **Downstream usage:** none identified. No route, report, or export in `packages/api/src` treats these records differently from any other; they were simply never retrieved or acted upon by anything other than this investigation's own read-only queries. -- **Customer visibility:** none. No external caller, integration, or consumer-facing surface references these specific transaction IDs; they are not linked from any customer-facing artifact. -- **Reporting impact:** none — no compliance, audit, or financial report in this repository aggregates or relies on `vendor-payment`/`payments:execute` records (no such reporting surface exists in the codebase at all). - -**Recommended remediation: none required** for the trust-integrity concern itself, since no real party was ever deceived. One minor, optional housekeeping observation, not a remediation for TD-1: using a project explicitly named "REDACTED-PROJECT-NAME" as the target for `ALLOW_LIVE_SUPABASE=1` integration-test runs means test-fixture rows (`test-txn-*`) accumulate in the same database production reads from. This carries no security or trust-integrity risk (every row is self-labeled, none are load-bearing for anything), but a future housekeeping pass could consider separating "live integration test" runs from whatever "audit" role this specific project otherwise serves. **Not performed in this phase** — out of scope (verification only, no remediation). - -## 8. Final Classification - -**HISTORICAL INTEGRITY CONFIRMED.** - -| Requirement | Status | -| ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -| Production database identity proven | ✓ — §1, via the production app's own `/ready` endpoint and observed `pg_stat_activity` correlation, not inference | -| Historical database identity matches production | ✓ — §3, IDENTICAL, same project (`REDACTED-PROJECT-REF` / "REDACTED-PROJECT-NAME") throughout | -| No historical production Trust Records or Receipts originated from MockConnector serving real, non-test activity | ✓ — §4–5, all 8 Trust Records / 10 Receipts are self-identified as test-attributed; zero records show any other origin | - -**Every conclusion above is supported by operational evidence obtained this phase or in Phase 2A.1/2A.2, not by inference from repository or deployment history alone** — per this phase's own instruction that repository/deployment history is insufficient on its own. The identity proof specifically rests on live, self-triggered, timestamp-correlated database activity (§1, §3), the strongest evidence category this phase's instructions describe, obtained without exposing any secret value. - -**TD-1 (Phase 1H's original finding) is now fully closed**: the implementation fix (Phase 2A), its deployment (Phase 2A.2), and the historical integrity of every record that existed before the fix (this phase) are all resolved, each with direct evidence at its own level — repository, deployment, and now production/historical. - -**Addendum (2026-08-05, Phase 2A.4):** this report's Final Classification (§8) was the triggering evidence for TD-1's formal closure in `docs/architecture/repository-certification.md`'s Technical Debt Register. Canonical evidence chain, commit SHAs, and closure rationale: `docs/operations/td1-closure-summary.md`. diff --git a/docs/operations/policy-approval-runbook.md b/docs/operations/policy-approval-runbook.md deleted file mode 100644 index ae72b219..00000000 --- a/docs/operations/policy-approval-runbook.md +++ /dev/null @@ -1,282 +0,0 @@ -# Policy Approval Runbook: Moving the 10 Pending Policies to APPROVED - -> **Update 2026-09-16: completed.** A distinct reviewer, `policy-reviewer-1`, approved -> all 10 policies below plus 4 additional pre-existing policies that had never been -> proposed at all (`agent-vendor-payment`, `api-key-issuance`, `expense-reimbursement`, -> `slack-post-message`), 14 total. Real approvals now exist in -> `policy_change_approval_records`; `docs/CLAIMS.md` §2.26/§2.35 and -> `docs/VERIFICATION-GAPS.md` gap 40 have been updated accordingly. Part 6's tracking -> table below is filled in with the real result. This runbook's steps remain accurate -> for any future policy needing the same maker-checker flow — kept as a how-to, not -> archived. -> -> Original status note, for the record: as of 2026-09-07, none of the 10 policies -> below had been reviewed or approved by anyone. This runbook described the real -> commands and endpoints that exist in this codebase, verified against source, so -> that when a genuinely distinct human checker was ready to act, the steps would be -> correct. - -## Why this exists - -Execution time policy verification (see `docs/CLAIMS.md` §2.35 and §2.36) is built, -tested, and, since 2026-09-20, enforced by default everywhere except `NODE_ENV` -`test` and `development`. It refuses every execution under a policy that has no -signed approval record, so a policy must be approved through this runbook before -the deployment that runs it is promoted. (This section was written when the flag -defaulted to `false` and none of the 10 real production policies had an approval -record yet.) This runbook is the path to closing that, once a real -reviewer is available. - -## Part 1: The 10 policies (verified live, 2026-09-07) - -Queried directly against `pending_policy_changes` via `@parmana/storage`'s real -`PendingPolicyChangeRepository` (the same repository `GET /policies/pending-changes` -uses) — not read from a prior draft of this document. - -| # | Policy | Version | Proposed by | Proposed at (UTC) | -| --- | ----------------------- | ------- | ----------- | ------------------- | -| 1 | `access-control` | 1.0.0 | charak1987 | 2026-08-19 01:47:43 | -| 2 | `connector-capability` | 1.0.0 | charak1987 | 2026-08-19 01:47:44 | -| 3 | `customer-refund` | 1.0.0 | charak1987 | 2026-08-19 01:47:44 | -| 4 | `database-change` | 3.0.0 | charak1987 | 2026-08-19 01:47:45 | -| 5 | `github-pr-approval` | 1.0.0 | charak1987 | 2026-08-19 01:47:45 | -| 6 | `hubspot-deal-update` | 1.0.0 | charak1987 | 2026-08-19 01:47:46 | -| 7 | `llm-tool-call` | 1.0.0 | charak1987 | 2026-08-19 01:47:46 | -| 8 | `production-deployment` | 1.0.0 | charak1987 | 2026-08-19 01:47:47 | -| 9 | `rag-document-access` | 1.0.0 | charak1987 | 2026-08-19 01:47:47 | -| 10 | `vendor-payment` | 2.0.0 | charak1987 | 2026-08-19 01:47:47 | - -All ten are proposed by the same account. By `SameActorCannotApproveOwnChangeError`, -none of them can be approved or rejected by `charak1987` — a genuinely distinct -human, with their own credential, must act on all ten. - -## Part 2: Prerequisite — identify the reviewer - -**Reviewer:** _______________ (not filled in by this runbook; confirm this -yourself before proceeding) - -The reviewer must be a real, distinct person who will actually read each policy's -rules and decide approve/reject on their own judgment — not a rubber stamp, and -not anyone who can be confused with `charak1987` by the server's own caller -identity (a different `callerId`/API key, at minimum). - -## Part 3: Provisioning the reviewer's credential (you, the operator, do this once) - -The reviewer needs two separate secrets: an ordinary bearer API key (for reading -pending changes and submitting the approve/reject request) and a step-up signing -keypair (Layer 4 — a second, independent proof of intent for the approve/reject -action itself, on top of the bearer token). `scripts/generate-api-key.ts` produces -both in one run and talks to nothing — you copy its output into config yourself. - -```bash -npx tsx scripts/generate-api-key.ts \ - --caller-id "" \ - --credential-holder-type USER \ - --generate-step-up-key -``` - -This prints, once, to your terminal only (nothing is written to disk): - -- the reviewer's bearer API key -- the reviewer's step-up **private** key (PEM) -- a JSON `entry` (containing only the key **hash** and the step-up **public** key) - to append to the `PARMANA_API_KEYS` environment variable (a JSON array) — - server-side config, not something the reviewer holds - -After adding the entry to `PARMANA_API_KEYS` and restarting the API, hand the -bearer key and the step-up private key to the reviewer over a secure channel — -ideally two different channels, since either alone is enough to forge a signed -approval if both are exposed together. - -## Part 4: What the reviewer does, per policy - -### Step A — list what's pending - -```bash -curl -X GET "/policies/pending-changes?status=PENDING_APPROVAL" \ - -H "Authorization: Bearer " -``` - -Real route: `packages/api/src/routes/pending-policy-changes.ts`, mounted at -`/policies` in `packages/api/src/app.ts` — not `/api/pending-changes`. The -response includes each change's `pendingPolicyChangeId` (the real id field — -not `id`) plus a `diff` (`current` vs. `proposed` content) for review. - -### Step B — review the policy content - -Read `proposedContent.rules` and the proposal's own `reason` field in the -response above. Decide approve or reject on the actual rules, not on trusting -that they match some external expectation — the diff view exists specifically -so the reviewer isn't asked to trust the proposer. - -### Step C — sign a step-up authorization (on the reviewer's own machine) - -```bash -npx tsx scripts/sign-policy-change-step-up.ts \ - --private-key-file ./reviewer.step-up.private.pem \ - --key-id \ - --pending-policy-change-id \ - --action approve -``` - -This never contacts the API. It prints a `stepUpAuthorization` JSON envelope -(valid for 120 seconds by default) for the reviewer to paste into the next -request's body. - -### Step D — submit the decision - -```bash -curl -X POST "/policies/pending-changes//approve" \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ "stepUpAuthorization": }' -``` - -(For a rejection instead: `.../reject` with `{ "rejectionReason": "...", "stepUpAuthorization": ... }`.) - -A successful approve resolves the change to `APPROVED` **and** creates a signed -`PolicyChangeApprovalRecord` (`PolicyChangeApprovalService`) — the record -`PolicyGovernanceExecutionVerifier` will check once enforcement is enabled. - -Repeat Steps A–D for each of the 10 policies. There is no single command that -does all 10 at once — each is a distinct decision. - -### Step E — verify - -```bash -curl -X GET "/policies/pending-changes?status=PENDING_APPROVAL" \ - -H "Authorization: Bearer " -``` - -Should return an empty `changes` array once all 10 are resolved (approved or -rejected). - -## Part 5: Enforcement (on by default since 2026-09-20) - -Enforcement is now the default in production, so there is nothing to enable here. Set -`POLICY_EXECUTION_VERIFICATION_ENFORCED=true` only to turn it on in a `NODE_ENV=test` or -`development` environment. In production the variable is ignored and cannot switch enforcement -off. The consequence is an ordering rule: complete Part 4 for every policy the deployment -executes against BEFORE promoting that deployment, or executions under an unapproved policy -are refused. - -Restart the API process after a change. From that point, `RuntimeEngine` refuses execution -against any policy with no approval record, an invalid approval-record -signature, or content that no longer matches its approval record — see -`docs/CLAIMS.md` §2.35. - -**Do this only once every policy this deployment actually executes against has -been resolved** (approved, or rejected and replaced by an approved proposal). -A policy left `PENDING_APPROVAL` or rejected with no approved replacement will -be refused at execution time the moment this flag is on. - -## Part 6: Tracking (filled in as each was actually resolved) - -Every row below reflects the _final_ resolution. Each of the original 10 was -approved twice: once against the stale 2026-08-19 proposal content (missing -`unboundSignalReasons`, and for `connector-capability`/`customer-refund`, -missing `boundSignals`), then re-proposed with current file content and -re-approved once the drift was caught — see `docs/CLAIMS.md` §2.26's -"Legacy-policy backfill" entry for the full account. `pendingPolicyChangeId` -below is the ID of the _final_ (correct-content) proposal, not the first one. -Four additional policies that predated Policy Governance entirely and had -never been proposed at all are included too, since they went through the -same session and the same reviewer. - -| Policy | Version | Reviewer | Decision | Resolved at (UTC) | pendingPolicyChangeId | -| --------------------- | ------- | ----------------- | -------- | ------------------------ | ------------------------------------ | -| access-control | 1.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:44:34.429Z | d9a5b8eb-1615-4273-b8a1-58b9371a8d46 | -| connector-capability | 1.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:44:39.232Z | d8fa1807-c822-4cc0-ac51-a85669217436 | -| customer-refund | 1.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:44:43.985Z | 2dd9b62a-938e-4d1f-bc6e-e1d60cbbebad | -| database-change | 3.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:44:48.721Z | d57291e9-ab7c-4f23-9aa7-776354ab2064 | -| github-pr-approval | 1.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:44:53.493Z | d8373b49-1ff7-49c5-804f-c7096d3199fd | -| hubspot-deal-update | 1.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:44:57.990Z | 917aef58-0d01-4071-b108-abe29df83904 | -| llm-tool-call | 1.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:45:02.475Z | a9fd546c-9ba1-4527-b152-7807c838a974 | -| production-deployment | 1.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:45:07.006Z | 40cca789-2bfe-42dc-a0b7-087baa7e1aad | -| rag-document-access | 1.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:45:11.476Z | f64d924d-cacb-4d7a-b96c-8ab2074028cc | -| vendor-payment | 2.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:45:15.988Z | c6dae1d5-f326-4e01-967a-3b4d003297ff | -| agent-vendor-payment | 1.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:48:10.092Z | 3b27cd11-d816-45ec-b8ad-ecfd5ec2fa8a | -| api-key-issuance | 1.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:48:16.925Z | 4e0b4a10-e4c2-4647-a319-5a848f1f788d | -| expense-reimbursement | 1.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:48:23.820Z | 63f9903b-c125-4248-ab7c-526cd312f62e | -| slack-post-message | 1.0.0 | policy-reviewer-1 | APPROVED | 2026-09-16T08:48:30.864Z | a99a9216-a1b0-4e7e-971c-65520ec2015d | - -## Part 6.5: Before/after state verification queries - -These check the real database state directly, against the real schema -(`supabase/migrations/20260818120000_add_policy_governance_tables.sql`) — not a -guessed one. Every result below is a placeholder for you to fill in by actually -running the query; none of these have been run against real data as part of -this runbook, and no value should be copied into `CLAIMS.md`/ -`VERIFICATION-GAPS.md` until it has been. - -There is no `psql` client in this sandbox; run these from an environment that -has one, or adapt them to a one-off script using `@parmana/storage`'s -`StorageFactory` the way this runbook's own Part 1 table was produced (a -plain `pool.query(...)` against the same tables works too, but the repository -layer already does the mapping correctly). - -**Query 1 — pending-change status, before:** - -```sql -SELECT policy_name, policy_version, status, proposed_by, proposed_at, - resolved_by, resolved_at -FROM pending_policy_changes -ORDER BY proposed_at ASC; -``` - -Result: _______________ (expect 10 rows, all `PENDING_APPROVAL`, `resolved_by`/`resolved_at` null — confirmed live in this session on 2026-09-07) - -**Query 2 — approval records, before:** - -```sql -SELECT policy_change_approval_record_id, pending_policy_change_id, policy_name, - policy_version, approved_by, approved_at, content_hash_before, - content_hash_after -FROM policy_change_approval_records -ORDER BY approved_at ASC; -``` - -Result: _______________ (expect 0 rows — no policy in this system has ever completed the approval flow, per `docs/CLAIMS.md` §2.26) - -**Query 3 — pending-change status, after each approve/reject:** - -Same as Query 1. After all 10 are resolved, expect 0 rows with -`status = 'PENDING_APPROVAL'` and 10 rows with `resolved_at` set (split -between `APPROVED` and `REJECTED` depending on what the reviewer actually -decided — not necessarily all 10 approved). - -**Query 4 — approval records, after:** - -Same as Query 2. Expect one new row per **approved** policy only (a rejection -creates no `PolicyChangeApprovalRecord` — see that type's own doc comment: a -rejected change's `rejectionReason` is itself the durable evidence a -rejection leaves behind). There is no `step_up_signature_valid` column to -check here — the step-up signature is verified once, at the moment of the -`POST .../approve` call, and is never stored as a boolean afterward. What -persists and can be independently re-checked later is the approval record's -own `signature_json`, via `PolicyChangeCrypto.verify()`. - -**Query 5 — content still matches what was approved:** - -Don't hand-roll a `sha256sum` comparison — `content_hash_after` is a hash of -the _canonicalized_ JSON (`CanonicalSerializer`, key-sorted), not the raw -file bytes, so a naive file hash will not reliably match. Use the tool this -codebase already has for exactly this: - -```bash -npx tsx scripts/verify-policy-changes-approved.ts --full-scan -``` - -Result: _______________ (this already runs in CI on every push/PR against -changed policy files — see `.github/workflows/ci.yml`'s -`verify-policy-approvals` job; `--full-scan` here checks every policy on -disk, not only ones changed in a diff) - -## Part 7: After all 10 are genuinely resolved - -Come back and ask for `docs/CLAIMS.md` §2.35 and `docs/VERIFICATION-GAPS.md` -gap 40 to be updated — with the real reviewer name, real timestamps, and real -`policyChangeApprovalRecordId` values queried from the database, the same way -Part 1's table above was built from a real query rather than assumed. Enforcement -no longer needs to be switched on separately, it is the production default since -2026-09-20, so confirm every executed policy has its record first. diff --git a/docs/operations/policy-approval-windows-setup.md b/docs/operations/policy-approval-windows-setup.md deleted file mode 100644 index aadbe2d6..00000000 --- a/docs/operations/policy-approval-windows-setup.md +++ /dev/null @@ -1,114 +0,0 @@ -# Windows Setup for a Policy Reviewer - -> Companion to `docs/operations/policy-approval-runbook.md` (the full mechanics, -> real policy list, and current status) — this file only covers the -> Windows-specific machine setup for a reviewer, using the real tooling that -> actually exists in this codebase. It replaces an earlier draft (`app.md`, -> repo root) that named a nonexistent `packages/cli`, a password/MFA login -> flow this system doesn't have, and the wrong policy list — none of which -> would have worked if followed. -> -> **Status: not yet executed.** No policy has been approved. Do not treat -> anything below as having happened. - -## Step 1: Install prerequisites (one-time) - -```cmd -REM Git for Windows: https://git-scm.com/download/win -REM Node.js LTS: https://nodejs.org/ - -git --version -node --version -npm --version -``` - -## Step 2: Clone and build - -```cmd -mkdir D:\work -cd D:\work -git clone https://github.com/pavancharak/parmana-exp.git -cd parmana-exp -npm install -npm run build -``` - -`npm run build` runs the real project build (`tsc -b` across all packages) — -there is no separate "CLI build" step, because there is no `packages/cli` in -this codebase. The tools the reviewer actually needs are plain TypeScript -scripts run via `npx tsx`, already present after cloning. - -## Step 3: Receive your credential (from the operator, not self-generated) - -Unlike the earlier draft, the reviewer does **not** run a `login` command or -generate their own credential against a live server — there is no -username/password/MFA flow in this system. Instead, the person who controls -this deployment's configuration (`charak1987`) runs, once, on **their own** -machine: - -```bash -npx tsx scripts/generate-api-key.ts \ - --caller-id "mohinder-singh-charak" \ - --credential-holder-type USER \ - --generate-step-up-key -``` - -and sends the reviewer two secrets, ideally over two different channels: - -1. a bearer API key (used to authenticate every request below) -2. a step-up private key, PEM-encoded (used only to sign an approve/reject - decision — never sent to the server directly) - -Save the step-up private key locally, e.g. `mohinder.step-up.private.pem`. -Note the `--key-id` value the operator used when generating it — you'll need -it in Step 5. - -## Step 4: List what's pending - -```cmd -curl -X GET "/policies/pending-changes?status=PENDING_APPROVAL" -H "Authorization: Bearer " -``` - -Real route, mounted at `/policies` (`packages/api/src/app.ts`) — not -`/api/pending-changes`. See `docs/operations/policy-approval-runbook.md` Part 1 -for the actual 10 policies currently pending (as of 2026-09-07, all proposed -2026-08-19): `access-control`, `connector-capability`, `customer-refund`, -`database-change`, `github-pr-approval`, `hubspot-deal-update`, -`llm-tool-call`, `production-deployment`, `rag-document-access`, -`vendor-payment`. Each response entry's real id field is -`pendingPolicyChangeId`, and includes a `diff` (`current` vs. `proposed` -content) — read it before deciding. - -## Step 5: Sign and submit a decision, per policy - -```cmd -npx tsx scripts/sign-policy-change-step-up.ts --private-key-file mohinder.step-up.private.pem --key-id --pending-policy-change-id --action approve -``` - -This prints a `stepUpAuthorization` JSON envelope (valid 120 seconds) — never -sent anywhere by this script itself. Paste it into the actual request: - -```cmd -curl -X POST "/policies/pending-changes//approve" -H "Authorization: Bearer " -H "Content-Type: application/json" -d "{ \"stepUpAuthorization\": }" -``` - -Repeat for each of the 10 policies (`--action reject` with a `rejectionReason` -field instead, for any you decide not to approve). There is no single -command that does all 10 at once — each is meant to be a distinct decision, -not a batch rubber-stamp. - -## Step 6: Verify - -```cmd -curl -X GET "/policies/pending-changes?status=PENDING_APPROVAL" -H "Authorization: Bearer " -``` - -Should return an empty `changes` array once all 10 are resolved. - -## After this is genuinely done - -Tell the operator. They'll confirm the real state against the database (see -`docs/operations/policy-approval-runbook.md` Part 6.5) before updating -`docs/CLAIMS.md` §2.35 / `docs/VERIFICATION-GAPS.md` gap 40, and before -promoting a deployment, since enforcement is now the production default and an unapproved -policy is refused. diff --git a/docs/operations/policy-governance-developer-guide.md b/docs/operations/policy-governance-developer-guide.md deleted file mode 100644 index fb3b432d..00000000 --- a/docs/operations/policy-governance-developer-guide.md +++ /dev/null @@ -1,310 +0,0 @@ -# Policy Governance: Complete Developer Guide - -**Audience:** anyone new to this codebase who needs to create, propose, review, or -approve a policy — or debug why one of those steps isn't working. Self-contained; -you shouldn't need any other document to get from "I have a policy idea" to "it's -live and approved." - -**Companion documents:** `docs/operations/policy-approval-runbook.md` is the -narrower, copy-pasteable checklist for approving a specific batch of pending -policies (this guide explains the _system_; that one is a _task list_). -`docs/CLAIMS.md` §2.26/§2.35 is the audited, evidence-cited claim about what this -system guarantees and how it's tested — read this guide first, that one if you need -to cite proof for a specific claim. - ---- - -## 1. What this system is, in one paragraph - -Every policy that decides whether a request gets approved or rejected lives as a -`policy.json` file under `policies/{name}/{version}/policy.json`, and also (in any -environment with a real database configured) as a row in the `policies` table. A -policy's content can only change through a **maker-checker** flow: one person -_proposes_ a change, a genuinely different person _reviews and approves_ it, -cryptographically, before it takes effect. No one can approve their own proposal — -the system enforces this at the API layer, not just by convention. This exists -because policy content controls real decisions (who gets paid, what gets deployed, -what an AI agent is allowed to do), so a single person being able to silently -change that content, with no second party and no durable record, was treated as -an unacceptable gap. - -## 2. The five roles/concepts you need to know - -| Term | What it means | -| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| **Policy** | A `policy.json` file: rules that evaluate signals (booleans/numbers/strings) into `approve` or `reject`. | -| **Maker / proposer** | The person (or automation) who proposes a policy change. Identified by `callerId` in `PARMANA_API_KEYS`. | -| **Checker / reviewer** | A genuinely distinct person who reviews the proposal and approves or rejects it. Must have `credentialHolderType: "USER"` and, for approve/reject, a **step-up keypair**. | -| **Bearer key** | An ordinary API key (`Authorization: Bearer ...`) that proves _who_ is calling. Every request needs one. | -| **Step-up authorization** | A second, independent, short-lived (120s) cryptographic signature proving the checker specifically intends _this_ approve/reject action on _this_ pending change, on top of their bearer key. Signed locally, on the checker's own machine, with a private key that never leaves it. | - -## 3. Writing a new policy - -A `policy.json` has this shape (see `packages/policy/src/types/Policy.ts` for the -authoritative type): - -```json -{ - "policyId": "my-new-policy", - "policyVersion": "1.0.0", - "schemaVersion": "1.0.0", - "description": "One sentence: what this policy authorizes and on what basis.", - "signalsSchema": { - "someBooleanSignal": "boolean", - "someAmount": "number" - }, - "boundSignals": { - "someAmount": "parameters.amount" - }, - "unboundSignalReasons": { - "someBooleanSignal": "Why this can't be derived from the request itself — e.g. it's an independent verification result." - }, - "rules": [ - { - "id": "approve-default", - "condition": { - "all": [ - { "fact": "someBooleanSignal", "operator": "eq", "value": true }, - { "fact": "someAmount", "operator": "lte", "value": 5000 } - ] - }, - "outcome": { - "action": "approve", - "reason": "Human-readable reason shown in decisions/audit." - } - }, - { - "id": "reject-default", - "condition": { "always": true }, - "outcome": { - "action": "reject", - "reason": "Catch-all: anything not explicitly approved is rejected." - } - } - ] -} -``` - -Key rules to follow: - -- **Every fact referenced by a rule must appear in either `boundSignals` (if it can - be derived from the request's own `target`/`parameters`) or - `unboundSignalReasons` (if it must come from independent verification).** - `PolicyValidator.validate()` fails closed on this — a proposal with an uncovered - fact is rejected at propose time with a 400, not silently accepted. -- **Always end with a catch-all reject rule** (`"condition": { "always": true }`). - Default-deny, not default-allow. -- **`policyVersion` inside the file must match the version in the URL path** when - you propose it (see below) — this is what `PolicyChangeApprovalService` uses as - the actual write target, not the URL's version param. -- A worked example, written specifically to be copy-pasted from: `expense-reimbursement` - (`policies/expense-reimbursement/1.0.0/policy.json`) — see `LIVE-API-GUIDE.md`'s - "How to Write a New Policy" section for the full walkthrough. - -## 4. The full lifecycle, step by step - -``` - write policy.json → propose → review → sign (step-up) → submit approve/reject → verify - (maker) (maker) (checker) (checker) (checker) (anyone) -``` - -### Step 0 — Get a reviewer credential (one-time, per checker) - -The checker needs a bearer key **and** a step-up keypair, genuinely independent of -whoever is proposing. Generated together, once: - -```powershell -npx tsx scripts/generate-api-key.ts --caller-id "" --credential-holder-type USER --generate-step-up-key -``` - -This prints three things: a bearer key, a step-up **private** key (PEM), and an -`entry` JSON block (hash + public key only — safe to store/share, unlike the other -two). The operator appends the `entry` to the `PARMANA_API_KEYS` environment -variable (a JSON array) and redeploys; the checker keeps the bearer key and the -private key file (`checker.step-up.private.pem`) on their own machine, never in -this repo, never committed. See §7 "Rotating a credential" if you need to replace -one later. - -### Step A — Propose - -```bash -curl -X POST "/policies///pending-changes" \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{"proposedContent": , "reason": "why this change"}' -``` - -Returns `201` with the created `pendingPolicyChangeId`, or `400` if -`proposedContent`/`reason` are missing/malformed, or if `PolicyValidator` finds an -uncovered fact or a `matches` regex it rejects as too complex. - -### Step B — Review - -```bash -curl "/policies/pending-changes?status=PENDING_APPROVAL" \ - -H "Authorization: Bearer " -``` - -Read `proposedContent.rules` and the proposal's own `reason`. The response also -includes `diff.current` vs. `diff.proposed` (what's live today vs. what's being -proposed) and `coverageWarnings`/`ruleConflicts` if the validator or the advisory -conflict checker found anything worth a second look. This is a judgment call — no -script makes it for you. - -### Step C — Sign (on the checker's own machine, never anyone else's) - -```bash -npx tsx scripts/sign-policy-change-step-up.ts \ - --private-key-file ./checker.step-up.private.pem \ - --key-id \ - --pending-policy-change-id \ - --action approve -``` - -Prints a `stepUpAuthorization` JSON envelope, valid for **120 seconds**. This -script never talks to the API — it only signs, locally. - -### Step D — Submit - -```bash -curl -X POST "/policies/pending-changes//approve" \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{"stepUpAuthorization": }' -``` - -(For a rejection: `.../reject` with `{"rejectionReason": "...", "stepUpAuthorization": ...}`.) -Must happen within 120 seconds of signing, or you'll need to re-sign — see §6. - -### Step E — Verify - -```bash -curl "/policies/pending-changes?status=PENDING_APPROVAL" \ - -H "Authorization: Bearer " -``` - -The resolved change should no longer appear. Separately, anyone can confirm the -live file and the approval record agree: - -```bash -npx tsx scripts/verify-policy-changes-approved.ts --full-scan -``` - -## 5. The one-shot scripts (recommended over manual curl) - -Manually chaining curl commands across the 120-second signing window is fragile — -see §6 for exactly how it breaks. Two scripts in this repo collapse Steps C+D (or -A through D) into one process invocation, so there's no human-speed gap between -signing and submitting: - -**`scripts/local-review-action.ts`** — signs and submits approve/reject for an -_existing_ pending change, in one run: - -```powershell -$env:REVIEWER_KEY = "" -npx tsx scripts/local-review-action.ts --pending-policy-change-id --action approve -``` - -**`scripts/refresh-approved-policy-content.ts`** — proposes the _current on-disk -file content_ as a fresh pending change, then immediately signs and approves it, -in one run. Useful when you want to (re-)approve exactly what's in the file right -now without hand-copying JSON: - -```powershell -$env:PROPOSER_KEY = "" -$env:REVIEWER_KEY = "" -npx tsx scripts/refresh-approved-policy-content.ts --policy-name --policy-version -``` - -Both read the step-up private key from `./reviewer.step-up.private.pem` by default -(override with `--private-key-file`), and print only non-secret output (HTTP -status, response body) — neither script ever prints your bearer key or private key. - -## 6. Troubleshooting — real failures, and what they actually mean - -| Symptom | Real cause | Fix | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `{"error":"authentication required"}`, HTTP 401 | The bearer key is missing, wrong, or stale (rotated since). Also happens if your header literally reads `Authorization: Bearer` with nothing after it, or the header is missing the word `Bearer` entirely (`Authorization: ` alone doesn't work). | Confirm the env var is actually set in _this_ terminal (`$env:REVIEWER_KEY.Length` should print a number like 43, not `0`). If it's set but still 401, the key may be stale — see "Rotating a credential" below. | -| `{"error":"The step-up authorization envelope is missing, invalid, expired, replayed, or does not match this request.","code":"STEP_UP_AUTHORIZATION_INVALID"}`, HTTP 403 | One of: (a) more than 120 seconds passed between signing and submitting; (b) the envelope was already used once (nonces are single-use); (c) **the step-up private key you signed with doesn't match the `stepUpPublicKey` currently on file for that caller** — this is the easy one to miss, since the server verifies against whatever public key is _currently_ configured for your `callerId`, not whatever key generated the envelope. | Re-sign and resubmit within the window (or use the one-shot scripts in §5, which make this a non-issue). If it persists even with near-zero delay, verify your local `.pem` file actually matches the currently-configured public key: `node -e "const c=require('crypto');const f=require('fs');console.log(c.createPublicKey(c.createPrivateKey(f.readFileSync('reviewer.step-up.private.pem','utf8'))).export({type:'spki',format:'pem'}))"` and compare to the `stepUpPublicKey` in `PARMANA_API_KEYS`. | -| `{"error":"Internal Server Error"}`, HTTP 500, and the server log shows `Invalid JSON` from `body-parser` | The request body wasn't valid JSON by the time it reached the server — usually a shell-quoting problem, not a real JSON bug. PowerShell in particular rewrites how it passes quoted arguments to native executables like `curl.exe`, and a long inline `-d '{"a":"b",...}'` with many embedded quotes can get mangled in transit. | Don't build the JSON body inline in the command line. Write it to a file first (`[System.IO.File]::WriteAllText("body.json", $json, [System.Text.Encoding]::ASCII)`), then `curl.exe --data-binary "@body.json"`. Or use the one-shot scripts in §5, which build the request body inside Node, never through a shell. | -| `Error: EROFS: read-only file system, open '...policy.json...tmp'` | `PolicyChangeApprovalService.approve()` tried to write the approved policy content to the local filesystem via `FilePolicyRepository`, but the environment's filesystem is read-only (Vercel serverless Functions). This was a real bug, fixed 2026-09-16 — see `docs/CLAIMS.md` §2.26. | Should not happen anymore on a current deployment; `SupabasePolicyRepository` is used automatically whenever `PARMANA_STORAGE` isn't `memory`. If you see this, the deployed code predates the fix — redeploy from current `main`. | -| `vercel deploy --prod` fails with `"message": "fetch failed"` | Transient network issue between your machine and Vercel's upload endpoint — not a code or config problem. | Just retry `vercel deploy --prod`. It succeeded on retry every time this came up. | -| Proposal succeeds, but the approved content is missing fields you expect (e.g. `boundSignals`) | You approved a pending change that was proposed a long time ago, and the live file has since gained fields the old proposal doesn't have. Proposals are frozen snapshots — nothing re-checks them against the current file while they sit `PENDING_APPROVAL`. | Reject or don't approve stale proposals blindly. If you want to approve _current_ content, use `scripts/refresh-approved-policy-content.ts` (§5), which always proposes from the live file, not an old snapshot. Confirm with `scripts/verify-policy-changes-approved.ts --full-scan` afterward. | -| `{"error":"proposedContent is required and must be a policy.json object."}` or similar 400 on propose | Malformed request body, or `proposedContent.policyId` doesn't match the `name` in the URL, or `policyVersion` doesn't match `^[A-Za-z0-9._-]+$`. | Read the exact `error` message — it names the specific field. | -| `SAME_ACTOR_CANNOT_APPROVE_OWN_CHANGE`, HTTP 403 | The bearer key you're approving with belongs to the same `callerId` that proposed the change. | Use a genuinely different checker credential. There is no override. | - -## 7. Rotating a credential - -If a bearer key or step-up private key is ever exposed (pasted somewhere it -shouldn't have been, committed by accident, etc.), rotate it immediately — treat -it as compromised the moment it's been seen anywhere outside the holder's own -terminal. - -```powershell -npx tsx scripts/generate-api-key.ts --caller-id "" --credential-holder-type USER --generate-step-up-key -``` - -This produces an entirely new bearer key **and** step-up keypair (they're -independent — a leak of one doesn't compromise the other, but regenerating -`--generate-step-up-key` always refreshes both together). Update the `entry` in -`PARMANA_API_KEYS`, **and overwrite the local `.pem` file with the new private -key** — the single most common mistake is rotating the bearer key but forgetting -to also replace the `.pem` file, which produces the confusing -`STEP_UP_AUTHORIZATION_INVALID` failure in the table above even though everything -looks right at a glance. Verify the two actually match before moving on (the -`node -e ...` command in §6's second row). - -## 8. FAQ - -**Q: Can I approve my own proposal if there's no one else around?** -No. `SameActorCannotApproveOwnChangeError` is enforced server-side, not just a -convention. You need a genuinely distinct second credential. - -**Q: Does the step-up private key ever get sent to the API?** -No. Only the _signature_ it produces (the `stepUpAuthorization` envelope) is sent. -The private key never leaves the checker's machine — this is the entire point of -the mechanism. - -**Q: What happens if I let a signed envelope's 120 seconds expire before submitting?** -The submit request fails with `STEP_UP_AUTHORIZATION_INVALID` (see §6). Just -re-sign — envelopes are cheap and instant to produce. - -**Q: Can a rejection be un-rejected, or an approval undone?** -No. `PendingPolicyChange` moves `PENDING_APPROVAL` → `APPROVED`/`REJECTED` exactly -once, never back. To change your mind, propose a new change. - -**Q: Where does the approved content actually take effect?** -In any environment with a real database configured (`PARMANA_STORAGE` other than -`memory`), `PolicyChangeApprovalService.approve()` writes the approved content into -the `policies` table via `SupabasePolicyRepository`, and that's what -`RuntimeEngine` reads on every subsequent real request. In local dev/tests, it -writes to the local file instead. - -**Q: Is there a UI for any of this?** -`packages/governance-ui` covers propose/list/diff-review only — deliberately no -approve/reject controls, since a web UI collecting a step-up private key would -defeat the guarantee that the key never leaves the checker's machine. Approve/reject -stays CLI-only, by design. - -## 9. If you're stuck - -1. **Read the actual error message and HTTP status first** — every error in this - system is written to name the specific problem (missing field, wrong value, - which check failed). §6's table covers the ones that look confusing at first - glance but have a clear, specific cause. -2. **Check the server logs** for anything that returned a generic `500 Internal -Server Error` — `vercel logs ` (or your platform's equivalent) usually - shows the real underlying exception, which is almost always more specific than - what the HTTP response body says. -3. **Run `scripts/verify-policy-changes-approved.ts --full-scan`** to get an - honest, database-backed picture of what's actually approved vs. not, rather - than trusting assumptions about what should have happened. -4. **If a credential might be compromised, rotate it immediately** (§7) — don't - try to keep using something that might have leaked, and don't wait to see if - it "still works." -5. **If none of the above resolves it**, the underlying source is not large: - `packages/api/src/routes/pending-policy-changes.ts` (the four endpoints), - `packages/api/src/governance/PolicyChangeApprovalService.ts` (what approve - actually does), `packages/api/src/auth/PolicyChangeStepUpVerifier.ts` (what - step-up verification actually checks) are the three files that cover nearly - everything in this guide, in well under a thousand lines combined. diff --git a/docs/operations/td1-closure-summary.md b/docs/operations/td1-closure-summary.md deleted file mode 100644 index 225caa6b..00000000 --- a/docs/operations/td1-closure-summary.md +++ /dev/null @@ -1,99 +0,0 @@ -# TD-1 Closure Summary — Canonical Evidence Record - -**Status: CLOSED.** This document is the single canonical source for TD-1's full evidence chain. Every other repository document that references TD-1's closure should point here rather than re-stating this evidence. - ---- - -## 1. Original Finding - -First recorded in `docs/architecture/repository-certification.md` (Phase 1H independent repository certification), §4.9 and Technical Debt Register item **TD-1**: - -> `vendor-payment` connector is permanently backed by `MockConnector`, unconditionally registered (unlike credential-gated Razorpay/HubSpot), fabricating signed `{success:true}` Trust Records/Receipts for `payments:execute` with no real vendor ever contacted. - -Severity: **High**. Classified as a production-trust gap, not an architectural-boundary violation — the single-execution-pipeline and adapter-ownership invariants held throughout; `MockConnector` was always the one class the architecture allowlisted outside `execution-gateway`. - -## 2. Root Cause - -`packages/api/src/bootstrap/createVendorPaymentConnector.ts` unconditionally constructed a `MockConnector` for the `vendor-payment` connector's `payments:execute` capability, and `createConnectorRegistry.ts` registered it in every environment, including production — with no credential gate or environment check, unlike Razorpay's and HubSpot's connectors, which fail closed (skip registration, with a warning) when unconfigured. - -## 3. Why The Issue Existed - -Established with direct, contemporaneous, first-commit evidence in `docs/operations/phase2a-deployment-readiness.md` §1: `createVendorPaymentConnector.ts`'s original commit (`ddf4bc5`, 2026-07-09) already carried the docstring _"This is the production bootstrap for the vendor-payment connector. The current implementation uses MockConnector until the real enterprise connector is introduced."_ — intentional, temporary, self-documented technical debt from the moment it was written, not accidental. It was wired into the default server (`docs/site/roadmap.mdx`, commit `651497a`, 2026-07-11) specifically to demonstrate the credential-isolation/brokering architecture claim. No real `vendor-payment` implementation was ever built, and no roadmap entry committed to building one on any timeline. - -## 4. Phase 2A — Implementation - -Commit **`6eff8ecbc67b2610ffd1cbcf8a94676bdec31981`** (2026-08-05 07:32:29 +0530), `fix(production): remove MockConnector from production bootstrap (Phase 2A)`. - -`createVendorPaymentConnector.ts` now returns `undefined` outside `NODE_ENV=test`; `createConnectorRegistry.ts` guards registration on that (`if (connector === undefined) { console.warn(...) } else { registrations.push(...) }`), mirroring the pre-existing Razorpay/HubSpot fail-closed pattern exactly. In production, `payments:execute` now fails with the same `"No connector registered for capability"` error every other unimplemented capability already produces. Regression tests added and validated by mutation testing (revert the fix, confirm the new tests fail; restore, confirm they pass). Full report: `docs/architecture/phase2a-production-connectors.md`. - -## 5. Phase 2A.1 — Operational Readiness Review - -Commit **`10c80644548015a27dbb5d5ce146150690768993`** (2026-08-05 07:53:39 +0530), `docs(operations): independent readiness review before Phase 2A redeploy`. - -Independent review (historical rationale, production impact, fail-closed acceptability, capability readiness, and a first Historical Data Integrity Assessment). Classified **Unable to Certify** at this stage — strong evidence that every `payments:execute` record found was test-attributed, but the queried Supabase database's identity relative to production was not yet confirmed. Recommendation: **READY WITH CONDITIONS**, explicitly reviewed and accepted by Pavan before deployment proceeded. Full report: `docs/operations/phase2a-deployment-readiness.md`. - -## 6. Phase 2A.2 — Production Deployment Verification - -Commit **`0c19f78a3fb7bfd9714051bf4623ff384fc41441`** (2026-08-05 08:14:59 +0530), `docs(operations): verify Phase 2A production deployment`. - -Deployed commit `6eff8ec` to `parmana-api` (Fly.io) from a git worktree checked out at that exact SHA, guaranteeing byte-for-byte artifact fidelity with the reviewed commit (Fly release `v40`). Verified via direct production log evidence — a machine restart captured the live `vendor_payment_connector_unavailable` startup warning verbatim, confirming the fix is active. No live authenticated end-to-end call was attempted (no legitimate production caller credential available to that session); fail-closed behavior was instead established deterministically from the confirmed registry state plus the already-tested, unchanged code path. New evidence narrowed (but did not close) the database-identity question: the Supabase CLI showed the repository's linked project is named "REDACTED-PROJECT-NAME," matching the historically-queried database's host exactly. Recommendation: **DEPLOYMENT VERIFIED WITH FOLLOW-UP** — every requirement satisfied except the still-open database-identity question. Full report: `docs/operations/phase2a-deployment-verification.md`. - -## 7. Phase 2A.3 — Historical Integrity Verification - -Commit **`e6a25abb3a2d4fb5a1bc3967a6c5aa5dd68a317c`** (2026-08-05 08:36:02 +0530), `docs(operations): certify historical integrity after Phase 2A`. - -Resolved the database-identity question directly: production's own public `/ready` endpoint (exempt from caller auth, same as `/health`) executes a literal `SELECT 1` against the exact Postgres pool `DATABASE_URL` configures when `PARMANA_STORAGE=supabase`. Calling it live and, in the same instant, observing that literal `SELECT 1` — plus a Razorpay-webhook-store query matching the settlement poller's known cadence — land in `pg_stat_activity` on the "REDACTED-PROJECT-NAME" database (`REDACTED-PROJECT-REF`) constitutes direct, self-triggered, timestamp-correlated operational proof of identity, obtained without exposing any secret value. **Classification: IDENTICAL.** Re-querying the now-confirmed production database found 18 `payments:execute` business transactions, 8 Trust Records, 10 Receipts — unchanged from Phase 2A.1's count — every one attributed, within its own hashed record content, to `authority.principalId` of `"ordering-test"` or `"integration-test"`. Zero records show any other origin. Final Classification: **HISTORICAL INTEGRITY CONFIRMED**. Full report: `docs/operations/phase2a-historical-integrity-verification.md`. - -## 8. Final Evidence Chain - -| Phase | Commit | Date | Report | Outcome | -| ------------------------------ | --------- | ---------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------- | -| 2A (implementation) | `6eff8ec` | 2026-08-05 | `docs/architecture/phase2a-production-connectors.md` | MockConnector removed from unconditional production registration; fail-closed | -| 2A.1 (readiness review) | `10c8064` | 2026-08-05 | `docs/operations/phase2a-deployment-readiness.md` | READY WITH CONDITIONS | -| 2A.2 (deployment verification) | `0c19f78` | 2026-08-05 | `docs/operations/phase2a-deployment-verification.md` | DEPLOYMENT VERIFIED WITH FOLLOW-UP | -| 2A.3 (historical integrity) | `e6a25ab` | 2026-08-05 | `docs/operations/phase2a-historical-integrity-verification.md` | HISTORICAL INTEGRITY CONFIRMED | -| 2A.4 (this closure) | — | 2026-08-05 | `docs/operations/td1-closure-summary.md` (this document) | TD-1 CLOSED | - -Deployed artifact: Fly.io app `parmana-api`, release `v40`, image `registry.fly.io/parmana-api:deployment-01KZ7W5ZZF13WV46E6TBNTVFE6`, built from commit `6eff8ec`. - -## 9. Closure Rationale - -TD-1 is closed, not merely "fixed," because every layer of evidence this program's own instructions demanded has been independently satisfied, each at the level of evidence appropriate to it: - -- **Repository evidence** (Phase 2A): the fix exists, is tested, and mutation-testing confirmed the regression tests actually detect the reintroduced gap. -- **Deployment evidence** (Phase 2A.2): the exact reviewed commit is confirmed running in production, via direct build-identity chain-of-custody and a live-captured startup log showing the fix's own warning message firing in the real process. -- **Production/operational evidence** (Phase 2A.2, Phase 2A.3): the fail-closed behavior was established from confirmed registry state and unchanged, previously-proven code paths (not merely asserted); the historical-data question — whether any _real_ party was ever handed fabricated evidence of a _real_ execution — was answered directly, with self-triggered, timestamp-correlated proof of database identity, and a finding of zero non-test-attributed records. - -No unresolved condition remains from any of the four phases. Phase 2A.1's conditions were each individually satisfied (checklist completed, monitoring window observed, database identity resolved). Phase 2A.2's one follow-up (historical data) was resolved by Phase 2A.3. Nothing further is pending. - -## 10. Date Closed - -**2026-08-05** (Phase 2A.4). - -## 11. Related Commits - -- `6eff8ecbc67b2610ffd1cbcf8a94676bdec31981` — Phase 2A implementation -- `10c80644548015a27dbb5d5ce146150690768993` — Phase 2A.1 report -- `0c19f78a3fb7bfd9714051bf4623ff384fc41441` — Phase 2A.2 report -- `e6a25abb3a2d4fb5a1bc3967a6c5aa5dd68a317c` — Phase 2A.3 report -- Original finding: Phase 1H, `c3a29b5` (`docs(architecture): certify repository architecture (Phase 1H)`) - -## 12. Related Reports - -- `docs/architecture/repository-certification.md` — Phase 1H, original TD-1 finding (§4.9, Technical Debt Register) -- `docs/architecture/phase2a-production-connectors.md` — Phase 2A implementation report -- `docs/operations/phase2a-deployment-readiness.md` — Phase 2A.1 operational readiness review -- `docs/operations/phase2a-deployment-verification.md` — Phase 2A.2 deployment verification -- `docs/operations/phase2a-historical-integrity-verification.md` — Phase 2A.3 historical integrity verification -- `docs/architecture/execution-pipeline-report.md` — Phase 1E baseline, carries the original (later-corrected) claim about `MockConnector` and production bootstrap - -## 13. Repository Locations Updated (Phase 2A.4) - -- `docs/architecture/repository-certification.md` — TD-1 register row marked Closed; dated certification addendum appended -- `docs/architecture/execution-pipeline-report.md` — dated addendum appended, extending the existing Phase 2A note with the full evidence chain -- `docs/architecture/phase2a-production-connectors.md` — dated addendum appended, superseding the "remains present in the currently deployed environment" caveat -- `docs/operations/phase2a-deployment-verification.md` — dated addendum appended, noting the "WITH FOLLOW-UP" condition was resolved by Phase 2A.3 -- `docs/operations/phase2a-historical-integrity-verification.md` — traceability pointer added to this closure summary -- `docs/operations/td1-closure-summary.md` — this document, newly created - -**Not modified** (explicitly out of scope for this closure pass): `docs/VERIFICATION-GAPS.md` and `docs/ROADMAP-v1.md` track a separate, G-numbered gap register that includes an unrelated, still-genuinely-open item about independent _signal-state verification_ for `vendor-payment` (whether caller-declared signals like `vendorVerified`/`riskScore` are independently checked against a real system before execution) — a different concern from TD-1 (whether execution itself could be fabricated), using a different identifier, never tracked as "TD-1," and not addressed by Phase 2A. `docs/CLAIMS.md` contains one now-imprecise line (§ "create-connector-registry.test.ts... vendor-payment remains resolvable when razorpay is not," no longer accurate in production since Phase 2A) — noted here for visibility but not edited in this pass, as it falls outside this closure's explicitly named document set and warrants its own dedicated review given CLAIMS.md's size and separate maintenance conventions. diff --git a/docs/rfcs/RFC-0000-TEMPLATE.md b/docs/rfcs/RFC-0000-TEMPLATE.md deleted file mode 100644 index 4ed9e293..00000000 --- a/docs/rfcs/RFC-0000-TEMPLATE.md +++ /dev/null @@ -1,137 +0,0 @@ -\# RFC-XXXX — Title - -Status: Draft - -Author: - -Created: - -Updated: - -Target Version: - -\--- - -\# Summary - -A concise description of the proposal. - -\--- - -\# Motivation - -Why is this needed? - -What problem does it solve? - -\--- - -\# Goals - -\- - -\- - -\- - -\--- - -\# Non Goals - -\- - -\- - -\--- - -\# Background - -Relevant architecture. - -Relevant ADRs. - -Relevant specifications. - -\--- - -\# Proposal - -Describe the proposed solution. - -Include diagrams where appropriate. - -\--- - -\# Alternatives Considered - -Option A - -Option B - -Option C - -Explain why they were rejected. - -\--- - -\# Compatibility - -Does this change: - -\- APIs - -\- Runtime - -\- Verification - -\- Evidence - -\- Storage - -\- SDK - -\--- - -\# Migration - -Describe migration strategy. - -\--- - -\# Risks - -Technical risks. - -Operational risks. - -Compatibility risks. - -\--- - -\# Open Questions - -\- - -\- - -\- - -\--- - -\# Acceptance Criteria - -\- - -\- - -\- - -\--- - -\# References - -Specifications - -ADRs - -Research diff --git a/docs/rfcs/RFC-0001-Policy-Engine.md b/docs/rfcs/RFC-0001-Policy-Engine.md deleted file mode 100644 index 41a88232..00000000 --- a/docs/rfcs/RFC-0001-Policy-Engine.md +++ /dev/null @@ -1,383 +0,0 @@ -\# RFC-0001 — Policy Engine - -\*\*Status:\*\* Draft - -\*\*Author:\*\* Parmana Architecture Team - -\*\*Created:\*\* 2026-06-25 - -\*\*Target Version:\*\* 0.2.0 - -\--- - -\# Summary - -Introduce a pluggable Policy Engine responsible for evaluating whether an execution request is authorized. - -The Policy Engine operates before execution and produces an immutable `Authorization` object that becomes part of the `ExecutionTransaction`. - -The Policy Engine is independent of the Runtime, Verification Engine, Storage, and Cryptography. - -\--- - -\# Motivation - -Execution Trust requires more than recording execution. - -It requires proving that execution was permitted according to an explicit policy. - -Today, authorization is represented as a domain object but no architectural component is responsible for evaluating policy. - -The Policy Engine fills that gap. - -\--- - -\# Goals - -\* Provide deterministic policy evaluation. - -\* Produce immutable Authorization objects. - -\* Support multiple policy providers. - -\* Record policy decisions as execution evidence. - -\* Remain independent of Runtime orchestration. - -\--- - -\# Non-Goals - -This RFC does not define: - -\* A policy language. - -\* A rule authoring UI. - -\* Role management. - -\* Identity management. - -\* Authentication. - -These concerns remain external to Parmana. - -\--- - -\# Architecture - -```text - -Authority - - │ - - ▼ - -Intent - - │ - - ▼ - -Policy Engine - - │ - - ▼ - -Authorization - - │ - - ▼ - -Runtime - - │ - - ▼ - -Execution - -``` - -The Policy Engine determines whether execution may proceed. - -The Runtime executes only after authorization has been established. - -\--- - -\# Responsibilities - -The Policy Engine SHALL: - -\* Evaluate execution requests. - -\* Produce Authorization objects. - -\* Record policy identifiers. - -\* Record policy versions. - -\* Record evaluation timestamps. - -\* Record evaluation outcomes. - -The Policy Engine SHALL NOT: - -\* Execute business logic. - -\* Modify execution results. - -\* Verify execution. - -\* Persist transactions. - -\--- - -\# Provider Model - -The Policy Engine delegates evaluation to interchangeable providers. - -Example providers include: - -\* Static Policy Provider - -\* JSON Policy Provider - -\* YAML Policy Provider - -\* Enterprise Policy Provider - -\* External Decision Service - -\* Open Policy Agent (OPA) Provider - -\* Custom Provider - -Providers implement a common interface. - -\--- - -\# Policy Evaluation - -Policy evaluation accepts: - -\* Authority - -\* Intent - -\* Execution Context - -\* Policy Snapshot - -It produces: - -\* Authorization - -\* Decision - -\* Optional Reason - -\* Policy Metadata - -Evaluation must be deterministic. - -\--- - -\# Runtime Integration - -The Runtime invokes the Policy Engine during the Authorization Stage. - -```text - -AuthorityStage - - │ - - ▼ - -IntentStage - - │ - - ▼ - -AuthorizationStage - - │ - - ▼ - -Policy Engine - - │ - - ▼ - -Authorization - - │ - - ▼ - -ExecutionStage - -``` - -Execution SHALL NOT continue if authorization is denied. - -\--- - -\# Evidence - -Policy evaluation contributes immutable evidence to the execution record. - -Typical evidence includes: - -\* Policy Identifier - -\* Policy Version - -\* Decision - -\* Evaluation Timestamp - -\* Decision Reason (optional) - -This evidence enables later verification and audit. - -\--- - -\# Verification - -The Verification Engine validates: - -\* An Authorization exists. - -\* The recorded policy metadata is complete. - -\* The authorization is internally consistent. - -\* Policy evidence has not been modified. - -The Verification Engine does not re-evaluate policy. - -\--- - -\# Determinism - -Policy evaluation SHALL be deterministic. - -Equivalent inputs MUST produce equivalent authorization results. - -Providers that depend on external systems SHALL preserve or record the information required to explain the resulting authorization decision. - -\--- - -\# Alternatives Considered - -\## Policy Inside Runtime - -Rejected because it couples execution orchestration with policy evaluation. - -\--- - -\## Policy Inside Verification - -Rejected because verification evaluates recorded facts rather than making authorization decisions. - -\--- - -\## Embedded Rule Engine - -Rejected because organizations use diverse policy technologies. - -Parmana should integrate with existing policy systems rather than replace them. - -\--- - -\# Package Mapping - -```text - -packages/ - - - -policy/ - - src/ - - PolicyEngine.ts - - PolicyProvider.ts - - PolicyDecision.ts - - PolicyContext.ts - - providers/ - -``` - -The Policy package remains independent of Runtime and Verification. - -\--- - -\# Compatibility - -This RFC is backward compatible. - -Implementations without a Policy Engine MAY continue using static Authorization objects. - -Future Runtime implementations MAY delegate Authorization creation to the Policy Engine without changing the `ExecutionTransaction` domain model. - -\--- - -\# Open Questions - -\* Should policy snapshots become first-class domain objects? - -\* Should policy bundles be cryptographically signed? - -\* Should policy evaluation support batch authorization? - -\* Should policy metadata include provenance information? - -\--- - -\# Acceptance Criteria - -\* A `PolicyEngine` abstraction exists. - -\* Provider interfaces are defined. - -\* Runtime integrates with the Policy Engine during the Authorization Stage. - -\* Policy decisions produce immutable Authorization objects. - -\* Policy evidence is recorded within the `ExecutionTransaction`. - -\* Verification validates authorization artifacts without re-evaluating policy. - -\--- - -\# References - -\* 001-ARCHITECTURE.md - -\* 003-EXECUTION-TRANSACTION.md - -\* 011-RUNTIME.md - -\* 012-RUNTIME-STAGES.md - -\* 013-VERIFICATION-ENGINE.md - -\* 014-EXECUTION-TRUST-MODEL.md - -\* ADR-0003 — Verification Is Independent - -\* ADR-0004 — Runtime Pipeline diff --git a/docs/rfcs/RFC-0002-Replay-Engine.md b/docs/rfcs/RFC-0002-Replay-Engine.md deleted file mode 100644 index aa785748..00000000 --- a/docs/rfcs/RFC-0002-Replay-Engine.md +++ /dev/null @@ -1,459 +0,0 @@ -\# RFC-0002 — Replay Engine - -\*\*Status:\*\* Draft - -\*\*Author:\*\* Parmana Architecture Team - -\*\*Created:\*\* 2026-06-25 - -\*\*Target Version:\*\* 0.2.0 - -\--- - -\# Summary - -Introduce a Replay Engine capable of deterministically reconstructing an execution from an immutable `ExecutionTransaction` and its associated evidence. - -The Replay Engine validates that the recorded execution can be reproduced from preserved execution artifacts. - -Replay is independent of Runtime execution and independent of the Verification Engine. - -\--- - -\# Motivation - -Execution Trust requires more than recording execution. - -It must be possible to demonstrate that the recorded execution is reproducible using the preserved execution context and evidence. - -Replay enables: - -\* Independent validation - -\* Audit - -\* Incident investigation - -\* Regression testing - -\* Compliance - -\* Long-term execution preservation - -\--- - -\# Goals - -\* Deterministically reconstruct execution. - -\* Validate execution consistency. - -\* Operate independently of Runtime. - -\* Operate independently of Verification. - -\* Produce immutable Replay Reports. - -\--- - -\# Non-Goals - -This RFC does not define: - -\* Business workflow execution. - -\* Live orchestration. - -\* Scheduling. - -\* State mutation. - -\* Distributed execution. - -Replay never replaces Runtime. - -\--- - -\# Architecture - -```text - -ExecutionTransaction - - │ - - ▼ - - Replay Engine - - │ - - ▼ - -Replay Pipeline - - │ - - ├── Context Loader - - ├── Artifact Loader - - ├── Execution Reconstructor - - ├── Consistency Validator - - └── Replay Reporter - - │ - - ▼ - -Replay Report - -``` - -Replay reconstructs execution semantics without invoking production workflows. - -\--- - -\# Responsibilities - -The Replay Engine SHALL: - -\* Load immutable execution records. - -\* Load immutable evidence. - -\* Reconstruct execution context. - -\* Replay execution deterministically. - -\* Detect inconsistencies. - -\* Produce Replay Reports. - -The Replay Engine SHALL NOT: - -\* Execute business logic. - -\* Modify transactions. - -\* Modify evidence. - -\* Authorize execution. - -\* Produce new execution artifacts. - -\--- - -\# Replay Inputs - -Replay consumes: - -\* ExecutionTransaction - -\* Evidence - -\* Runtime metadata - -\* Policy snapshot (if available) - -\* Cryptographic metadata - -Inputs SHALL remain immutable. - -\--- - -\# Replay Outputs - -Replay produces a Replay Report containing: - -\* Replay Status - -\* Replay Timestamp - -\* Execution Consistency - -\* Missing Artifacts - -\* Replay Messages - -\* Metadata - -Replay reports are immutable. - -\--- - -\# Replay Pipeline - -```text - -ExecutionTransaction - - │ - - ▼ - -Load Context - - │ - - ▼ - -Load Evidence - - │ - - ▼ - -Reconstruct Execution - - │ - - ▼ - -Validate Consistency - - │ - - ▼ - -Generate Replay Report - -``` - -Each stage is deterministic. - -\--- - -\# Determinism - -Replay SHALL be deterministic. - -Equivalent replay inputs SHALL produce equivalent replay results. - -Replay SHALL NOT depend upon: - -\* Current system time - -\* Network availability - -\* Random values - -\* Runtime state - -\* External services - -If external dependencies influenced the original execution, their recorded results or preserved context SHALL be used during replay. - -\--- - -\# Relationship to Runtime - -```text - -Runtime - - │ - - ▼ - -ExecutionTransaction - - │ - - ├──────────────┐ - - ▼ ▼ - -Replay Engine Verification Engine - -``` - -Runtime creates execution records. - -Replay reconstructs execution. - -Verification evaluates trust. - -These are independent architectural components. - -\--- - -\# Relationship to Verification - -Replay and Verification serve different purposes. - -Replay answers: - -> Can this execution be reconstructed? - -Verification answers: - -> Can this execution be trusted? - -Replay MAY provide inputs to Verification but SHALL remain an independent subsystem. - -\--- - -\# Replay Report - -A Replay Report contains: - -\* Overall Status - -\* Replay Duration - -\* Context Summary - -\* Consistency Results - -\* Missing Evidence - -\* Replay Metadata - -Replay Reports never modify the original transaction. - -\--- - -\# Failure Model - -Replay failures are explicit. - -Typical outcomes include: - -\* SUCCESS - -\* FAILED - -\* INCOMPLETE - -\* NOT\_REPLAYABLE - -Failures are represented within the Replay Report. - -\--- - -\# Package Mapping - -```text - -packages/ - - - -replay/ - - src/ - - ReplayEngine.ts - - ReplayPipeline.ts - - ReplayReport.ts - - ReplayContext.ts - - interfaces/ - - ReplayComponent.ts - - components/ - - ContextLoader.ts - - ArtifactLoader.ts - - ExecutionReconstructor.ts - - ConsistencyValidator.ts - - ReplayReporter.ts - -``` - -The Replay package remains independent of Runtime and Verification. - -\--- - -\# Compatibility - -This RFC is backward compatible. - -Replay is an optional capability. - -ExecutionTransactions remain unchanged. - -\--- - -\# Alternatives Considered - -\## Replay Inside Runtime - -Rejected because Runtime is responsible for execution, not reconstruction. - -\--- - -\## Replay Inside Verification - -Rejected because verification evaluates trust, while replay reconstructs execution. - -Combining the two would weaken separation of concerns. - -\--- - -\## Runtime Re-Execution - -Rejected because re-running business logic may produce different outcomes due to environmental changes. - -Replay should reconstruct execution from preserved artifacts rather than execute live systems. - -\--- - -\# Open Questions - -\* Should Replay Reports be retained as evidence? - -\* Should replay support partial execution reconstruction? - -\* Should replay compare multiple executions for behavioral drift? - -\* Should replay support historical runtime versions? - -\--- - -\# Acceptance Criteria - -\* A `ReplayEngine` abstraction exists. - -\* Replay operates independently of Runtime. - -\* Replay consumes immutable execution artifacts. - -\* Replay produces immutable Replay Reports. - -\* Replay is deterministic. - -\* Replay does not modify ExecutionTransactions or Evidence. - -\--- - -\# References - -\* 003-EXECUTION-TRANSACTION.md - -\* 010-EVIDENCE.md - -\* 011-RUNTIME.md - -\* 012-RUNTIME-STAGES.md - -\* 013-VERIFICATION-ENGINE.md - -\* 014-EXECUTION-TRUST-MODEL.md - -\* ADR-0002 — Immutable Domain Model - -\* ADR-0003 — Verification Is Independent - -\* ADR-0007 — Deterministic Execution diff --git a/docs/rfcs/RFC-0003-Human-Approval.md b/docs/rfcs/RFC-0003-Human-Approval.md deleted file mode 100644 index 47b30694..00000000 --- a/docs/rfcs/RFC-0003-Human-Approval.md +++ /dev/null @@ -1,399 +0,0 @@ -\# RFC-0003 — Human Approval - -\*\*Status:\*\* Draft - -\*\*Author:\*\* Parmana Architecture Team - -\*\*Created:\*\* 2026-06-25 - -\*\*Target Version:\*\* 0.3.0 - -\--- - -\# Summary - -Introduce a Human Approval capability that allows execution to pause until one or more authorized humans explicitly approve or reject execution. - -Human Approval integrates into the Runtime Pipeline while preserving deterministic execution and immutable execution records. - -Approval decisions become part of the immutable execution evidence. - -\--- - -\# Motivation - -Many regulated and high-impact systems require human oversight before execution. - -Examples include: - -\* Financial transactions - -\* Healthcare workflows - -\* Legal approvals - -\* Production deployments - -\* AI agent actions - -\* Infrastructure changes - -Execution Trust should include evidence that required human approvals were obtained before execution. - -\--- - -\# Goals - -\* Support single and multiple approvers. - -\* Record immutable approval decisions. - -\* Support approval and rejection. - -\* Support deterministic replay. - -\* Preserve independent verification. - -\--- - -\# Non-Goals - -This RFC does not define: - -\* Identity management. - -\* Authentication. - -\* User interfaces. - -\* Notification systems. - -\* Workflow management. - -These concerns remain external to Parmana. - -\--- - -\# Architecture - -```text - -Authority - - │ - - ▼ - -Intent - - │ - - ▼ - -Authorization - - │ - - ▼ - -Human Approval - - │ - - ▼ - -Execution - - │ - - ▼ - -Evidence - -``` - -Execution SHALL NOT proceed until required approvals have been satisfied. - -\--- - -\# Human Approval Model - -A Human Approval contains: - -\* Approval Identifier - -\* Approver Identifier - -\* Decision - -\* Timestamp - -\* Optional Reason - -\* Metadata - -Approval objects are immutable. - -\--- - -\# Runtime Integration - -The Runtime Pipeline introduces an optional HumanApprovalStage. - -```text - -AuthorityStage - - │ - - ▼ - -IntentStage - - │ - - ▼ - -AuthorizationStage - - │ - - ▼ - -HumanApprovalStage - - │ - - ▼ - -ExecutionStage - - │ - - ▼ - -EvidenceStage - -``` - -The stage is configurable. - -If approval is not required, execution continues without entering the approval state. - -\--- - -\# Approval States - -The following states are defined: - -\* Pending - -\* Approved - -\* Rejected - -\* Expired - -\* Cancelled - -State transitions produce new immutable records. - -Historical approval decisions remain preserved. - -\--- - -\# Approval Policies - -Implementations MAY support: - -\* Single approver - -\* Multiple approvers - -\* Majority approval - -\* Unanimous approval - -\* Sequential approval - -\* Parallel approval - -These policies are implementation concerns and do not alter the Core domain model. - -\--- - -\# Evidence - -Approval decisions SHALL become immutable evidence. - -Typical evidence includes: - -\* Approver Identifier - -\* Decision - -\* Timestamp - -\* Approval Policy Identifier - -\* Optional Comments - -Approval evidence SHALL be append-only. - -\--- - -\# Verification - -The Verification Engine validates: - -\* Required approvals exist. - -\* Approval evidence is complete. - -\* Approval decisions are internally consistent. - -\* Approval evidence has not been modified. - -Verification SHALL NOT request new approvals or alter approval decisions. - -\--- - -\# Replay - -Replay reconstructs approval decisions using recorded evidence. - -Replay SHALL NOT request user interaction. - -Historical approval decisions are treated as immutable execution facts. - -\--- - -\# Determinism - -Human approval introduces a non-deterministic event at execution time. - -To preserve deterministic replay: - -\* The approval outcome SHALL be recorded. - -\* The approval timestamp SHALL be recorded. - -\* The approver identity SHALL be recorded. - -\* Replay SHALL consume the recorded approval rather than request a new decision. - -\--- - -\# Package Mapping - -```text - -packages/ - - - -approval/ - - src/ - - ApprovalEngine.ts - - ApprovalRequest.ts - - ApprovalDecision.ts - - ApprovalPolicy.ts - - ApprovalEvidence.ts - -``` - -The Approval package remains independent of Runtime, Verification, and Storage. - -\--- - -\# Alternatives Considered - -\## Approval Inside Policy Engine - -Rejected because policy evaluation determines whether approval is required, while human approval records a human decision. - -Keeping these responsibilities separate improves clarity and auditability. - -\--- - -\## Approval Inside Runtime - -Rejected because approval is a domain capability rather than an orchestration concern. - -The Runtime invokes the approval stage but does not own approval logic. - -\--- - -\## Approval Inside Verification - -Rejected because verification evaluates completed execution rather than interacting with humans. - -\--- - -\# Compatibility - -This RFC is backward compatible. - -Implementations that do not require human approval continue to operate unchanged. - -The HumanApprovalStage is optional and configurable. - -\--- - -\# Open Questions - -\* Should delegated approvals be supported? - -\* Should approval delegation be time-limited? - -\* Should approval chains support organizational hierarchies? - -\* Should cryptographic signatures be required for approval decisions? - -\* Should approval evidence support external attestations? - -\--- - -\# Acceptance Criteria - -\* A `HumanApprovalStage` exists. - -\* Immutable Approval objects are defined. - -\* Approval decisions become append-only evidence. - -\* Runtime supports optional approval gates. - -\* Replay reproduces historical approval outcomes. - -\* Verification validates approval evidence without requesting new approvals. - -\--- - -\# References - -\* 003-EXECUTION-TRANSACTION.md - -\* 010-EVIDENCE.md - -\* 011-RUNTIME.md - -\* 012-RUNTIME-STAGES.md - -\* 013-VERIFICATION-ENGINE.md - -\* 014-EXECUTION-TRUST-MODEL.md - -\* ADR-0002 — Immutable Domain Model - -\* ADR-0003 — Verification Is Independent - -\* ADR-0004 — Runtime Pipeline - -\* ADR-0007 — Deterministic Execution diff --git a/docs/rfcs/RFC-0004-Post-Quantum-Cryptography.md b/docs/rfcs/RFC-0004-Post-Quantum-Cryptography.md deleted file mode 100644 index 9feb02d2..00000000 --- a/docs/rfcs/RFC-0004-Post-Quantum-Cryptography.md +++ /dev/null @@ -1,397 +0,0 @@ -\# RFC-0004 — Post-Quantum Cryptography - -\*\*Status:\*\* Draft - -\*\*Author:\*\* Parmana Architecture Team - -\*\*Created:\*\* 2026-06-25 - -\*\*Target Version:\*\* 0.3.0 - -\--- - -\# Summary - -Introduce support for post-quantum cryptographic providers while preserving backward compatibility with existing cryptographic algorithms. - -The objective is to ensure that execution records remain independently verifiable throughout the expected lifetime of the platform, even as cryptographic standards evolve. - -This RFC extends the Cryptographic Agility principle established in ADR-0006. - -\--- - -\# Motivation - -Execution records may need to remain trustworthy for decades. - -Future advances in quantum computing may weaken or break widely deployed public-key cryptographic algorithms. - -Parmana should be capable of adopting new cryptographic standards without requiring changes to the Core domain model or invalidating historical execution records. - -\--- - -\# Goals - -\* Support multiple generations of cryptographic algorithms. - -\* Preserve historical verification. - -\* Enable gradual migration. - -\* Avoid changes to the Core domain model. - -\* Maintain deterministic verification. - -\--- - -\# Non-Goals - -This RFC does not: - -\* Mandate a specific post-quantum algorithm. - -\* Replace existing algorithms immediately. - -\* Define key management. - -\* Define certificate infrastructure. - -\* Define hardware security modules. - -Those concerns remain implementation specific. - -\--- - -\# Architectural Principles - -The Core domain remains cryptography agnostic. - -Cryptographic providers remain replaceable. - -Historical execution records remain valid. - -New executions may adopt newer providers without affecting previous records. - -\--- - -\# Provider Architecture - -```text - -ExecutionTransaction - - │ - - ▼ - -Cryptography Interfaces - - │ - - ▼ - -Provider Registry - - │ - - ┌──────┼──────────────┐ - - ▼ ▼ ▼ - -Classical Hybrid Post-Quantum - -Providers Providers Providers - -``` - -The Runtime and Verification Engine interact only with provider interfaces. - -\--- - -\# Supported Provider Classes - -\## Classical Providers - -Examples include: - -\* SHA-256 - -\* SHA-3 - -\* BLAKE3 - -\* Ed25519 - -\* ECDSA - -\--- - -\## Hybrid Providers - -Hybrid providers combine classical and post-quantum algorithms during the migration period. - -Example: - -```text - -SHA-256 - - + - -ML-DSA - -``` - -Hybrid verification succeeds only when the configured verification policy is satisfied. - -\--- - -\## Post-Quantum Providers - -Examples may include: - -\* ML-DSA - -\* SLH-DSA - -\* Future NIST-standardized algorithms - -The specification intentionally avoids hard-coding algorithm names. - -\--- - -\# Cryptographic Metadata - -Execution records SHOULD preserve: - -\* Hash Algorithm - -\* Signature Algorithm - -\* Provider Identifier - -\* Algorithm Version - -\* Key Identifier - -\* Provider Version - -This metadata enables long-term verification. - -\--- - -\# Migration Strategy - -Migration occurs gradually. - -```text - -Generation 1 - - - -SHA-256 - -Ed25519 - - - - │ - - - -Generation 2 - - - -SHA-256 - -Ed25519 - -ML-DSA - - - - │ - - - -Generation 3 - - - -SHA-3 - -ML-DSA - - - - │ - - - -Generation 4 - - - -Future Providers - -``` - -Older execution records remain verifiable. - -\--- - -\# Verification - -The Verification Engine SHALL: - -\* Select the correct provider. - -\* Validate signatures. - -\* Validate hashes. - -\* Report unsupported algorithms. - -\* Preserve deterministic behavior. - -Verification SHALL NOT rewrite cryptographic metadata. - -\--- - -\# Replay - -Replay SHALL use the algorithms recorded with the original execution. - -Replay SHALL NOT substitute newer algorithms automatically. - -This preserves historical correctness. - -\--- - -\# Runtime - -The Runtime delegates all cryptographic operations to provider interfaces. - -The Runtime SHALL remain independent of concrete cryptographic algorithms. - -\--- - -\# Storage - -Storage preserves cryptographic metadata exactly as recorded. - -Storage SHALL NOT regenerate hashes or signatures. - -\--- - -\# Package Mapping - -```text - -packages/ - - - -crypto/ - - src/ - - providers/ - - classical/ - - hybrid/ - - postquantum/ - - - - HashProvider.ts - - SignatureProvider.ts - - ProviderRegistry.ts - -``` - -No changes are required to the Core package. - -\--- - -\# Compatibility - -This RFC is fully backward compatible. - -Existing execution records remain valid. - -Existing providers continue to function. - -New providers may be introduced without changing the execution model. - -\--- - -\# Alternatives Considered - -\## Immediate Algorithm Replacement - -Rejected because it would invalidate historical verification workflows and complicate long-term archival. - -\--- - -\## Hard-Coded Post-Quantum Algorithms - -Rejected because cryptographic standards continue to evolve. - -Parmana should depend upon provider interfaces rather than specific algorithms. - -\--- - -\## Runtime-Specific Cryptography - -Rejected because interoperability requires a common cryptographic abstraction. - -\--- - -\# Open Questions - -\* Should hybrid signatures become the default during migration? - -\* Should cryptographic policy become configurable? - -\* Should provider trust policies be standardized? - -\* Should algorithm deprecation be represented within protocol metadata? - -\--- - -\# Acceptance Criteria - -\* Provider registry supports multiple algorithm families. - -\* Runtime remains algorithm independent. - -\* Verification supports multiple provider generations. - -\* Historical execution records remain verifiable. - -\* Replay preserves original cryptographic metadata. - -\* No Core domain changes are required. - -\--- - -\# References - -\* 005-CRYPTOGRAPHY.md - -\* 010-EVIDENCE.md - -\* 013-VERIFICATION-ENGINE.md - -\* 014-EXECUTION-TRUST-MODEL.md - -\* 016-PLATFORM-GUARANTEES.md - -\* ADR-0006 — Cryptographic Agility diff --git a/docs/rfcs/RFC-0005-Distributed-Verification.md b/docs/rfcs/RFC-0005-Distributed-Verification.md deleted file mode 100644 index cca8ab1b..00000000 --- a/docs/rfcs/RFC-0005-Distributed-Verification.md +++ /dev/null @@ -1,367 +0,0 @@ -\# RFC-0005 — Distributed Verification - -\*\*Status:\*\* Draft - -\*\*Author:\*\* Parmana Architecture Team - -\*\*Created:\*\* 2026-06-25 - -\*\*Target Version:\*\* 0.3.0 - -\--- - -\# Summary - -Introduce a Distributed Verification model that enables multiple independent Verification Engines to evaluate the same immutable execution record and produce independently verifiable results. - -Distributed Verification strengthens trust by allowing verification across organizational, geographical, and technological boundaries without requiring shared runtime state or distributed consensus. - -\--- - -\# Motivation - -Many execution environments involve multiple organizations or trust domains. - -Examples include: - -\* Financial institutions - -\* Supply chains - -\* Government agencies - -\* Healthcare ecosystems - -\* Multi-cloud deployments - -\* AI agent ecosystems - -Execution Trust should not depend on a single verifier. - -Independent parties should be able to verify the same execution record and compare results. - -\--- - -\# Goals - -\* Support independent verification by multiple parties. - -\* Preserve deterministic verification. - -\* Avoid shared runtime state. - -\* Avoid consensus protocols. - -\* Preserve execution record immutability. - -\--- - -\# Non-Goals - -This RFC does not define: - -\* Blockchain integration. - -\* Distributed consensus. - -\* Byzantine fault tolerance. - -\* Replicated databases. - -\* Distributed transaction coordination. - -Distributed Verification is verification, not coordination. - -\--- - -\# Architecture - -```text - - ExecutionTransaction - - │ - - ┌───────────────┼───────────────┐ - - ▼ ▼ ▼ - -Verifier A Verifier B Verifier C - - │ │ │ - - ▼ ▼ ▼ - -Verification Verification Verification - - Report A Report B Report C - -``` - -Each verifier operates independently. - -\--- - -\# Verification Independence - -Each Verification Engine: - -\* Loads immutable execution records. - -\* Loads immutable evidence. - -\* Performs deterministic verification. - -\* Produces an immutable Verification Report. - -Verifiers never communicate during verification. - -\--- - -\# Trust Model - -Distributed trust is established when independent verifiers reach equivalent conclusions using the same immutable execution artifacts. - -Trust is derived from: - -\* Shared evidence - -\* Deterministic verification - -\* Independent implementations - -Not from verifier communication. - -\--- - -\# Verification Inputs - -Every verifier consumes: - -\* ExecutionTransaction - -\* Evidence - -\* Cryptographic metadata - -\* Verification configuration - -Inputs remain immutable. - -\--- - -\# Verification Outputs - -Each verifier produces: - -\* Verification Report - -\* Verification Timestamp - -\* Verifier Identifier - -\* Supported Specification Version - -\* Metadata - -Reports remain independent. - -\--- - -\# Determinism - -Equivalent inputs SHALL produce equivalent verification outcomes. - -Differences in: - -\* Programming language - -\* Operating system - -\* Cloud provider - -\* Hardware - -\* Database - -SHALL NOT affect verification semantics. - -\--- - -\# Verification Comparison - -Independent reports MAY be compared. - -Comparison may include: - -\* Overall Status - -\* Individual Verification Results - -\* Cryptographic Validation - -\* Evidence Completeness - -\* Metadata Compatibility - -Comparison does not alter either report. - -\--- - -\# Trust Domains - -Example: - -```text - -Organization A - - │ - - ▼ - -Verifier A - - - -Organization B - - │ - - ▼ - -Verifier B - - - -Organization C - - │ - - ▼ - -Verifier C - -``` - -Each organization maintains its own verifier. - -No shared runtime is required. - -\--- - -\# Runtime Relationship - -The Runtime produces execution records. - -The Runtime is unaware of how many verifiers exist. - -Execution occurs exactly once. - -Verification may occur many times. - -\--- - -\# Replay Relationship - -Replay reconstructs execution. - -Distributed Verification evaluates trust. - -Replay and Verification remain independent capabilities. - -\--- - -\# Package Mapping - -```text - -packages/ - - - -verification/ - - distributed/ - - VerificationCoordinator.ts - - VerificationComparator.ts - - VerificationReportSet.ts - -``` - -Distributed Verification extends the Verification package without changing the Core domain model. - -\--- - -\# Compatibility - -This RFC is backward compatible. - -Single-verifier deployments remain fully supported. - -Distributed Verification is an optional capability. - -\--- - -\# Alternatives Considered - -\## Blockchain Verification - -Rejected because Parmana focuses on independently verifiable execution rather than distributed consensus. - -\--- - -\## Shared Verification Database - -Rejected because it creates unnecessary coupling between organizations. - -\--- - -\## Runtime-Based Verification - -Rejected because verification must remain independent of execution. - -\--- - -\# Open Questions - -\* Should verification reports be digitally signed? - -\* Should report comparison become standardized? - -\* Should verifier capabilities be discoverable? - -\* Should verifier reputation be represented? - -\--- - -\# Acceptance Criteria - -\* Multiple Verification Engines can evaluate the same execution record independently. - -\* Equivalent inputs produce equivalent verification outcomes. - -\* Verification Reports remain immutable. - -\* Verification requires no shared runtime state. - -\* Distributed Verification introduces no changes to the Core domain model. - -\--- - -\# References - -\* 013-VERIFICATION-ENGINE.md - -\* 014-EXECUTION-TRUST-MODEL.md - -\* 015-PLATFORM-ARCHITECTURE.md - -\* 016-PLATFORM-GUARANTEES.md - -\* ADR-0003 — Verification Is Independent - -\* ADR-0007 — Deterministic Execution diff --git a/docs/rfcs/RFC-0006-Multi-Tenant-Runtime.md b/docs/rfcs/RFC-0006-Multi-Tenant-Runtime.md deleted file mode 100644 index 0b861635..00000000 --- a/docs/rfcs/RFC-0006-Multi-Tenant-Runtime.md +++ /dev/null @@ -1,429 +0,0 @@ -\# RFC-0006 — Multi-Tenant Runtime - -\*\*Status:\*\* Draft - -\*\*Author:\*\* Parmana Architecture Team - -\*\*Created:\*\* 2026-06-25 - -\*\*Target Version:\*\* 0.4.0 - -\--- - -\# Summary - -Introduce a Multi-Tenant Runtime architecture that enables multiple organizations (tenants) to execute independent workloads on the same Parmana deployment while maintaining strict isolation of execution, evidence, verification, policy, and operational resources. - -Multi-tenancy is an infrastructure capability and SHALL NOT alter the Core domain model. - -\--- - -\# Motivation - -Enterprise deployments frequently require a single platform to serve multiple organizations. - -Examples include: - -\* SaaS platforms - -\* Managed AI platforms - -\* Enterprise business units - -\* Government agencies - -\* Cloud service providers - -Each tenant must remain logically isolated while benefiting from a shared runtime infrastructure. - -\--- - -\# Goals - -\* Strong tenant isolation. - -\* Shared Runtime infrastructure. - -\* Independent execution records. - -\* Independent verification. - -\* Independent policy configuration. - -\* Independent storage. - -\* Deterministic execution. - -\--- - -\# Non-Goals - -This RFC does not define: - -\* Identity providers. - -\* Billing. - -\* User management. - -\* Authentication. - -\* Authorization between tenants. - -Those concerns belong to platform administration. - -\--- - -\# Architectural Principle - -Multi-tenancy is an operational concern. - -The Core domain SHALL remain tenant-neutral. - -Tenant awareness exists only within infrastructure components. - -\--- - -\# Architecture - -```text - - Runtime Cluster - - │ - - ┌───────────────┼───────────────┐ - - ▼ ▼ ▼ - - Tenant A Tenant B Tenant C - - │ │ │ - - ▼ ▼ ▼ - - Runtime Context Runtime Context Runtime Context - - │ │ │ - - ▼ ▼ ▼ - -Execution Txn Execution Txn Execution Txn - -``` - -Each tenant receives an isolated runtime context. - -\--- - -\# Tenant Context - -Every runtime execution SHALL execute within a Tenant Context. - -A Tenant Context MAY include: - -\* Tenant Identifier - -\* Runtime Configuration - -\* Policy Configuration - -\* Cryptographic Providers - -\* Storage Providers - -\* Verification Configuration - -Tenant Context is infrastructure metadata and is not part of the Core domain model. - -\--- - -\# Isolation Requirements - -Tenant isolation SHALL apply to: - -\* Execution - -\* Evidence - -\* Verification - -\* Storage - -\* Policy - -\* Runtime Configuration - -\* Cryptographic Providers - -A tenant SHALL NOT access another tenant's execution artifacts unless explicitly permitted by external platform controls. - -\--- - -\# Runtime Model - -```text - -Runtime - - │ - - ▼ - -Tenant Runtime - - │ - - ▼ - -Runtime Pipeline - - │ - - ▼ - -ExecutionTransaction - -``` - -The Runtime selects the appropriate Tenant Context before execution begins. - -\--- - -\# Storage - -Storage providers MAY: - -\* Share infrastructure. - -\* Partition data logically. - -\* Partition data physically. - -Regardless of implementation, the observable behavior SHALL preserve tenant isolation. - -\--- - -\# Verification - -Each tenant MAY operate: - -\* Its own Verification Engine. - -\* A shared Verification service. - -\* Distributed Verification. - -Verification SHALL operate only on execution records accessible within the applicable tenant context unless an external governance policy explicitly permits cross-tenant verification. - -\--- - -\# Policy - -Each tenant MAY define: - -\* Independent policies. - -\* Independent approval workflows. - -\* Independent policy providers. - -Policy evaluation occurs within the Tenant Context. - -\--- - -\# Cryptography - -Each tenant MAY configure: - -\* Independent key material. - -\* Independent provider registries. - -\* Independent cryptographic policies. - -Cryptographic metadata remains associated with individual execution records. - -\--- - -\# Runtime Context - -Example: - -```text - -Tenant Context - - - -Tenant ID - - - -Runtime Profile - - - -Policy Profile - - - -Storage Profile - - - -Crypto Profile - - - -Verification Profile - -``` - -The Runtime loads this context before pipeline execution. - -\--- - -\# Core Domain - -The following Core objects remain tenant-independent: - -\* Authority - -\* Intent - -\* Authorization - -\* Execution - -\* Evidence - -\* ExecutionTransaction - -The Core package SHALL NOT introduce tenant-specific fields. - -\--- - -\# Determinism - -Tenant Context SHALL be considered part of the execution environment. - -Any tenant-specific configuration that influences observable execution SHOULD be preserved as execution context or evidence sufficient to support replay and verification. - -Replay within the same tenant context SHALL produce equivalent execution semantics. - -\--- - -\# Package Mapping - -```text - -packages/ - - - -runtime/ - - tenancy/ - - TenantContext.ts - - TenantRuntime.ts - - TenantResolver.ts - - TenantConfiguration.ts - -``` - -Multi-tenancy extends Runtime without changing the Core domain. - -\--- - -\# Compatibility - -This RFC is backward compatible. - -Single-tenant deployments continue to operate unchanged. - -The default Runtime behaves as a single-tenant Runtime when no Tenant Context is configured. - -\--- - -\# Alternatives Considered - -\## Tenant Identifier in Core - -Rejected because tenancy is an infrastructure concern rather than a domain concept. - -Embedding tenant identifiers in domain objects would reduce portability and increase coupling. - -\--- - -\## Separate Runtime per Tenant - -Rejected because it increases operational complexity and resource usage for deployments that can safely share infrastructure. - -\--- - -\## Shared Execution Store - -Rejected because it weakens tenant isolation and complicates governance. - -Storage implementations may share infrastructure but must preserve logical isolation. - -\--- - -\# Open Questions - -\* Should tenant configuration be versioned? - -\* Should tenant migration tooling be standardized? - -\* Should tenant-specific conformance profiles exist? - -\* Should tenant capabilities be discoverable through the SDK? - -\--- - -\# Acceptance Criteria - -\* Runtime supports isolated Tenant Contexts. - -\* Core domain remains tenant-neutral. - -\* Execution records remain immutable. - -\* Verification supports tenant isolation. - -\* Storage preserves tenant boundaries. - -\* Replay functions correctly within isolated tenant contexts. - -\* Multi-tenancy requires no changes to the Core domain model. - -\--- - -\# References - -\* 001-ARCHITECTURE.md - -\* 003-EXECUTION-TRANSACTION.md - -\* 011-RUNTIME.md - -\* 012-RUNTIME-STAGES.md - -\* 013-VERIFICATION-ENGINE.md - -\* 015-PLATFORM-ARCHITECTURE.md - -\* 016-PLATFORM-GUARANTEES.md - -\* ADR-0002 — Immutable Domain Model - -\* ADR-0003 — Verification Is Independent - -\* ADR-0004 — Runtime Pipeline - -\* ADR-0007 — Deterministic Execution diff --git a/docs/rfcs/RFC-0007-Agent-SDK.md b/docs/rfcs/RFC-0007-Agent-SDK.md deleted file mode 100644 index 996f425e..00000000 --- a/docs/rfcs/RFC-0007-Agent-SDK.md +++ /dev/null @@ -1,429 +0,0 @@ -\# RFC-0007 — Agent SDK - -\*\*Status:\*\* Draft - -\*\*Author:\*\* Parmana Architecture Team - -\*\*Created:\*\* 2026-06-25 - -\*\*Target Version:\*\* 0.4.0 - -\--- - -\# Summary - -Introduce an Agent SDK that enables autonomous systems to integrate with Parmana and produce verifiable execution records. - -The Agent SDK provides a developer-friendly interface for recording authority, intent, authorization, execution, evidence, and verification while remaining independent of any specific AI framework or model provider. - -\--- - -\# Motivation - -Modern AI systems increasingly operate as autonomous agents capable of planning, reasoning, invoking tools, and executing actions. - -Organizations require evidence that these actions: - -\* Were authorized. - -\* Matched the intended objective. - -\* Produced immutable execution records. - -\* Can be independently verified. - -The Agent SDK provides this capability without replacing existing agent frameworks. - -\--- - -\# Goals - -\* Integrate with existing agent frameworks. - -\* Record immutable execution transactions. - -\* Preserve deterministic execution semantics. - -\* Support replay and verification. - -\* Provide a consistent developer experience. - -\--- - -\# Non-Goals - -This RFC does not define: - -\* An LLM abstraction. - -\* Prompt engineering. - -\* Agent orchestration. - -\* Planning algorithms. - -\* Memory systems. - -\* Vector databases. - -\* Tool execution frameworks. - -These remain the responsibility of external agent frameworks. - -\--- - -\# Architectural Principle - -The Agent SDK extends autonomous systems with Execution Trust. - -It does not replace agent runtimes. - -\--- - -\# Architecture - -```text - - AI Framework - - │ - - ▼ - - Agent SDK - - │ - - ┌──────────────┼──────────────┐ - - ▼ ▼ ▼ - - Runtime Verification Replay - - │ - - ▼ - - ExecutionTransaction - -``` - -The SDK acts as an integration layer between autonomous systems and the Parmana platform. - -\--- - -\# Responsibilities - -The Agent SDK SHALL: - -\* Create execution transactions. - -\* Capture authority and intent. - -\* Submit execution requests. - -\* Record execution evidence. - -\* Invoke verification. - -\* Support replay integration. - -The Agent SDK SHALL NOT: - -\* Execute LLM inference. - -\* Manage prompts. - -\* Implement planning. - -\* Replace orchestration frameworks. - -\--- - -\# Agent Execution Model - -A typical execution flow: - -```text - -Agent Request - - │ - - ▼ - -Authority - - │ - - ▼ - -Intent - - │ - - ▼ - -Authorization - - │ - - ▼ - -Execution - - │ - - ▼ - -Evidence - - │ - - ▼ - -Verification - -``` - -The SDK records each stage as part of the execution lifecycle. - -\--- - -\# Integration Model - -The SDK MAY integrate with: - -\* Single-agent systems. - -\* Multi-agent systems. - -\* Human-in-the-loop systems. - -\* Workflow engines. - -\* Custom automation platforms. - -Integration occurs through stable public APIs. - -\--- - -\# Developer API - -Illustrative example: - -```typescript - -const sdk = new AgentSDK(); - - - -const transaction = await sdk.execute({ - - authority, - - intent, - - execute: async () => { - - return toolResult; - - } - -}); - - - -const report = await sdk.verify(transaction); - -``` - -The API emphasizes execution recording rather than agent orchestration. - -\--- - -\# Execution Context - -The SDK MAY capture contextual information such as: - -\* Agent Identifier - -\* Session Identifier - -\* Tool Invocation Metadata - -\* Runtime Configuration - -\* Model Metadata - -\* Correlation Identifiers - -Context is recorded only when relevant to execution trust. - -\--- - -\# Evidence - -The SDK records immutable evidence, including: - -\* Tool invocations. - -\* Execution results. - -\* Approval outcomes. - -\* Policy decisions. - -\* Runtime metadata. - -Evidence remains append-only. - -\--- - -\# Verification - -The SDK exposes verification capabilities but does not implement verification itself. - -Verification is delegated to the Verification Engine. - -\--- - -\# Replay - -The SDK MAY expose replay functionality by delegating to the Replay Engine. - -Replay remains an independent subsystem. - -\--- - -\# Framework Independence - -The Agent SDK SHALL remain independent of any specific: - -\* AI model. - -\* LLM provider. - -\* Agent framework. - -\* Workflow engine. - -\* Cloud provider. - -Framework-specific integrations SHOULD be implemented as adapters. - -\--- - -\# Package Mapping - -```text - -packages/ - - - -sdk/ - - src/ - - AgentSDK.ts - - AgentBuilder.ts - - ExecutionClient.ts - - VerificationClient.ts - - ReplayClient.ts - - adapters/ - -``` - -Adapters provide integration with external ecosystems without changing the SDK's public contract. - -\--- - -\# Compatibility - -This RFC is backward compatible. - -Existing Runtime, Verification, and Replay packages require no architectural changes. - -The SDK is an additional integration layer. - -\--- - -\# Alternatives Considered - -\## Agent Framework - -Rejected because Parmana's purpose is Execution Trust, not agent orchestration. - -\--- - -\## Runtime-Coupled SDK - -Rejected because coupling the SDK directly to Runtime internals would reduce portability and hinder future implementations. - -\--- - -\## AI Provider SDK - -Rejected because model providers evolve rapidly. - -The SDK should remain provider-neutral and focus on execution trust. - -\--- - -\# Open Questions - -\* Should streaming tool execution be standardized? - -\* Should agent capabilities be discoverable? - -\* Should agent identity become a first-class domain concept? - -\* Should SDK adapters be maintained within the core repository or separately? - -\--- - -\# Acceptance Criteria - -\* An `AgentSDK` abstraction exists. - -\* The SDK integrates with Runtime through public APIs. - -\* Execution transactions are recorded immutably. - -\* Verification and Replay are exposed through client interfaces. - -\* The SDK remains independent of AI frameworks and model providers. - -\* Existing Core, Runtime, and Verification packages require no architectural changes. - -\--- - -\# References - -\* 003-EXECUTION-TRANSACTION.md - -\* 006-API.md - -\* 007-SDK.md - -\* 011-RUNTIME.md - -\* 013-VERIFICATION-ENGINE.md - -\* 014-EXECUTION-TRUST-MODEL.md - -\* ADR-0003 — Verification Is Independent - -\* ADR-0004 — Runtime Pipeline - -\* ADR-0007 — Deterministic Execution - -\* RFC-0002 — Replay Engine - -\* RFC-0003 — Human Approval diff --git a/docs/rfcs/RFC-0007-Canonical-Trust-Chain-Domain-Model.md b/docs/rfcs/RFC-0007-Canonical-Trust-Chain-Domain-Model.md deleted file mode 100644 index 8fc33dc7..00000000 --- a/docs/rfcs/RFC-0007-Canonical-Trust-Chain-Domain-Model.md +++ /dev/null @@ -1,205 +0,0 @@ -# RFC-0007: Canonical Trust Chain Domain Model - -**Status:** Accepted - -## Purpose - -This RFC defines the canonical domain model for Parmana. - -It establishes the immutable trust chain linking authority, authorization, intent, policy, execution, evidence, and verification. - -The trust chain is the foundation of Parmana's Execution Trust Infrastructure. - ---- - -# Canonical Trust Chain - -``` -Authority - │ - ▼ -Authorization - │ - ▼ -Intent - │ - ▼ -BusinessTransaction - │ - ▼ -PolicyReference -(name, version, schemaVersion) - │ - ▼ -Decision - │ - ▼ -TrustRecord - │ - ▼ -Receipt - │ - ▼ -Independent Verification -``` - -Every execution artifact is cryptographically linked to the previous artifact. - ---- - -# Domain Objects - -## Authority - -Represents the entity permitted to authorize execution. - -Examples: - -- Human approver -- Service account -- Organization -- Workflow owner - ---- - -## Authorization - -Represents the approval allowing execution. - -Examples: - -- Approval ID -- Digital signature -- Approval timestamp - ---- - -## Intent - -Represents what is intended to happen. - -Intent is independent of implementation. - -Examples: - -- Release vendor payment -- Approve loan -- Grant access - ---- - -## BusinessTransaction - -Represents the execution request. - -A BusinessTransaction SHALL contain a PolicyReference. - -The BusinessTransaction is the canonical execution input. - ---- - -## PolicyReference - -```ts -interface PolicyReference { - readonly name: string; - readonly version: string; - readonly schemaVersion: string; -} -``` - -The PolicyReference identifies the exact policy artifact used during execution. - -Runtime SHALL NOT infer or discover policies. - ---- - -## Decision - -Represents the deterministic outcome produced by evaluating the referenced policy. - -A Decision is derived only from: - -- Policy -- Signals -- Deterministic evaluation - ---- - -## TrustRecord - -Immutable evidence describing execution. - -A TrustRecord binds together: - -- BusinessTransaction -- PolicyReference -- Decision -- Hashes -- Metadata - ---- - -## Receipt - -Cryptographically signed proof of execution. - -A Receipt enables independent verification without trusting the runtime. - ---- - -## Verification - -Verification independently proves that: - -- the correct policy executed, -- the execution has not been modified, -- the evidence is authentic, -- the receipt signature is valid. - ---- - -# Trust Chain Principles - -The system SHALL maintain a cryptographically verifiable chain: - -``` -Authority -→ Authorization -→ Intent -→ BusinessTransaction -→ PolicyReference -→ Decision -→ TrustRecord -→ Receipt -→ Verification -``` - -Each stage depends only on the previous stage. - ---- - -# Architectural Invariants - -- BusinessTransaction SHALL reference exactly one PolicyReference. -- PolicyReference SHALL contain `name`, `version`, and `schemaVersion`. -- Runtime SHALL execute exactly one policy. -- Runtime SHALL NOT discover, scan, or guess policies. -- Decisions SHALL be deterministic. -- TrustRecords SHALL be immutable once created. -- Receipts SHALL be digitally signed. -- Verification SHALL be independent of the runtime. - ---- - -# Scope - -This RFC defines the canonical trust-chain domain model only. - -Policy loading, runtime architecture, policy versioning, and schema evolution are defined in separate RFCs. - ---- - -# Status - -This document is the canonical domain model for Parmana Phase 1 and serves as the foundation for all subsequent architecture and implementation RFCs. diff --git a/docs/rfcs/RFC-0008-Canonical-Policy-Architecture.md b/docs/rfcs/RFC-0008-Canonical-Policy-Architecture.md deleted file mode 100644 index 499acad7..00000000 --- a/docs/rfcs/RFC-0008-Canonical-Policy-Architecture.md +++ /dev/null @@ -1,235 +0,0 @@ -\# RFC-0008: Canonical Policy Architecture - -\*\*Status:\*\* Accepted - -\## Purpose - -This RFC defines the canonical policy architecture for Parmana. - -The goal is to ensure that policy selection is deterministic, auditable, and forms part of the Execution Trust Chain. - -\--- - -\# Problem - -A runtime must never infer which policy applies by scanning all available policies. - -Policy selection is an authorization decision, not a runtime decision. - -Allowing the runtime to discover or choose policies would make execution dependent on runtime state rather than the trusted execution request. - -\--- - -\# Canonical Architecture - -``` - -Authority - - ↓ - -Authorization - - ↓ - -Intent - - ↓ - -BusinessTransaction - - ↓ - -PolicyReference - - ↓ - -PolicyRouter - - ↓ - -PolicyRegistry - - ↓ - -PolicyEngine - - ↓ - -Decision - - ↓ - -Execution - -``` - -\--- - -\# BusinessTransaction - -The BusinessTransaction explicitly specifies the PolicyReference. - -Example: - -```typescript - -policy: { - - name: "vendor-payment", - - version: "1.0.0" - -} - -``` - -The PolicyReference is part of the cryptographically verifiable execution trust chain. - -\--- - -\# PolicyRouter - -PolicyRouter has a single responsibility. - -It loads the requested versioned policy from storage. - -Example: - -``` - -load(policyId, version) - -``` - -PolicyRouter MUST NOT: - -\* discover policies - -\* evaluate policies - -\* determine which policy applies - -\* scan every policy on disk - -\--- - -\# PolicyRegistry - -PolicyRegistry maintains metadata describing available policies. - -Responsibilities include: - -\* registering policies - -\* version lookup - -\* metadata lookup - -\* latest-version resolution - -PolicyRegistry does not evaluate policies. - -\--- - -\# PolicyEngine - -PolicyEngine evaluates exactly one loaded policy. - -Inputs: - -\* Policy - -\* Signals - -Outputs: - -\* Decision - -PolicyEngine must remain deterministic. - -\--- - -\# Runtime - -Runtime must never guess which policy applies. - -Runtime receives the PolicyReference from the BusinessTransaction and passes it unchanged through the execution pipeline. - -\--- - -\# Trust Chain - -The policy is part of the execution trust chain. - -``` - -Authority - - ↓ - -Authorization - - ↓ - -Intent - - ↓ - -PolicyReference - - ↓ - -Decision - - ↓ - -Execution - -``` - -Changing the policy changes the trust chain. - -\--- - -\# Determinism - -Deterministic execution requires: - -\* explicit PolicyReference - -\* immutable versioned policies - -\* evaluation of exactly one policy - -\* no policy discovery - -\* no policy guessing - -\--- - -\# Design Principles - -1\. Policy selection is an authorization decision. - -2\. Policy execution is a runtime decision. - -3\. PolicyRouter loads. - -4\. PolicyRegistry manages. - -5\. PolicyEngine evaluates. - -6\. Runtime orchestrates. - -7\. BusinessTransaction specifies the PolicyReference. - -8\. PolicyReference is part of the trust chain. - -\--- - -\# Architecture Lock - -This architecture is locked. - -Future changes may extend the policy language or storage mechanisms but must not change these responsibilities or reintroduce runtime policy discovery. diff --git a/docs/rfcs/RFC-0008-Generic-Policy-Runtime-Architecture.md b/docs/rfcs/RFC-0008-Generic-Policy-Runtime-Architecture.md deleted file mode 100644 index 27445acd..00000000 --- a/docs/rfcs/RFC-0008-Generic-Policy-Runtime-Architecture.md +++ /dev/null @@ -1,445 +0,0 @@ -\# RFC-0008: Generic Policy Runtime Architecture - -\*\*Status:\*\* Accepted - -\## Purpose - -This RFC defines the canonical architecture of the Parmana Policy Runtime. - -The runtime is responsible for loading, validating, and executing the exact policy referenced by a BusinessTransaction. It is deterministic, domain-independent, and does not contain business-specific logic. - -This RFC complements RFC-0007 (Canonical Trust Chain Domain Model) by defining the runtime responsibilities for policy execution. - -\--- - -\# Goals - -The Policy Runtime SHALL: - -\* Execute exactly one policy. - -\* Never infer or discover which policy applies. - -\* Be deterministic. - -\* Be domain independent. - -\* Support arbitrary business domains. - -\* Support future policy schema evolution. - -\* Produce verifiable execution evidence. - -\--- - -\# Architecture - -```text - -BusinessTransaction - - │ - - ▼ - -PolicyReference - -(name, version, schemaVersion) - - │ - - ▼ - -PolicyRegistry - -(metadata only) - - │ - - ▼ - -PolicyRouter - -(load exact artifact) - - │ - - ▼ - -PolicyAdapter - -(RuntimeTransaction → PolicySignals) - - │ - - ▼ - -PolicyEngine - - │ - - ▼ - -Decision - -``` - -\--- - -\# Policy Reference - -Every BusinessTransaction SHALL contain exactly one PolicyReference. - -```ts - -interface PolicyReference { - - readonly name: string; - - readonly version: string; - - readonly schemaVersion: string; - -} - -``` - -The PolicyReference uniquely identifies the policy artifact to execute. - -\--- - -\# Policy Artifact - -Every policy artifact SHALL declare: - -```json - -{ - - "policyId": "vendor-payment", - - "policyVersion": "1.0.0", - - "schemaVersion": "1.0.0", - - - - "signalsSchema": { - - "amount": "number", - - "currency": "string", - - "vendorVerified": "boolean" - - }, - - - - "rules": \[ - - ... - - ] - -} - -``` - -The runtime SHALL validate: - -\* policyId - -\* policyVersion - -\* schemaVersion - -before execution. - -\--- - -\# Runtime Transaction - -The runtime SHALL remain domain independent. - -```ts - -interface RuntimeTransaction { - - readonly signals: Record; - -} - -``` - -The runtime SHALL NOT contain payment-specific, healthcare-specific, or any other business-specific fields. - -\--- - -\# Policy Signals - -The Policy Engine evaluates generic runtime signals. - -```ts - -interface PolicySignals { - - \[key: string]: JsonValue; - -} - - - -type PolicyInput = PolicySignals; - -``` - -Signals are supplied by the runtime and interpreted by the policy. - -\--- - -\# Policy Adapter - -The PolicyAdapter converts a RuntimeTransaction into the PolicyInput consumed by the PolicyEngine. - -```text - -RuntimeTransaction - - │ - - ▼ - -PolicyAdapter - - │ - - ▼ - -PolicySignals - -``` - -The adapter SHALL NOT contain domain-specific mapping logic. - -\--- - -\# Policy Registry - -The PolicyRegistry manages policy metadata. - -Responsibilities: - -\* register policy metadata - -\* list policies - -\* resolve policy metadata - -The PolicyRegistry SHALL NOT execute policies. - -\--- - -\# Policy Router - -The PolicyRouter loads the exact policy artifact referenced by the BusinessTransaction. - -Responsibilities: - -\* locate policy artifact - -\* load policy JSON - -\* validate identifier - -\* validate business version - -\* validate schema version - -The PolicyRouter SHALL NOT: - -\* scan policies to determine applicability - -\* evaluate business rules - -\* choose among multiple policies - -Policy selection occurs before runtime execution. - -\--- - -\# Policy Engine - -The PolicyEngine evaluates exactly one loaded policy. - -Responsibilities: - -\* evaluate rules deterministically - -\* produce a Decision - -\* record evaluation trace - -The PolicyEngine SHALL NOT: - -\* load policies - -\* discover policies - -\* modify runtime state - -\--- - -\# Domain Independence - -The runtime supports any policy domain. - -Examples: - -Payment - -```json - -{ - - "signals": { - - "amount": 5000, - - "currency": "USD" - - } - -} - -``` - -Loan - -```json - -{ - - "signals": { - - "creditScore": 780, - - "income": 120000 - - } - -} - -``` - -Healthcare - -```json - -{ - - "signals": { - - "patientAge": 67, - - "diagnosis": "Diabetes" - - } - -} - -``` - -Cybersecurity - -```json - -{ - - "signals": { - - "riskScore": 91, - - "deviceTrusted": true - - } - -} - -``` - -No runtime code changes are required to support new domains. - -\--- - -\# Determinism - -For identical: - -\* PolicyReference - -\* Policy Artifact - -\* Runtime Signals - -the PolicyEngine SHALL always produce the same Decision. - -Determinism is a fundamental invariant of Parmana. - -\--- - -\# Separation of Responsibilities - -| Component | Responsibility | - -| ------------------- | ----------------------------------------- | - -| BusinessTransaction | References the policy | - -| PolicyReference | Identifies the exact policy | - -| PolicyRegistry | Maintains policy metadata | - -| PolicyRouter | Loads the exact policy artifact | - -| PolicyAdapter | Converts runtime data into policy signals | - -| PolicyEngine | Evaluates one policy | - -| Decision | Records evaluation result | - -No component performs another component's responsibility. - -\--- - -\# Architectural Invariants - -\* Exactly one policy SHALL be executed. - -\* Runtime SHALL NOT guess the applicable policy. - -\* Runtime SHALL NOT scan all policies. - -\* Policy loading SHALL be deterministic. - -\* Policy execution SHALL be deterministic. - -\* Business logic SHALL reside in policy artifacts. - -\* Runtime SHALL remain domain independent. - -\* New policy domains SHALL require no runtime code changes. - -\--- - -\# Relationship to RFC-0007 - -RFC-0007 defines the canonical trust-chain domain model. - -RFC-0008 defines how the runtime executes the PolicyReference contained within the BusinessTransaction while preserving determinism and execution trust. - -\--- - -\# Status - -This document defines the canonical Generic Policy Runtime Architecture for Parmana Phase 1. diff --git a/docs/rfcs/RFC-0008-WASM-Runtime.md b/docs/rfcs/RFC-0008-WASM-Runtime.md deleted file mode 100644 index f8b73fb0..00000000 --- a/docs/rfcs/RFC-0008-WASM-Runtime.md +++ /dev/null @@ -1,371 +0,0 @@ -\# RFC-0008 — WASM Runtime - -\*\*Status:\*\* Draft - -\*\*Author:\*\* Parmana Architecture Team - -\*\*Created:\*\* 2026-06-25 - -\*\*Target Version:\*\* 0.4.0 - -\--- - -\# Summary - -Introduce a WebAssembly (WASM) Runtime implementation that enables Parmana to execute in portable, sandboxed environments while preserving deterministic execution and execution trust guarantees. - -The WASM Runtime is an alternative Runtime implementation and SHALL conform to the Parmana Specification. - -\--- - -\# Motivation - -Modern applications increasingly execute at the edge, inside browsers, serverless platforms, embedded systems, and sandboxed execution environments. - -WebAssembly provides: - -\* Platform independence. - -\* Sandboxed execution. - -\* Near-native performance. - -\* Broad runtime availability. - -\* Strong isolation. - -A WASM Runtime enables Parmana to execute consistently across these environments. - -\--- - -\# Goals - -\* Provide a portable Runtime implementation. - -\* Preserve deterministic execution. - -\* Support Replay and Verification. - -\* Enable edge deployment. - -\* Maintain specification conformance. - -\--- - -\# Non-Goals - -This RFC does not define: - -\* Browser APIs. - -\* JavaScript SDKs. - -\* Edge platform integrations. - -\* WASI extensions. - -\* Container orchestration. - -These concerns remain implementation specific. - -\--- - -\# Architectural Principle - -The Parmana Runtime architecture remains unchanged. - -The WASM Runtime is simply another implementation of the Runtime contract. - -\--- - -\# Architecture - -```text - - Parmana Specification - - │ - - ┌────────────┼────────────┐ - - ▼ ▼ ▼ - - Native Runtime WASM Runtime Future Runtime - - │ │ - - ▼ ▼ - - ExecutionTransaction - -``` - -All Runtime implementations produce equivalent observable behavior. - -\--- - -\# Runtime Contract - -The WASM Runtime SHALL: - -\* Execute the Runtime Pipeline. - -\* Produce immutable ExecutionTransactions. - -\* Generate append-only evidence. - -\* Preserve deterministic behavior. - -The WASM Runtime SHALL NOT: - -\* Modify the Core domain model. - -\* Change execution semantics. - -\* Introduce platform-specific observable behavior. - -\--- - -\# Execution Model - -```text - -Application - - │ - - ▼ - -WASM Runtime - - │ - - ▼ - -Runtime Pipeline - - │ - - ▼ - -ExecutionTransaction - -``` - -Execution semantics remain identical to the native Runtime. - -\--- - -\# Determinism - -Equivalent execution inputs SHALL produce equivalent observable outputs regardless of whether execution occurs in: - -\* Native Runtime - -\* WASM Runtime - -Platform-specific implementation details SHALL NOT influence execution semantics. - -\--- - -\# Runtime Environment - -The WASM Runtime MAY execute in: - -\* Web browsers. - -\* Edge runtimes. - -\* Serverless platforms. - -\* WASI environments. - -\* Embedded systems. - -\* Desktop applications. - -Deployment environment SHALL NOT affect observable execution behavior. - -\--- - -\# Storage - -The WASM Runtime delegates persistence to Storage providers. - -Storage implementations MAY differ across environments while preserving the Storage contract. - -\--- - -\# Verification - -Execution records produced by the WASM Runtime SHALL be verifiable by any compliant Verification Engine. - -Verification remains independent of the Runtime implementation. - -\--- - -\# Replay - -Replay SHALL reconstruct executions produced by the WASM Runtime without requiring the original execution environment. - -Replay semantics remain identical to native Runtime executions. - -\--- - -\# Security - -The WASM Runtime benefits from: - -\* Process isolation. - -\* Memory isolation. - -\* Sandboxed execution. - -\* Restricted host access. - -Security properties provided by the execution environment complement, but do not replace, Parmana's execution trust guarantees. - -\--- - -\# Performance - -The specification imposes no performance requirements. - -Implementations MAY optimize: - -\* Startup time. - -\* Memory usage. - -\* Binary size. - -\* Throughput. - -Optimizations SHALL NOT alter observable execution semantics. - -\--- - -\# Package Mapping - -```text - -packages/ - - - -runtime/ - - native/ - - - - wasm/ - - WasmRuntime.ts - - WasmRuntimeBuilder.ts - - WasmPipeline.ts - -``` - -Both implementations conform to the same Runtime interfaces. - -\--- - -\# Compatibility - -This RFC is fully backward compatible. - -No changes are required to: - -\* Core - -\* Runtime contracts - -\* Verification - -\* Replay - -\* Storage - -\* SDK - -The WASM Runtime is an additional implementation. - -\--- - -\# Alternatives Considered - -\## Browser-Specific Runtime - -Rejected because Parmana should remain deployment-platform independent. - -\--- - -\## JavaScript Runtime - -Rejected because JavaScript is a language runtime rather than a portable execution target. - -WebAssembly provides broader deployment flexibility. - -\--- - -\## Separate WASM Architecture - -Rejected because multiple architectural models would weaken interoperability. - -All Runtime implementations should share the same execution semantics. - -\--- - -\# Open Questions - -\* Should WASI become the preferred host interface? - -\* Should host capability restrictions be standardized? - -\* Should deterministic resource limits become part of the Runtime contract? - -\* Should precompiled Runtime modules be distributed as release artifacts? - -\--- - -\# Acceptance Criteria - -\* A WASM Runtime implementation exists. - -\* The Runtime conforms to the existing Runtime contract. - -\* Execution semantics match the native Runtime. - -\* Verification operates without modification. - -\* Replay supports WASM-generated execution records. - -\* No Core domain changes are required. - -\--- - -\# References - -\* 001-ARCHITECTURE.md - -\* 003-EXECUTION-TRANSACTION.md - -\* 011-RUNTIME.md - -\* 012-RUNTIME-STAGES.md - -\* 013-VERIFICATION-ENGINE.md - -\* 015-PLATFORM-ARCHITECTURE.md - -\* 016-PLATFORM-GUARANTEES.md - -\* ADR-0004 — Runtime Pipeline - -\* ADR-0007 — Deterministic Execution - -\* RFC-0002 — Replay Engine diff --git a/docs/rfcs/RFC-0009-Policy-Versioning-and-Resolution.md b/docs/rfcs/RFC-0009-Policy-Versioning-and-Resolution.md deleted file mode 100644 index d9cf9ce7..00000000 --- a/docs/rfcs/RFC-0009-Policy-Versioning-and-Resolution.md +++ /dev/null @@ -1,411 +0,0 @@ -\# RFC-0009: Policy Versioning and Resolution - -\*\*Status:\*\* Accepted - -\## Purpose - -This RFC defines the canonical model for policy identification, versioning, and resolution in Parmana. - -Its purpose is to ensure that every execution is reproducible, deterministic, and independently verifiable by uniquely identifying the policy artifact used during execution. - -\--- - -\# Goals - -The policy versioning model SHALL: - -\* uniquely identify every policy artifact - -\* support deterministic execution - -\* support independent verification - -\* support future schema evolution - -\* eliminate runtime policy discovery - -\* preserve execution reproducibility - -\--- - -\# Policy Identity - -A policy is uniquely identified by three attributes: - -```ts - -interface PolicyReference { - - readonly name: string; - - readonly version: string; - - readonly schemaVersion: string; - -} - -``` - -These attributes form the canonical identity of a policy artifact. - -\--- - -\# Policy Name - -The policy name identifies the business policy. - -Examples: - -``` - -vendor-payment - -loan-approval - -employee-access - -insurance-claim - -purchase-order - -``` - -The name is stable across business versions. - -\--- - -\# Business Version - -The business version identifies the business logic revision. - -Example: - -``` - -1.0.0 - -1.1.0 - -2.0.0 - -``` - -Changing business rules requires a new business version. - -Examples: - -\* approval thresholds - -\* risk scoring logic - -\* authorization requirements - -\* rule ordering - -Business versions SHALL be immutable. - -\--- - -\# Schema Version - -The schema version identifies the structure of the policy artifact. - -Examples: - -``` - -1.0.0 - -2.0.0 - -``` - -Changing the artifact format requires a schema version change. - -Examples include: - -\* new JSON fields - -\* modified field structure - -\* new condition operators - -\* metadata changes - -Changing the schema SHALL NOT imply a change in business logic. - -\--- - -\# Policy Artifact - -Each policy SHALL declare its identity. - -Example: - -```json - -{ - - "policyId": "vendor-payment", - - "policyVersion": "1.0.0", - - "schemaVersion": "1.0.0", - - - - "signalsSchema": { - - "amount": "number", - - "vendorVerified": "boolean" - - }, - - - - "rules": \[ - - ... - - ] - -} - -``` - -\--- - -\# Policy Directory Layout - -The canonical layout is: - -```text - -policies/ - -└── vendor-payment/ - - ├── 1.0.0/ - - │ └── policy.json - - ├── 1.1.0/ - - │ └── policy.json - - └── 2.0.0/ - - └── policy.json - -``` - -The directory structure is organized by business version. - -The policy artifact itself declares both the business version and schema version. - -\--- - -\# Resolution Process - -Policy resolution SHALL follow this sequence: - -```text - -BusinessTransaction - - │ - - ▼ - -PolicyReference - -(name, version, schemaVersion) - - │ - - ▼ - -PolicyRegistry - -(metadata lookup) - - │ - - ▼ - -PolicyRouter - -(load exact artifact) - - │ - - ▼ - -Validate: - -• policyId - -• policyVersion - -• schemaVersion - - │ - - ▼ - -PolicyEngine - -``` - -No policy discovery occurs during resolution. - -\--- - -\# Policy Registry Responsibilities - -The PolicyRegistry SHALL: - -\* register policy metadata - -\* resolve policy metadata - -\* list available policies - -The PolicyRegistry SHALL NOT: - -\* execute policies - -\* evaluate rules - -\* choose policies - -\--- - -\# Policy Router Responsibilities - -The PolicyRouter SHALL: - -\* load the exact referenced policy - -\* read the policy artifact - -\* validate: - - \* policyId - - \* policyVersion - - \* schemaVersion - -The PolicyRouter SHALL NOT: - -\* scan all policies - -\* infer applicable policies - -\* select the best policy - -\* evaluate business logic - -\--- - -\# Runtime Responsibilities - -The runtime SHALL execute only the policy referenced by the BusinessTransaction. - -The runtime SHALL NOT determine which policy should execute. - -Policy selection occurs before runtime execution. - -\--- - -\# Deterministic Resolution - -Given the same: - -\* PolicyReference - -\* policy artifact - -\* runtime signals - -the runtime SHALL always load the same policy artifact and produce the same decision. - -Policy resolution is therefore deterministic. - -\--- - -\# Version Evolution - -Business Version changes: - -\* business rules - -\* approval logic - -\* decision behavior - -Schema Version changes: - -\* artifact structure - -\* metadata - -\* serialization format - -\* policy language capabilities - -Business versions and schema versions evolve independently. - -\--- - -\# Verification - -A verifier SHALL confirm that: - -\* the requested policy name matches - -\* the requested business version matches - -\* the requested schema version matches - -\* the executed policy artifact matches the reference - -Failure of any validation SHALL invalidate the execution. - -\--- - -\# Architectural Invariants - -\* Every BusinessTransaction SHALL reference exactly one PolicyReference. - -\* Policy identity SHALL consist of name, version, and schemaVersion. - -\* Policy artifacts SHALL declare their own identity. - -\* Runtime SHALL execute exactly one policy. - -\* Runtime SHALL NOT discover policies. - -\* Runtime SHALL NOT guess policy versions. - -\* Policy resolution SHALL be deterministic. - -\* Policy artifacts SHALL be immutable after publication. - -\--- - -\# Relationship to Other RFCs - -\* \*\*RFC-0007\*\* defines the canonical trust-chain domain model. - -\* \*\*RFC-0008\*\* defines the generic policy runtime architecture. - -\* \*\*RFC-0009\*\* defines how policies are uniquely identified and deterministically resolved. - -\--- - -\# Status - -This document defines the canonical policy versioning and resolution model for Parmana Phase 1 and establishes the foundation for deterministic, reproducible, and independently verifiable policy execution. diff --git a/docs/rfcs/RFC-0009-Streaming-Evidence.md b/docs/rfcs/RFC-0009-Streaming-Evidence.md deleted file mode 100644 index 9e8c2c73..00000000 --- a/docs/rfcs/RFC-0009-Streaming-Evidence.md +++ /dev/null @@ -1,425 +0,0 @@ -\# RFC-0009 — Streaming Evidence - -\*\*Status:\*\* Draft - -\*\*Author:\*\* Parmana Architecture Team - -\*\*Created:\*\* 2026-06-25 - -\*\*Target Version:\*\* 0.4.0 - -\--- - -\# Summary - -Introduce Streaming Evidence to allow long-running executions to emit immutable evidence artifacts incrementally before execution completes. - -Streaming Evidence enables near real-time observability, monitoring, and audit without compromising Parmana's append-only evidence model or deterministic execution guarantees. - -The ExecutionTransaction remains the aggregate root. Streaming Evidence extends how evidence is recorded, not how execution is modeled. - -\--- - -\# Motivation - -Some executions may last: - -\* Minutes - -\* Hours - -\* Days - -\* Weeks - -Waiting until completion to record evidence delays: - -\* Monitoring - -\* Incident response - -\* Compliance visibility - -\* Human supervision - -\* Operational diagnostics - -Streaming Evidence allows immutable execution facts to be recorded as they occur. - -\--- - -\# Goals - -\* Record evidence incrementally. - -\* Preserve append-only semantics. - -\* Preserve deterministic replay. - -\* Support long-running execution. - -\* Enable real-time consumers. - -\--- - -\# Non-Goals - -This RFC does not define: - -\* Event streaming platforms. - -\* Message brokers. - -\* Transport protocols. - -\* Observability dashboards. - -\* Workflow orchestration. - -Streaming transport remains implementation specific. - -\--- - -\# Architectural Principle - -Execution remains atomic. - -Evidence becomes incrementally observable. - -Execution semantics remain unchanged. - -\--- - -\# Architecture - -```text - -Execution - - │ - - ├───────────────┐ - - ▼ ▼ - -Evidence 1 Evidence Stream - - │ │ - - ▼ ▼ - -Evidence 2 Subscribers - - │ - - ▼ - -Evidence 3 - - │ - - ▼ - -Execution Complete - - │ - - ▼ - -ExecutionTransaction - -``` - -Evidence artifacts are appended throughout execution. - -\--- - -\# Evidence Model - -Streaming Evidence SHALL consist of immutable Evidence Artifacts. - -Each artifact: - -\* Represents a factual observation. - -\* Is immutable. - -\* Is append-only. - -\* Has a deterministic order. - -Previously emitted artifacts SHALL NOT be modified. - -\--- - -\# Streaming Model - -```text - -Execution - - - -Artifact 1 - - - -Artifact 2 - - - -Artifact 3 - - - -Artifact 4 - - - -Execution Complete - -``` - -Each artifact becomes immediately available after creation. - -\--- - -\# Ordering - -Streaming Evidence SHALL preserve logical ordering. - -Every artifact SHALL include ordering metadata sufficient to reconstruct the original execution sequence. - -Ordering MAY be represented by: - -\* Sequence Number - -\* Event Index - -\* Logical Clock - -\* Equivalent deterministic mechanism - -\--- - -\# Runtime Integration - -The Runtime MAY emit evidence after each Runtime Stage. - -Example: - -```text - -AuthorityStage - - │ - - ▼ - -Evidence - - - -IntentStage - - │ - - ▼ - -Evidence - - - -AuthorizationStage - - │ - - ▼ - -Evidence - - - -ExecutionStage - - │ - - ▼ - -Evidence - -``` - -Streaming is an implementation capability. - -The Runtime Pipeline remains unchanged. - -\--- - -\# ExecutionTransaction - -ExecutionTransaction SHALL contain the complete ordered evidence collection after execution completes. - -Streaming does not alter the final execution record. - -\--- - -\# Verification - -The Verification Engine verifies the completed evidence collection. - -Verification SHALL NOT require live evidence streaming. - -Streaming is an optimization for operational visibility, not a verification requirement. - -\--- - -\# Replay - -Replay consumes the completed immutable evidence collection. - -Replay MAY reconstruct intermediate execution milestones using streaming order metadata. - -Replay SHALL remain deterministic. - -\--- - -\# Subscribers - -Implementations MAY expose evidence to subscribers. - -Typical consumers include: - -\* Monitoring systems. - -\* Audit systems. - -\* Human approval workflows. - -\* Incident response tools. - -\* Compliance platforms. - -Subscribers SHALL NOT modify evidence. - -\--- - -\# Storage - -Storage SHALL preserve: - -\* Artifact ordering. - -\* Artifact immutability. - -\* Append-only semantics. - -Storage implementations MAY persist artifacts individually before assembling the final execution record. - -\--- - -\# Package Mapping - -```text - -packages/ - - - -runtime/ - - streaming/ - - EvidencePublisher.ts - - EvidenceSubscriber.ts - - EvidenceStream.ts - -``` - -Streaming components extend Runtime without modifying the Core domain model. - -\--- - -\# Compatibility - -This RFC is fully backward compatible. - -Implementations that do not support streaming continue to produce identical completed ExecutionTransactions. - -Streaming is optional. - -\--- - -\# Alternatives Considered - -\## Buffer Until Completion - -Rejected because long-running executions benefit from incremental visibility. - -\--- - -\## Mutable Evidence - -Rejected because modifying evidence violates append-only semantics and weakens execution trust. - -\--- - -\## Event-Sourced Runtime - -Rejected because Parmana records execution evidence rather than treating events as the primary execution model. - -ExecutionTransaction remains the aggregate root. - -\--- - -\# Open Questions - -\* Should evidence streams support filtering? - -\* Should subscribers receive cryptographically signed artifacts? - -\* Should evidence batching be standardized? - -\* Should streaming back-pressure be part of the Runtime contract? - -\--- - -\# Acceptance Criteria - -\* Runtime supports incremental evidence publication. - -\* Evidence remains immutable. - -\* Evidence remains append-only. - -\* ExecutionTransaction contains the complete ordered evidence collection. - -\* Verification requires no architectural changes. - -\* Replay remains deterministic. - -\--- - -\# References - -\* 003-EXECUTION-TRANSACTION.md - -\* 010-EVIDENCE.md - -\* 011-RUNTIME.md - -\* 012-RUNTIME-STAGES.md - -\* 013-VERIFICATION-ENGINE.md - -\* 016-PLATFORM-GUARANTEES.md - -\* ADR-0004 — Runtime Pipeline - -\* ADR-0005 — Evidence Is Append-Only - -\* ADR-0007 — Deterministic Execution - -\* RFC-0002 — Replay Engine diff --git a/docs/rfcs/RFC-0010-Execution-Proofs.md b/docs/rfcs/RFC-0010-Execution-Proofs.md deleted file mode 100644 index ba35c019..00000000 --- a/docs/rfcs/RFC-0010-Execution-Proofs.md +++ /dev/null @@ -1,405 +0,0 @@ -\# RFC-0010 — Execution Proofs - -\*\*Status:\*\* Draft - -\*\*Author:\*\* Parmana Architecture Team - -\*\*Created:\*\* 2026-06-25 - -\*\*Target Version:\*\* 0.5.0 - -\--- - -\# Summary - -Introduce \*\*Execution Proofs\*\*, portable cryptographically protected artifacts that enable third parties to independently validate that an execution satisfies the Parmana Execution Trust Model. - -Execution Proofs provide a transportable representation of execution trust without requiring direct access to the originating Runtime. - -Execution Proofs extend existing execution artifacts and do not replace the ExecutionTransaction. - -\--- - -\# Motivation - -ExecutionTrust should be portable. - -Organizations increasingly need to demonstrate execution trust across organizational boundaries. - -Examples include: - -\* Regulatory submissions - -\* Customer audits - -\* Partner integrations - -\* Supply chains - -\* Financial systems - -\* AI agent ecosystems - -Sharing an entire execution database is unnecessary. - -Instead, organizations should be able to exchange a compact proof describing the execution. - -\--- - -\# Goals - -\* Produce portable trust artifacts. - -\* Support independent verification. - -\* Preserve cryptographic integrity. - -\* Support long-term archival. - -\* Avoid Runtime dependencies. - -\--- - -\# Non-Goals - -This RFC does not define: - -\* Zero-knowledge proofs. - -\* Blockchain proofs. - -\* Consensus protocols. - -\* Formal mathematical proofs. - -\* Identity systems. - -Execution Proofs are execution trust artifacts. - -\--- - -\# Architectural Principle - -Execution Proofs are derived from immutable execution records. - -They never replace: - -\* ExecutionTransaction - -\* Evidence - -\* Verification Report - -Instead, they summarize and protect trust information. - -\--- - -\# Architecture - -```text - -ExecutionTransaction - - │ - - ▼ - -Verification Engine - - │ - - ▼ - -Execution Proof Generator - - │ - - ▼ - -Execution Proof - - │ - - ▼ - -External Verifier - -``` - -Execution Proofs are generated after successful verification. - -\--- - -\# Proof Model - -An Execution Proof SHALL contain: - -\* Proof Identifier - -\* Execution Transaction Identifier - -\* Verification Status - -\* Evidence Digest - -\* Cryptographic Metadata - -\* Specification Version - -\* Generation Timestamp - -\* Optional Signature - -Execution Proofs are immutable. - -\--- - -\# Evidence Digest - -Execution Proofs SHOULD include a digest representing the associated evidence. - -The digest enables integrity verification without embedding every evidence artifact. - -Evidence remains the authoritative execution record. - -\--- - -\# Proof Generation - -Proof generation SHALL occur only after verification completes successfully. - -Typical flow: - -```text - -Execution - - │ - - ▼ - -Evidence - - │ - - ▼ - -Verification - - │ - - ▼ - -Execution Proof - -``` - -Proofs represent verified execution. - -\--- - -\# Verification - -Third parties SHALL verify an Execution Proof by validating: - -\* Proof integrity. - -\* Cryptographic metadata. - -\* Evidence digest. - -\* Verification metadata. - -\* Specification compatibility. - -Verification SHALL NOT require access to the originating Runtime. - -\--- - -\# Replay - -Replay remains based on the complete ExecutionTransaction. - -Execution Proofs are not replay artifacts. - -Replay MAY use an Execution Proof to validate integrity before reconstructing execution. - -\--- - -\# Transport - -Execution Proofs MAY be exchanged using: - -\* Files - -\* HTTP APIs - -\* Message queues - -\* Object storage - -\* Email attachments - -\* Physical media - -The transport mechanism is outside the scope of this RFC. - -\--- - -\# Cryptography - -Execution Proofs leverage the provider model defined by the Cryptography package. - -Proofs remain algorithm independent. - -Historical proofs remain verifiable using the recorded cryptographic metadata. - -\--- - -\# Trust Model - -Execution Trust becomes portable. - -```text - -Execution - - │ - - ▼ - -Evidence - - │ - - ▼ - -Verification - - │ - - ▼ - -Execution Proof - - │ - - ▼ - -Independent Trust - -``` - -Trust is derived from immutable execution artifacts rather than assertions. - -\--- - -\# Package Mapping - -```text - -packages/ - - - -proof/ - - src/ - - ExecutionProof.ts - - ProofGenerator.ts - - ProofVerifier.ts - - ProofSerializer.ts - -``` - -Execution Proofs form an independent package layered above Verification. - -\--- - -\# Compatibility - -This RFC is backward compatible. - -Existing Runtime, Verification, Replay, and Storage implementations remain unchanged. - -Execution Proofs are an optional capability built on verified execution records. - -\--- - -\# Alternatives Considered - -\## Sharing Full Execution Records - -Rejected because complete execution records may contain unnecessary operational detail and increase data transfer requirements. - -\--- - -\## Blockchain-Based Proofs - -Rejected because Parmana establishes trust through deterministic verification of immutable execution records rather than distributed consensus. - -\--- - -\## Runtime-Generated Proofs - -Rejected because proofs should only be generated after successful independent verification. - -\--- - -\# Open Questions - -\* Should proof profiles be standardized for different regulatory environments? - -\* Should proofs support selective disclosure? - -\* Should proofs be time-stamped by external trust services? - -\* Should proof expiration policies be standardized? - -\* Should proof bundles contain multiple execution proofs? - -\--- - -\# Acceptance Criteria - -\* An `ExecutionProof` model exists. - -\* Proof generation occurs after successful verification. - -\* Proofs are immutable. - -\* Proofs support independent verification. - -\* Proofs remain cryptographically agile. - -\* Runtime and Core packages require no architectural changes. - -\--- - -\# References - -\* 005-CRYPTOGRAPHY.md - -\* 010-EVIDENCE.md - -\* 013-VERIFICATION-ENGINE.md - -\* 014-EXECUTION-TRUST-MODEL.md - -\* 016-PLATFORM-GUARANTEES.md - -\* ADR-0003 — Verification Is Independent - -\* ADR-0005 — Evidence Is Append-Only - -\* ADR-0006 — Cryptographic Agility - -\* ADR-0007 — Deterministic Execution - -\* RFC-0002 — Replay Engine - -\* RFC-0005 — Distributed Verification diff --git a/docs/rfcs/RFC-0010-Policy-Schema-Evolution.md b/docs/rfcs/RFC-0010-Policy-Schema-Evolution.md deleted file mode 100644 index 968dc926..00000000 --- a/docs/rfcs/RFC-0010-Policy-Schema-Evolution.md +++ /dev/null @@ -1,335 +0,0 @@ -\# RFC-0010: Policy Schema Evolution - -\*\*Status:\*\* Accepted - -\## Purpose - -This RFC defines how the structure of policy artifacts evolves over time while preserving deterministic execution, replayability, and independent verification. - -Policy schema evolution enables Parmana to introduce new policy language capabilities without breaking previously executed Business Transactions. - -\--- - -\# Goals - -Policy schema evolution SHALL: - -\* preserve deterministic execution - -\* preserve replayability - -\* preserve independent verification - -\* allow policy language evolution - -\* avoid breaking existing policy artifacts - -\* maintain immutable historical artifacts - -\--- - -\# Definitions - -\## Business Version - -The business version identifies the business rules implemented by a policy. - -Example: - -```text id="4uxifw" - -1.0.0 - -1.1.0 - -2.0.0 - -``` - -Business version changes when business logic changes. - -\--- - -\## Schema Version - -The schema version identifies the structure and capabilities of the policy artifact. - -Example: - -```text id="xemjlwm" - -1.0.0 - -2.0.0 - -``` - -Schema version changes when the policy language or artifact format changes. - -Business versions and schema versions evolve independently. - -\--- - -\# Policy Identity - -Every policy SHALL declare: - -```json id="e89z0u" - -{ - - "policyId": "vendor-payment", - - "policyVersion": "1.0.0", - - "schemaVersion": "1.0.0" - -} - -``` - -The runtime SHALL validate all three values before execution. - -\--- - -\# What Requires a Schema Version Change - -A schema version SHALL change whenever the policy artifact structure changes. - -Examples include: - -\* new top-level JSON fields - -\* renamed fields - -\* removed fields - -\* new rule syntax - -\* new condition operators - -\* new metadata sections - -\* new evaluation semantics - -\--- - -\# What Does NOT Require a Schema Version Change - -The following do not require a schema version change: - -\* changing rule thresholds - -\* changing business limits - -\* adding business rules - -\* removing business rules - -\* modifying decision reasons - -\* changing approval values - -These are business logic changes and require only a new business version. - -\--- - -\# Examples - -\## Business Version Change - -Version 1.0.0 - -```json id="2ohwd9" - -{ - - "policyVersion": "1.0.0", - - "schemaVersion": "1.0.0" - -} - -``` - -Version 1.1.0 - -```json id="8k5oqc" - -{ - - "policyVersion": "1.1.0", - - "schemaVersion": "1.0.0" - -} - -``` - -The artifact structure is identical. - -Only business rules changed. - -\--- - -\## Schema Version Change - -Schema 1.0.0 - -```json id="r4i9hl" - -{ - - "condition": { - - "greater\_than": 1000 - - } - -} - -``` - -Schema 2.0.0 - -```json id="cybws7" - -{ - - "condition": { - - "greater\_than\_or\_equal": 1000 - - } - -} - -``` - -The policy language changed. - -A new schema version is required. - -\--- - -\# Runtime Responsibilities - -The runtime SHALL: - -\* read PolicyReference - -\* load the referenced policy artifact - -\* validate schemaVersion - -\* reject incompatible policy artifacts - -The runtime SHALL NOT: - -\* automatically migrate policies - -\* rewrite policy artifacts - -\* silently ignore schema differences - -\--- - -\# Replay - -Replay SHALL use the exact policy artifact referenced by the original BusinessTransaction. - -Replay SHALL NOT substitute: - -\* newer business versions - -\* newer schema versions - -Historical executions remain reproducible. - -\--- - -\# Verification - -Independent verification SHALL confirm: - -\* policyId matches - -\* policyVersion matches - -\* schemaVersion matches - -If any validation fails, verification SHALL fail. - -\--- - -\# Backward Compatibility - -Schema evolution is additive. - -Older schema versions remain valid and executable. - -The runtime may support multiple schema versions simultaneously, provided each version has deterministic evaluation semantics. - -\--- - -\# Forward Compatibility - -Older runtimes SHALL NOT execute policy artifacts with unsupported schema versions. - -Instead, execution SHALL fail with an explicit schema compatibility error. - -Silent fallback is prohibited. - -\--- - -\# Migration Strategy - -When introducing a new schema version: - -1\. Publish the new schema specification. - -2\. Implement runtime support. - -3\. Publish new policy artifacts. - -4\. Continue supporting existing schema versions until formally deprecated. - -Previously published policy artifacts SHALL remain immutable. - -\--- - -\# Architectural Invariants - -\* Business version and schema version evolve independently. - -\* Policy artifacts are immutable after publication. - -\* Schema changes SHALL be explicit. - -\* Runtime SHALL validate schemaVersion. - -\* Replay SHALL use the original schema version. - -\* Verification SHALL validate schemaVersion. - -\* Automatic schema migration is prohibited. - -\* Deterministic execution SHALL be preserved across all supported schema versions. - -\--- - -\# Relationship to Other RFCs - -\* \*\*RFC-0007\*\* defines the canonical trust-chain domain model. - -\* \*\*RFC-0008\*\* defines the generic policy runtime architecture. - -\* \*\*RFC-0009\*\* defines policy versioning and deterministic resolution. - -\* \*\*RFC-0010\*\* defines how policy schemas evolve without compromising execution trust. - -\--- - -\# Status - -This document defines the canonical policy schema evolution strategy for Parmana Phase 1 and establishes the rules for introducing future policy language capabilities while preserving deterministic, replayable, and independently verifiable execution. diff --git a/docs/rfcs/RFC-0011-Deterministic-Policy-Execution-Architecture.md b/docs/rfcs/RFC-0011-Deterministic-Policy-Execution-Architecture.md deleted file mode 100644 index e11cccf8..00000000 --- a/docs/rfcs/RFC-0011-Deterministic-Policy-Execution-Architecture.md +++ /dev/null @@ -1,421 +0,0 @@ -\# RFC-0011: Deterministic Policy Execution Architecture - -\*\*Status:\*\* Accepted - -\## Purpose - -This RFC defines the canonical execution architecture of the Parmana Runtime. - -Its purpose is to ensure that policy execution is deterministic, reproducible, independently verifiable, and free from runtime policy-selection logic. - -This RFC establishes the execution contract between the Business Transaction, Policy Reference, Policy Registry, Policy Router, and Policy Engine. - -\--- - -\# Design Principles - -The runtime SHALL: - -\* execute exactly one policy - -\* never determine which policy applies - -\* never infer business intent - -\* never discover policies dynamically - -\* remain deterministic - -\* remain domain independent - -Business decisions belong to policy artifacts. - -Execution belongs to the runtime. - -\--- - -\# Canonical Execution Pipeline - -```text - -Authority - - │ - - ▼ - -Authorization - - │ - - ▼ - -Intent - - │ - - ▼ - -BusinessTransaction - - │ - - ▼ - -PolicyReference - -(name, version, schemaVersion) - - │ - - ▼ - -PolicyRegistry - -(metadata only) - - │ - - ▼ - -PolicyRouter - -(load exact policy artifact) - - │ - - ▼ - -Policy Schema Validation - - │ - - ▼ - -PolicyAdapter - -(RuntimeTransaction → PolicySignals) - - │ - - ▼ - -PolicyEngine - - │ - - ▼ - -Decision - - │ - - ▼ - -TrustRecord - - │ - - ▼ - -Receipt - - │ - - ▼ - -Independent Verification - -``` - -Each stage has a single responsibility. - -No stage performs another stage's responsibility. - -\--- - -\# BusinessTransaction - -A BusinessTransaction SHALL contain exactly one PolicyReference. - -The runtime SHALL NOT determine which policy to execute. - -Policy selection occurs before runtime execution. - -\--- - -\# PolicyReference - -Every execution SHALL identify exactly one policy artifact. - -```ts - -interface PolicyReference { - - readonly name: string; - - readonly version: string; - - readonly schemaVersion: string; - -} - -``` - -The PolicyReference is part of the execution trust chain. - -\--- - -\# PolicyRegistry - -The PolicyRegistry manages policy metadata. - -Responsibilities: - -\* register policies - -\* list policies - -\* resolve metadata - -The PolicyRegistry SHALL NOT: - -\* load policy files - -\* execute policies - -\* evaluate business logic - -\--- - -\# PolicyRouter - -The PolicyRouter loads exactly one policy artifact. - -Responsibilities: - -\* load the referenced artifact - -\* validate policy identifier - -\* validate business version - -\* validate schema version - -The PolicyRouter SHALL NOT: - -\* scan policy directories - -\* discover applicable policies - -\* evaluate rules - -\* select between multiple policies - -\* apply fallback policy selection - -If the referenced policy cannot be loaded or validated, execution SHALL fail. - -\--- - -\# PolicyAdapter - -The PolicyAdapter converts runtime data into PolicySignals. - -```text - -RuntimeTransaction - - │ - - ▼ - -PolicySignals - -``` - -The adapter SHALL remain domain independent. - -It SHALL NOT contain payment-specific, healthcare-specific, HR-specific, or any other business-specific mappings. - -\--- - -\# PolicySignals - -Policy signals are generic runtime inputs. - -```ts - -interface PolicySignals { - - \[key: string]: JsonValue; - -} - -``` - -A policy may evaluate any number of signals. - -The runtime imposes no domain-specific restrictions. - -\--- - -\# PolicyEngine - -The PolicyEngine evaluates exactly one loaded policy. - -Responsibilities: - -\* deterministic rule evaluation - -\* decision generation - -\* evaluation trace generation - -The PolicyEngine SHALL NOT: - -\* load policy artifacts - -\* select policies - -\* modify execution state - -\--- - -\# Decision - -A Decision is derived exclusively from: - -\* the referenced policy artifact - -\* runtime signals - -\* deterministic evaluation - -No external runtime state may influence the decision. - -\--- - -\# Determinism - -Given identical: - -\* BusinessTransaction - -\* PolicyReference - -\* Policy artifact - -\* Runtime signals - -the runtime SHALL always produce the same Decision. - -This invariant enables: - -\* replay - -\* verification - -\* cryptographic evidence - -\* reproducibility - -\--- - -\# Separation of Responsibilities - -| Component | Responsibility | - -| ------------------- | --------------------------------- | - -| BusinessTransaction | References policy | - -| PolicyReference | Identifies policy artifact | - -| PolicyRegistry | Maintains metadata | - -| PolicyRouter | Loads exact artifact | - -| PolicyAdapter | Produces PolicySignals | - -| PolicyEngine | Evaluates policy | - -| Decision | Captures deterministic outcome | - -| TrustRecord | Records execution evidence | - -| Receipt | Produces cryptographic proof | - -| Verification | Independently validates execution | - -Each component owns exactly one responsibility. - -\--- - -\# Architectural Invariants - -The runtime SHALL satisfy the following invariants: - -1\. BusinessTransaction SHALL contain exactly one PolicyReference. - -2\. PolicyReference SHALL contain name, version, and schemaVersion. - -3\. PolicyRegistry SHALL manage metadata only. - -4\. PolicyRouter SHALL load exactly one policy artifact. - -5\. PolicyRouter SHALL validate policyId, policyVersion, and schemaVersion. - -6\. PolicyEngine SHALL evaluate exactly one loaded policy. - -7\. Runtime SHALL NOT scan policy directories to determine applicability. - -8\. Runtime SHALL NOT infer, guess, or select policies. - -9\. Policy artifacts SHALL be immutable after publication. - -10\. Identical inputs SHALL always produce identical decisions. - -\--- - -\# Failure Conditions - -Execution SHALL fail if: - -\* PolicyReference is missing. - -\* The policy artifact cannot be found. - -\* policyId does not match. - -\* policyVersion does not match. - -\* schemaVersion does not match. - -\* The policy artifact is malformed. - -\* Runtime signals do not satisfy policy requirements. - -The runtime SHALL fail explicitly rather than attempting recovery or fallback. - -\--- - -\# Relationship to Other RFCs - -\* RFC-0007 — Canonical Trust Chain Domain Model - -\* RFC-0008 — Generic Policy Runtime Architecture - -\* RFC-0009 — Policy Versioning and Resolution - -\* RFC-0010 — Policy Schema Evolution - -\* RFC-0011 — Deterministic Policy Execution Architecture - -\--- - -\# Status - -This RFC defines the canonical execution architecture for the Parmana Runtime. - -It establishes the deterministic execution contract for all future implementations and ensures that policy execution remains reproducible, independently verifiable, domain independent, and free from runtime policy-selection logic. diff --git a/docs/rfcs/RFC-0012-Phase-1-Architecture-Completion.md b/docs/rfcs/RFC-0012-Phase-1-Architecture-Completion.md deleted file mode 100644 index d19ac848..00000000 --- a/docs/rfcs/RFC-0012-Phase-1-Architecture-Completion.md +++ /dev/null @@ -1,455 +0,0 @@ -\# RFC-0012: Phase 1 Architecture Completion - -\*\*Status:\*\* Accepted - -\## Purpose - -This RFC records the successful completion of Phase 1 of the Parmana architecture. - -Phase 1 establishes the canonical execution trust foundation upon which all future capabilities will be built. - -The objective of Phase 1 was to transform the runtime from a payment-oriented implementation into a deterministic, policy-driven, domain-independent execution platform. - -\--- - -\# Phase 1 Objectives - -Phase 1 established the following architectural principles: - -\* deterministic execution - -\* policy-driven execution - -\* domain independence - -\* immutable policy artifacts - -\* execution trust - -\* replayability - -\* independent verification - -\--- - -\# Locked Architecture - -\## PolicyReference - -The canonical PolicyReference is: - -```ts - -interface PolicyReference { - - readonly name: string; - - readonly version: string; - - readonly schemaVersion: string; - -} - -``` - -The PolicyReference uniquely identifies the policy artifact. - -It forms part of the execution trust chain. - -\--- - -\## PolicyRouter - -The PolicyRouter is responsible for loading exactly one policy artifact. - -Responsibilities: - -\* load referenced policy - -\* validate policy identity - -\* validate policy version - -\* validate schema version - -The PolicyRouter SHALL NOT: - -\* scan policy directories - -\* discover policies - -\* infer applicability - -\* evaluate business rules - -\* choose between policies - -\--- - -\## Policy - -The canonical policy artifact contains: - -```ts - -interface Policy { - - policyId: string; - - policyVersion: string; - - schemaVersion: string; - - signalsSchema: Record; - - rules: PolicyRule\[]; - -} - -``` - -Policy artifacts are immutable after publication. - -\--- - -\## PolicyEngine - -The PolicyEngine is generic. - -Responsibilities: - -\* deterministic evaluation - -\* rule execution - -\* decision generation - -The PolicyEngine contains no business-specific logic. - -\--- - -\## PolicyAdapter - -The PolicyAdapter converts runtime data into policy signals. - -```text - -RuntimeTransaction - - │ - - ▼ - -PolicySignals - -``` - -The adapter is domain independent. - -It performs no business-specific transformation. - -\--- - -\## RuntimeTransaction - -The canonical RuntimeTransaction is: - -```ts - -interface RuntimeTransaction { - - readonly signals: Record; - -} - -``` - -The runtime no longer contains fields such as: - -\* amount - -\* currency - -\* recipient - -Business-specific information is represented entirely as runtime signals. - -\--- - -\# Domain Independence - -The runtime supports any business domain. - -Examples include: - -\## Payment - -```json - -{ - - "signals": { - - "amount": 5000, - - "currency": "USD" - - } - -} - -``` - -\## Lending - -```json - -{ - - "signals": { - - "creditScore": 760, - - "income": 90000 - - } - -} - -``` - -\## Healthcare - -```json - -{ - - "signals": { - - "patientAge": 67, - - "diagnosis": "Diabetes" - - } - -} - -``` - -\## Cybersecurity - -```json - -{ - - "signals": { - - "riskScore": 82, - - "deviceTrusted": true - - } - -} - -``` - -\## Manufacturing - -```json - -{ - - "signals": { - - "temperature": 72, - - "pressure": 125 - - } - -} - -``` - -The runtime requires no code changes to support additional domains. - -\--- - -\# Architectural Achievements - -Phase 1 establishes: - -\* Generic policy evaluation - -\* Unlimited runtime signals - -\* Policy schema awareness - -\* Policy version awareness - -\* Deterministic execution - -\* Replayable execution - -\* Independently verifiable execution - -\* Clear separation of responsibilities - -\--- - -\# Remaining Phase 1 Milestone - -One architectural refinement remains. - -The runtime currently contains residual policy-selection behavior. - -The final step is to eliminate all policy-selection logic. - -The runtime SHALL execute only the policy explicitly referenced by the BusinessTransaction. - -\--- - -\# Target Execution Flow - -```text - -Authority - - │ - - ▼ - -Authorization - - │ - - ▼ - -Intent - - │ - - ▼ - -BusinessTransaction - - │ - - ▼ - -PolicyReference - -(name, version, schemaVersion) - - │ - - ▼ - -PolicyRegistry - -(metadata only) - - │ - - ▼ - -PolicyRouter - -(load exact artifact) - - │ - - ▼ - -PolicyEngine - - │ - - ▼ - -Decision - - │ - - ▼ - -TrustRecord - - │ - - ▼ - -Receipt - - │ - - ▼ - -Independent Verification - -``` - -Once the PolicyRouter loads only the explicitly referenced policy, the runtime becomes a pure execution engine. - -\--- - -\# Phase 1 Completion Criteria - -Phase 1 is considered complete when all of the following conditions are satisfied: - -\* BusinessTransaction references exactly one PolicyReference. - -\* PolicyReference contains `name`, `version`, and `schemaVersion`. - -\* PolicyRegistry manages metadata only. - -\* PolicyRouter loads exactly one policy artifact. - -\* PolicyRouter performs no policy discovery. - -\* PolicyEngine evaluates exactly one loaded policy. - -\* Runtime contains no policy-selection logic. - -\* Policy artifacts are immutable. - -\* Execution is deterministic. - -\* Replay reproduces the original decision. - -\* Verification independently validates the execution. - -\--- - -\# Foundation for Future Phases - -Phase 1 provides the foundation for: - -\* richer policy language features - -\* additional condition operators - -\* policy schema evolution - -\* advanced policy tooling - -\* policy authoring environments - -\* policy registries - -\* distributed policy repositories - -\* execution authorization - -\* cryptographic policy attestations - -None of these future capabilities require changes to the core execution architecture established in Phase 1. - -\--- - -\# Status - -This RFC records the completion of the Phase 1 architectural foundation for Parmana. - -The runtime is now a generic, deterministic, policy-driven execution platform capable of supporting any business domain while preserving execution trust, replayability, and independent verification. diff --git a/docs/rfcs/RFC-0013-Policy-Execution-Pipeline.md b/docs/rfcs/RFC-0013-Policy-Execution-Pipeline.md deleted file mode 100644 index a4bf4834..00000000 --- a/docs/rfcs/RFC-0013-Policy-Execution-Pipeline.md +++ /dev/null @@ -1,377 +0,0 @@ -\# RFC-0013: Policy Execution Pipeline - -\*\*Status:\*\* Accepted - -\## Purpose - -This RFC defines the canonical Policy Execution Pipeline for Parmana. - -It specifies how a BusinessTransaction progresses from a policy reference to a deterministic Decision while preserving execution trust. - -This pipeline separates policy loading, validation, signal preparation, signal validation, and policy evaluation into independent responsibilities. - -\--- - -\# Objectives - -The Policy Execution Pipeline SHALL: - -\* execute exactly one policy - -\* remain deterministic - -\* remain domain independent - -\* validate policy artifacts before execution - -\* validate runtime signals before evaluation - -\* produce a deterministic Decision - -\--- - -\# Canonical Pipeline - -```text - -BusinessTransaction - - │ - - ▼ - -PolicyReference - -(name, version, schemaVersion) - - │ - - ▼ - -PolicyRegistry - -(metadata only) - - │ - - ▼ - -PolicyRouter - -(load policy artifact) - - │ - - ▼ - -PolicyValidator - -(validate policy artifact) - - │ - - ▼ - -PolicyAdapter - -(RuntimeTransaction → PolicySignals) - - │ - - ▼ - -SignalValidator - -(validate PolicySignals) - - │ - - ▼ - -PolicyEngine - -(evaluate policy) - - │ - - ▼ - -Decision - -``` - -Each component owns exactly one responsibility. - -\--- - -\# Component Responsibilities - -\## BusinessTransaction - -Provides: - -\* execution request - -\* PolicyReference - -\* runtime data - -The BusinessTransaction SHALL NOT contain policy logic. - -\--- - -\## PolicyReference - -Identifies the exact policy artifact. - -```ts - -interface PolicyReference { - - readonly name: string; - - readonly version: string; - - readonly schemaVersion: string; - -} - -``` - -\--- - -\## PolicyRegistry - -Responsibilities: - -\* register policy metadata - -\* resolve metadata - -\* list available policies - -The registry SHALL NOT execute policies. - -\--- - -\## PolicyRouter - -Responsibilities: - -\* locate policy artifact - -\* load policy.json - -The router SHALL NOT: - -\* evaluate policies - -\* validate policies - -\* discover policies - -\* apply business logic - -\--- - -\## PolicyValidator - -Responsibilities: - -Validate: - -\* policyId - -\* policyVersion - -\* schemaVersion - -\* signalsSchema - -\* rule structure - -Execution SHALL fail if validation fails. - -\--- - -\## PolicyAdapter - -Responsibilities: - -Convert RuntimeTransaction into PolicySignals. - -The adapter SHALL remain business-domain independent. - -\--- - -\## SignalValidator - -Responsibilities: - -Validate runtime signals against the policy's declared signalsSchema. - -Validation includes: - -\* required signals - -\* signal types - -Execution SHALL fail on validation errors. - -\--- - -\## PolicyEngine - -Responsibilities: - -\* evaluate one policy - -\* produce one Decision - -\* record evaluation trace - -The PolicyEngine SHALL NOT: - -\* load policies - -\* validate policy artifacts - -\* validate runtime signals - -\--- - -\# Decision - -The Decision is the deterministic result of policy evaluation. - -A Decision is derived solely from: - -\* PolicyReference - -\* policy artifact - -\* validated PolicySignals - -No external runtime state may influence the result. - -\--- - -\# Determinism - -Given identical: - -\* BusinessTransaction - -\* PolicyReference - -\* policy artifact - -\* PolicySignals - -the Policy Execution Pipeline SHALL always produce the same Decision. - -\--- - -\# Failure Conditions - -Execution SHALL terminate if: - -\* PolicyReference is invalid. - -\* Policy artifact cannot be loaded. - -\* Policy validation fails. - -\* Signal validation fails. - -\* Policy evaluation fails. - -No fallback execution is permitted. - -\--- - -\# Architectural Invariants - -The pipeline SHALL satisfy the following invariants: - -1\. Exactly one policy is executed. - -2\. Policy artifacts are validated before execution. - -3\. Runtime signals are validated before evaluation. - -4\. Policy evaluation is deterministic. - -5\. Every component has a single responsibility. - -6\. Business logic resides exclusively in policy artifacts. - -7\. The runtime remains domain independent. - -\--- - -\# Relationship to the Trust Chain - -This RFC covers the transition from: - -```text - -BusinessTransaction - - │ - - ▼ - -Decision - -``` - -The Decision becomes the canonical input for the remaining execution trust chain: - -```text - -Decision - - │ - - ▼ - -TrustRecord - - │ - - ▼ - -Receipt - - │ - - ▼ - -Independent Verification - -``` - -\--- - -\# Phase 1 Completion - -The Policy Execution Pipeline is considered complete when: - -\* Policy loading is deterministic. - -\* Policy validation is complete. - -\* Runtime signal validation is complete. - -\* Policy evaluation is deterministic. - -\* A canonical Decision is produced. - -This completes the policy execution stage of the Parmana Execution Trust Architecture. - -\--- - -\# Status - -This RFC defines the canonical Policy Execution Pipeline for Parmana Phase 1 and serves as the implementation contract for all future runtime implementations. diff --git a/docs/rfcs/RFC-0014-Phase-1-Execution-Architecture.md b/docs/rfcs/RFC-0014-Phase-1-Execution-Architecture.md deleted file mode 100644 index a150b3a3..00000000 --- a/docs/rfcs/RFC-0014-Phase-1-Execution-Architecture.md +++ /dev/null @@ -1,513 +0,0 @@ -\# RFC-0014: Phase 1 Execution Architecture - -\*\*Status:\*\* Accepted - -\## Purpose - -This RFC records the completion of the Phase 1 execution architecture for Parmana. - -Phase 1 establishes the canonical execution trust model, deterministic policy execution pipeline, and domain model that all future implementations SHALL follow. - -This RFC serves as the architectural baseline for implementation. - -\--- - -\# Phase 1 Goals - -Phase 1 establishes: - -\* Deterministic execution - -\* Domain-independent policy evaluation - -\* Immutable execution artifacts - -\* Replayability - -\* Independent verification - -\* Cryptographically verifiable execution evidence - -\--- - -\# Canonical Trust Chain - -The canonical trust chain is: - -```text - -Authority - - │ - - ▼ - -Authorization - - │ - - ▼ - -Intent - - │ - - ▼ - -BusinessTransaction - - │ - - ▼ - -PolicyReference - -(name, version, schemaVersion) - - │ - - ▼ - -Policy - - │ - - ▼ - -Decision - - │ - - ▼ - -Execution - - │ - - ▼ - -ExecutionTrustRecord - - │ - - ▼ - -Receipt - - │ - - ▼ - -Verification - -``` - -Each artifact is immutable. - -Each artifact extends the trust chain. - -\--- - -\# Canonical Policy Execution Pipeline - -```text - -BusinessTransaction - - │ - - ▼ - -PolicyReference - - │ - - ▼ - -PolicyRegistry - -(metadata) - - │ - - ▼ - -PolicyRouter - -(load policy artifact) - - │ - - ▼ - -PolicyValidator - -(validate artifact) - - │ - - ▼ - -PolicyAdapter - -(RuntimeTransaction → PolicySignals) - - │ - - ▼ - -SignalValidator - -(validate runtime signals) - - │ - - ▼ - -PolicyEngine - -(evaluate) - - │ - - ▼ - -Decision - -``` - -The runtime executes exactly one policy. - -\--- - -\# Domain Model - -\## Authority - -Defines who is permitted to authorize actions. - -\--- - -\## Authorization - -Records approval granted by an authority. - -\--- - -\## Intent - -Represents the desired business outcome. - -\--- - -\## BusinessTransaction - -Represents an execution request. - -It contains exactly one: - -```text - -PolicyReference - -``` - -\--- - -\## PolicyReference - -```ts - -interface PolicyReference { - - readonly name: string; - - readonly version: string; - - readonly schemaVersion: string; - -} - -``` - -It uniquely identifies the policy artifact. - -\--- - -\## Policy - -Every policy declares: - -```text - -policyId - -policyVersion - -schemaVersion - -signalsSchema - -rules - -``` - -Policy artifacts are immutable. - -\--- - -\## Decision - -The deterministic result of evaluating a policy against validated runtime signals. - -A Decision binds together: - -\* Intent - -\* PolicyReference - -\* Runtime signals - -\* Outcome - -\* Reason - -\* Evaluation timestamp - -\--- - -\## Execution - -Records the lifecycle of processing a BusinessTransaction. - -Execution captures: - -\* lifecycle state - -\* execution mode - -\* timestamps - -\* execution evidence - -\* execution metadata - -Execution is immutable. - -\--- - -\## ExecutionTrustRecord - -The canonical aggregate. - -It contains: - -\* BusinessTransaction - -\* Executions - -\* Overrides - -\* Verifications - -\* Receipts - -\* TrustRecordHash - -It is the authoritative source for replay, audit, receipt generation, and verification. - -\--- - -\## Receipt - -Cryptographically signed proof of the ExecutionTrustRecord state. - -Receipts are immutable. - -\--- - -\## Verification - -Records the independent verification of an ExecutionTrustRecord. - -Each verification is immutable. - -\--- - -\# Runtime Components - -\## PolicyRegistry - -Maintains policy metadata. - -\--- - -\## PolicyRouter - -Loads the exact policy artifact identified by the PolicyReference. - -The router SHALL NOT: - -\* discover policies - -\* infer applicability - -\* evaluate business logic - -\--- - -\## PolicyValidator - -Validates: - -\* policyId - -\* policyVersion - -\* schemaVersion - -\* signalsSchema - -\* rule structure - -\--- - -\## PolicyAdapter - -Converts RuntimeTransaction into generic PolicySignals. - -The adapter contains no business-specific logic. - -\--- - -\## SignalValidator - -Validates runtime signals against the policy's declared signalsSchema. - -Execution SHALL fail if validation fails. - -\--- - -\## PolicyEngine - -Evaluates exactly one loaded policy. - -The engine is deterministic and domain independent. - -\--- - -\# Architectural Invariants - -The runtime SHALL satisfy the following invariants: - -1\. Exactly one policy is executed. - -2\. BusinessTransaction contains exactly one PolicyReference. - -3\. Policy artifacts are immutable. - -4\. Runtime never discovers policies. - -5\. Runtime never guesses policies. - -6\. Policy evaluation is deterministic. - -7\. Runtime signals are validated before evaluation. - -8\. Business logic resides exclusively in policy artifacts. - -9\. Runtime remains domain independent. - -10\. Identical inputs SHALL produce identical decisions. - -\--- - -\# Phase 1 Deliverables - -The following architectural components are complete: - -\* Authority - -\* Authorization - -\* Intent - -\* BusinessTransaction - -\* PolicyReference - -\* Policy - -\* PolicyRegistry - -\* PolicyRouter - -\* PolicyValidator - -\* PolicyAdapter - -\* SignalValidator - -\* PolicyEngine - -\* Decision - -\* Execution - -\* ExecutionTrustRecord - -\* Receipt - -\* Verification - -\--- - -\# Remaining Implementation Work - -The remaining work focuses on implementation rather than architectural redesign. - -Priority order: - -1\. DecisionService - -2\. ExecutionTrustRecordService - -3\. ReceiptService - -4\. ReplayService completion - -5\. VerificationService completion - -6\. End-to-end integration tests - -7\. Performance optimization - -These components SHALL implement the architecture defined in this RFC without altering the canonical domain model. - -\--- - -\# Relationship to Previous RFCs - -\* RFC-0007 — Canonical Trust Chain Domain Model - -\* RFC-0008 — Generic Policy Runtime Architecture - -\* RFC-0009 — Policy Versioning and Resolution - -\* RFC-0010 — Policy Schema Evolution - -\* RFC-0011 — Deterministic Execution Contract - -\* RFC-0012 — Phase 1 Architecture Completion - -\* RFC-0013 — Policy Execution Pipeline - -\* RFC-0014 — Phase 1 Execution Architecture - -\--- - -\# Status - -This RFC locks the Phase 1 execution architecture for Parmana. - -Future work SHALL extend this architecture without changing the core execution trust model, deterministic policy pipeline, or canonical domain model established in Phase 1. diff --git a/docs/rfcs/RFC-0015-Decision-Service.md b/docs/rfcs/RFC-0015-Decision-Service.md deleted file mode 100644 index e69de29b..00000000 diff --git a/docs/rfcs/RFC-0016-Runtime-Service-Architecture.md b/docs/rfcs/RFC-0016-Runtime-Service-Architecture.md deleted file mode 100644 index cc9b711b..00000000 --- a/docs/rfcs/RFC-0016-Runtime-Service-Architecture.md +++ /dev/null @@ -1,421 +0,0 @@ -\# RFC-0016: Runtime Service Architecture - -\*\*Status:\*\* Accepted - -\## Purpose - -This RFC defines the canonical runtime service architecture for Parmana. - -Phase 1 established the canonical domain model and deterministic policy execution pipeline. - -This RFC defines how runtime services collaborate to execute that architecture while maintaining strict separation of responsibilities. - -\--- - -\# Objectives - -The runtime service architecture SHALL: - -\* separate orchestration from business logic - -\* isolate policy evaluation from decision construction - -\* isolate execution from persistence - -\* preserve deterministic execution - -\* produce immutable execution artifacts - -\* maintain a single responsibility for every runtime service - -\--- - -\# Runtime Service Pipeline - -```text - -BusinessTransaction - - │ - - ▼ - -ExecutionService - -(orchestrator) - - │ - - ├────────► PolicyRouter - - │ - - ├────────► PolicyValidator - - │ - - ├────────► PolicyAdapter - - │ - - ├────────► SignalValidator - - │ - - ├────────► PolicyEngine - - │ - - ├────────► DecisionService - - │ - - ├────────► ExecutionTrustRecordService - - │ - - ├────────► ReceiptService - - │ - - └────────► VerificationService - -``` - -The ExecutionService coordinates the workflow. - -It does not contain business logic. - -\--- - -\# Runtime Services - -\## BusinessTransactionService - -Responsibilities: - -\* construct BusinessTransaction - -\* validate transaction structure - -\* initialize execution request - -\--- - -\## ExecutionService - -Responsibilities: - -\* coordinate execution workflow - -\* invoke runtime services - -\* manage execution lifecycle - -The ExecutionService SHALL NOT: - -\* evaluate policy rules - -\* construct Decisions - -\* generate Receipts - -\* verify execution - -\--- - -\## PolicyRouter - -Responsibilities: - -\* locate policy artifact - -\* load one policy - -\--- - -\## PolicyValidator - -Responsibilities: - -\* validate policy artifact - -\* validate policy identity - -\* validate schema version - -\--- - -\## PolicyAdapter - -Responsibilities: - -Convert RuntimeTransaction into PolicySignals. - -\--- - -\## SignalValidator - -Responsibilities: - -Validate PolicySignals against the policy's declared signalsSchema. - -\--- - -\## PolicyEngine - -Responsibilities: - -Evaluate deterministic policy rules. - -The PolicyEngine SHALL NOT construct Decisions. - -\--- - -\## DecisionService - -Responsibilities: - -Construct immutable Decision artifacts. - -The DecisionService SHALL: - -\* generate decisionId - -\* bind PolicyReference - -\* bind PolicySignals - -\* record outcome - -\* record reason - -\* record timestamp - -\--- - -\## ExecutionTrustRecordService - -Responsibilities: - -Construct immutable ExecutionTrustRecord aggregates. - -The service SHALL: - -\* initialize execution history - -\* initialize receipt history - -\* initialize verification history - -\* initialize override history - -\* compute initial trustRecordHash - -\--- - -\## ReceiptService - -Responsibilities: - -Generate immutable cryptographic Receipts. - -\--- - -\## VerificationService - -Responsibilities: - -Perform independent verification of execution artifacts. - -\--- - -\# Service Responsibilities - -| Service | Responsibility | - -| --------------------------- | --------------------------------------------- | - -| BusinessTransactionService | Create BusinessTransaction | - -| ExecutionService | Orchestrate runtime execution | - -| PolicyRouter | Load policy artifact | - -| PolicyValidator | Validate policy artifact | - -| PolicyAdapter | Convert RuntimeTransaction into PolicySignals | - -| SignalValidator | Validate runtime signals | - -| PolicyEngine | Evaluate policy | - -| DecisionService | Build Decision | - -| ExecutionTrustRecordService | Build ExecutionTrustRecord | - -| ReceiptService | Generate Receipt | - -| VerificationService | Verify execution | - -Every service SHALL own exactly one responsibility. - -\--- - -\# Execution Flow - -```text - -Authority - - │ - - ▼ - -Authorization - - │ - - ▼ - -Intent - - │ - - ▼ - -BusinessTransaction - - │ - - ▼ - -PolicyReference - - │ - - ▼ - -Policy - - │ - - ▼ - -PolicyEngine - - │ - - ▼ - -DecisionService - - │ - - ▼ - -Decision - - │ - - ▼ - -ExecutionService - - │ - - ▼ - -Execution - - │ - - ▼ - -ExecutionTrustRecordService - - │ - - ▼ - -ExecutionTrustRecord - - │ - - ▼ - -ReceiptService - - │ - - ▼ - -Receipt - - │ - - ▼ - -VerificationService - - │ - - ▼ - -Verification - -``` - -\--- - -\# Architectural Invariants - -The runtime SHALL satisfy the following invariants: - -1\. Runtime services SHALL have a single responsibility. - -2\. Policy evaluation SHALL occur only in the PolicyEngine. - -3\. Decision construction SHALL occur only in the DecisionService. - -4\. Execution orchestration SHALL occur only in the ExecutionService. - -5\. ExecutionTrustRecord construction SHALL occur only in the ExecutionTrustRecordService. - -6\. Receipt generation SHALL occur only in the ReceiptService. - -7\. Verification SHALL occur only in the VerificationService. - -8\. Runtime SHALL remain deterministic. - -9\. Runtime SHALL remain domain independent. - -10\. Business logic SHALL reside exclusively in policy artifacts. - -\--- - -\# Remaining Phase 1 Implementation - -The remaining implementation work SHALL proceed in the following order: - -1\. Refactor ExecutionService into a pure orchestrator. - -2\. Refactor ReceiptService to consume ExecutionTrustRecord. - -3\. Refactor VerificationService to verify immutable execution artifacts. - -4\. Complete ReplayService integration. - -5\. Complete end-to-end integration tests. - -6\. Validate persistence, replay, cryptographic receipts, and verification. - -No additional architectural changes are required. - -\--- - -\# Status - -This RFC locks the runtime service architecture for Parmana Phase 1. - -Future enhancements SHALL extend this architecture without changing the canonical execution trust chain or the single-responsibility service model. diff --git a/docs/rfcs/RFC-0017-Execution-Orchestration.md b/docs/rfcs/RFC-0017-Execution-Orchestration.md deleted file mode 100644 index 7a0c1e65..00000000 --- a/docs/rfcs/RFC-0017-Execution-Orchestration.md +++ /dev/null @@ -1,387 +0,0 @@ -\# RFC-0017: Execution Orchestration - -\*\*Status:\*\* Accepted - -\## Purpose - -This RFC defines the canonical execution orchestration architecture for the Parmana Runtime. - -With the domain model, policy runtime, `DecisionService`, and `ExecutionTrustRecordService` complete, the remaining work is orchestration rather than architectural design. - -The ExecutionService becomes the coordinator of the execution pipeline while delegating all specialized work to dedicated runtime services. - -\--- - -\# Phase 1 Status - -The following architectural components are complete. - -\## Domain Model - -\* Authority - -\* Authorization - -\* Intent - -\* BusinessTransaction - -\* PolicyReference - -\* Policy - -\* Decision - -\* Execution - -\* ExecutionTrustRecord - -\* Receipt - -\* Verification - -\--- - -\## Policy Runtime - -\* PolicyRegistry - -\* PolicyRouter - -\* PolicyValidator - -\* PolicyAdapter - -\* SignalValidator - -\* PolicyEngine - -\--- - -\## Runtime Services - -Implemented: - -\* BusinessTransactionService - -\* DecisionService - -\* ExecutionTrustRecordService - -\* OverrideService - -\* ReceiptService - -\* VerificationService - -ExecutionService remains to be aligned with the completed architecture. - -\--- - -\# Canonical Runtime Services - -```text - -packages/runtime/src/services/ - -│ - -├── business-transaction-service.ts - -├── decision-service.ts - -├── execution-service.ts - -├── execution-trust-record-service.ts - -├── override-service.ts - -├── receipt-service.ts - -└── verification-service.ts - -``` - -Each runtime service owns exactly one responsibility. - -\--- - -\# Canonical Execution Pipeline - -```text - -Authority - - │ - - ▼ - -Authorization - - │ - - ▼ - -Intent - - │ - - ▼ - -BusinessTransaction - - │ - - ▼ - -PolicyReference - - │ - - ▼ - -PolicyRegistry - - │ - - ▼ - -PolicyRouter - - │ - - ▼ - -PolicyValidator - - │ - - ▼ - -PolicyAdapter - - │ - - ▼ - -SignalValidator - - │ - - ▼ - -PolicyEngine - - │ - - ▼ - -DecisionService - - │ - - ▼ - -Decision - - │ - - ▼ - -ExecutionService - - │ - - ▼ - -Execution - - │ - - ▼ - -ExecutionTrustRecordService - - │ - - ▼ - -ExecutionTrustRecord - - │ - - ▼ - -ReceiptService - - │ - - ▼ - -Receipt - - │ - - ▼ - -VerificationService - - │ - - ▼ - -Verification - -``` - -Execution remains deterministic throughout the pipeline. - -\--- - -\# ExecutionService - -The ExecutionService is the runtime orchestrator. - -It SHALL coordinate execution without implementing business logic. - -ExecutionService SHALL invoke: - -\* PolicyRouter - -\* PolicyValidator - -\* PolicyAdapter - -\* SignalValidator - -\* PolicyEngine - -\* DecisionService - -\* ExecutionTrustRecordService - -\* ReceiptService - -\* VerificationService - -ExecutionService SHALL NOT: - -\* evaluate policy rules - -\* construct Decisions - -\* construct ExecutionTrustRecords - -\* generate Receipts - -\* perform Verification - -Those responsibilities belong to dedicated services. - -\--- - -\# Runtime Service Responsibilities - -| Service | Responsibility | - -| --------------------------- | --------------------------------------------- | - -| BusinessTransactionService | Construct BusinessTransaction | - -| ExecutionService | Orchestrate execution workflow | - -| PolicyRouter | Load policy artifact | - -| PolicyValidator | Validate policy artifact | - -| PolicyAdapter | Convert RuntimeTransaction into PolicySignals | - -| SignalValidator | Validate runtime signals | - -| PolicyEngine | Evaluate deterministic rules | - -| DecisionService | Construct immutable Decision | - -| ExecutionTrustRecordService | Construct immutable ExecutionTrustRecord | - -| ReceiptService | Generate cryptographic Receipt | - -| VerificationService | Verify execution artifacts | - -Each service SHALL own one responsibility only. - -\--- - -\# Architectural Invariants - -The runtime SHALL satisfy the following invariants: - -1\. ExecutionService SHALL act only as an orchestrator. - -2\. Policy evaluation SHALL occur only in the PolicyEngine. - -3\. Decision construction SHALL occur only in the DecisionService. - -4\. ExecutionTrustRecord construction SHALL occur only in the ExecutionTrustRecordService. - -5\. Receipt generation SHALL occur only in the ReceiptService. - -6\. Verification SHALL occur only in the VerificationService. - -7\. Runtime SHALL remain deterministic. - -8\. Runtime SHALL remain domain independent. - -9\. Policy artifacts SHALL remain immutable. - -10\. Identical validated inputs SHALL produce identical Decisions. - -\--- - -\# Remaining Phase 1 Work - -The remaining implementation tasks are: - -1\. Refactor ExecutionService into a pure orchestrator. - -2\. Align ReceiptService with the ExecutionTrustRecord model. - -3\. Align VerificationService with the immutable trust chain. - -4\. Complete replay integration. - -5\. Complete persistence integration. - -6\. Complete end-to-end execution tests. - -7\. Validate replay, receipts, verification, and cryptographic integrity. - -No further changes to the core domain model or policy execution architecture are required. - -\--- - -\# Phase 1 Milestone - -Phase 1 has transitioned from architecture design to implementation. - -The canonical: - -\* domain model, - -\* policy runtime, - -\* execution pipeline, and - -\* runtime service architecture - -are now established. - -Future work focuses on integrating these components into a complete execution trust pipeline without changing the architectural foundations defined by the preceding RFCs. - -\--- - -\# Status - -This RFC locks the canonical execution orchestration architecture for Parmana Phase 1. - -Future implementations SHALL preserve the orchestration model and the single-responsibility design of runtime services. diff --git a/docs/rfcs/RFC-0018-Execution-Service-Orchestration.md b/docs/rfcs/RFC-0018-Execution-Service-Orchestration.md deleted file mode 100644 index 9d04c13e..00000000 --- a/docs/rfcs/RFC-0018-Execution-Service-Orchestration.md +++ /dev/null @@ -1,383 +0,0 @@ -\# RFC-0018: Execution Service Orchestration - -\*\*Status:\*\* Accepted - -\## Purpose - -This RFC defines the canonical responsibilities of the `ExecutionService` within the Parmana Runtime. - -With the domain model, policy runtime, `DecisionService`, and `ExecutionTrustRecordService` established, the `ExecutionService` becomes the orchestration layer that coordinates execution without containing business logic. - -\--- - -\# Objectives - -The `ExecutionService` SHALL: - -\* orchestrate the end-to-end execution workflow - -\* invoke runtime services in the correct order - -\* coordinate artifact creation - -\* manage execution lifecycle - -\* terminate execution on unrecoverable validation failures - -The `ExecutionService` SHALL NOT: - -\* evaluate policy rules - -\* construct Decisions - -\* construct ExecutionTrustRecords - -\* generate Receipts - -\* perform Verification - -\* contain business-specific logic - -\--- - -\# Current Runtime Services - -```text - -packages/runtime/src/services/ - -│ - -├── business-transaction-service.ts - -├── decision-service.ts - -├── execution-service.ts - -├── execution-trust-record-service.ts - -├── override-service.ts - -├── receipt-service.ts - -└── verification-service.ts - -``` - -\--- - -\# Canonical Execution Pipeline - -```text - -Authority - - │ - - ▼ - -Authorization - - │ - - ▼ - -Intent - - │ - - ▼ - -BusinessTransaction - - │ - - ▼ - -PolicyReference - - │ - - ▼ - -PolicyRegistry - - │ - - ▼ - -PolicyRouter - -(load) - - │ - - ▼ - -PolicyValidator - -(validate) - - │ - - ▼ - -PolicyAdapter - -(RuntimeTransaction → PolicySignals) - - │ - - ▼ - -SignalValidator - -(validate) - - │ - - ▼ - -PolicyEngine - -(evaluate) - - │ - - ▼ - -DecisionService - -(build Decision) - - │ - - ▼ - -Decision - - │ - - ▼ - -ExecutionService - -(orchestrate) - - │ - - ▼ - -Execution - - │ - - ▼ - -ExecutionTrustRecordService - -(build ExecutionTrustRecord) - - │ - - ▼ - -ExecutionTrustRecord - - │ - - ▼ - -ReceiptService - -(generate Receipt) - - │ - - ▼ - -Receipt - - │ - - ▼ - -VerificationService - -(verify) - - │ - - ▼ - -Verification - -``` - -\--- - -\# ExecutionService Responsibilities - -The `ExecutionService` SHALL orchestrate the following components: - -```text - -ExecutionService - - │ - - ├── PolicyRouter - - ├── PolicyValidator - - ├── PolicyAdapter - - ├── SignalValidator - - ├── PolicyEngine - - ├── DecisionService - - ├── ExecutionTrustRecordService - - ├── ReceiptService - - └── VerificationService - -``` - -The `ExecutionService` SHALL coordinate these components but SHALL NOT implement their internal responsibilities. - -\--- - -\# Runtime Service Responsibilities - -| Service | Responsibility | - -| --------------------------- | --------------------------------------------- | - -| BusinessTransactionService | Construct BusinessTransaction | - -| ExecutionService | Orchestrate execution workflow | - -| PolicyRouter | Load policy artifact | - -| PolicyValidator | Validate policy artifact | - -| PolicyAdapter | Convert RuntimeTransaction into PolicySignals | - -| SignalValidator | Validate runtime signals | - -| PolicyEngine | Evaluate deterministic policy rules | - -| DecisionService | Construct immutable Decision | - -| ExecutionTrustRecordService | Construct immutable ExecutionTrustRecord | - -| ReceiptService | Generate cryptographic Receipt | - -| VerificationService | Verify execution artifacts | - -Every runtime service SHALL own exactly one responsibility. - -\--- - -\# Execution Flow - -The `ExecutionService` SHALL execute the following sequence: - -1\. Receive a validated `BusinessTransaction`. - -2\. Resolve the referenced policy. - -3\. Load the policy artifact. - -4\. Validate the policy artifact. - -5\. Convert the runtime transaction into `PolicySignals`. - -6\. Validate the runtime signals. - -7\. Evaluate the policy. - -8\. Construct the immutable `Decision`. - -9\. Create the `Execution`. - -10\. Construct the immutable `ExecutionTrustRecord`. - -11\. Generate the cryptographic `Receipt`. - -12\. Perform verification. - -13\. Return the completed execution result. - -Execution SHALL terminate immediately if any mandatory validation step fails. - -\--- - -\# Architectural Invariants - -The runtime SHALL satisfy the following invariants: - -1\. `ExecutionService` SHALL act only as an orchestrator. - -2\. Policy evaluation SHALL occur only in the `PolicyEngine`. - -3\. Decision construction SHALL occur only in the `DecisionService`. - -4\. ExecutionTrustRecord construction SHALL occur only in the `ExecutionTrustRecordService`. - -5\. Receipt generation SHALL occur only in the `ReceiptService`. - -6\. Verification SHALL occur only in the `VerificationService`. - -7\. Runtime SHALL remain deterministic. - -8\. Runtime SHALL remain domain independent. - -9\. Business logic SHALL reside exclusively in policy artifacts. - -10\. Identical validated inputs SHALL produce identical Decisions. - -\--- - -\# Remaining Phase 1 Implementation - -The remaining implementation work SHALL proceed in the following order: - -1\. Refactor `ExecutionService` into a pure orchestrator. - -2\. Refactor `ReceiptService` to operate on `ExecutionTrustRecord`. - -3\. Refactor `VerificationService` to verify immutable execution artifacts. - -4\. Complete replay integration. - -5\. Complete persistence integration. - -6\. Complete end-to-end integration tests. - -7\. Validate persistence, replay, receipt generation, cryptographic integrity, and verification. - -\--- - -\# Phase 1 Milestone - -With the implementation of: - -\* DecisionService - -\* ExecutionTrustRecordService - -the project has transitioned from architectural definition to execution pipeline integration. - -The remaining work focuses on wiring the existing components into a complete, deterministic execution workflow without changing the established architecture. - -\--- - -\# Status - -This RFC locks the orchestration responsibilities of the `ExecutionService` for Parmana Phase 1. - -Future implementations SHALL preserve the orchestration model and the single-responsibility design established by the runtime service architecture. diff --git a/docs/rfcs/RFC-0019-Business-Transaction-Domain-Model.md b/docs/rfcs/RFC-0019-Business-Transaction-Domain-Model.md deleted file mode 100644 index a5c0ec6a..00000000 --- a/docs/rfcs/RFC-0019-Business-Transaction-Domain-Model.md +++ /dev/null @@ -1,387 +0,0 @@ -\# RFC-0019: Business Transaction Domain Model - -\*\*Status:\*\* Accepted - -\## Purpose - -This RFC defines the canonical `BusinessTransaction` domain model within the Parmana Execution Trust Architecture. - -The `BusinessTransaction` represents the immutable business context submitted for execution. It captures everything required to deterministically evaluate a policy, but it does not contain the results of that evaluation. - -\--- - -\# Architectural Position - -The `BusinessTransaction` is an input artifact. - -It exists before policy evaluation and therefore SHALL NOT contain execution artifacts produced later in the trust chain. - -The canonical execution flow is: - -```text - -Authority - - │ - - ▼ - -Authorization - - │ - - ▼ - -Intent - - │ - - ▼ - -BusinessTransaction - - │ - - ▼ - -PolicyReference - - │ - - ▼ - -PolicyEngine - - │ - - ▼ - -Decision - - │ - - ▼ - -Execution - - │ - - ▼ - -ExecutionTrustRecord - - │ - - ▼ - -Receipt - - │ - - ▼ - -Verification - -``` - -\--- - -\# Responsibilities - -The `BusinessTransaction` SHALL: - -\* identify the business transaction - -\* capture the complete upstream trust chain - -\* specify the exact policy to execute - -\* carry the runtime signals required for evaluation - -\* remain immutable after creation - -The `BusinessTransaction` SHALL NOT: - -\* contain a `Decision` - -\* contain execution state - -\* contain receipt information - -\* contain verification results - -\* contain execution evidence - -\--- - -\# Canonical Structure - -The canonical `BusinessTransaction` SHALL contain: - -```text - -BusinessTransaction - -├── businessTransactionId - -├── metadata - -├── authority - -├── authorization - -├── intent - -├── policy : PolicyReference - -├── signals - -├── status - -└── createdAt - -``` - -\--- - -\# Policy Reference - -Every `BusinessTransaction` SHALL contain exactly one `PolicyReference`. - -The `PolicyReference` SHALL include: - -```text - -PolicyReference - -├── name - -├── version - -└── schemaVersion - -``` - -The `PolicyReference` forms part of the cryptographically verifiable execution trust chain. - -\--- - -\# Runtime Signals - -Runtime signals are opaque business facts supplied at execution time. - -Examples include: - -Payment - -```json - -{ - - "amount": 5000, - - "currency": "USD" - -} - -``` - -Loan - -```json - -{ - - "creditScore": 760, - - "income": 90000 - -} - -``` - -Healthcare - -```json - -{ - - "patientAge": 67, - - "diagnosis": "Diabetes" - -} - -``` - -Cybersecurity - -```json - -{ - - "riskScore": 82, - - "deviceTrusted": true - -} - -``` - -Manufacturing - -```json - -{ - - "temperature": 72, - - "pressure": 125 - -} - -``` - -Parmana assigns no business meaning to these values. - -Their interpretation belongs entirely to the referenced policy. - -\--- - -\# Decision Separation - -A `Decision` SHALL NOT be embedded within a `BusinessTransaction`. - -The `Decision` is produced only after deterministic policy evaluation. - -```text - -BusinessTransaction - - │ - - ▼ - -PolicyEngine - - │ - - ▼ - -Decision - -``` - -This preserves the causal relationship between inputs and outputs. - -\--- - -\# Architectural Invariants - -The `BusinessTransaction` SHALL satisfy the following invariants: - -1\. It SHALL be immutable. - -2\. It SHALL represent only execution inputs. - -3\. It SHALL contain exactly one `PolicyReference`. - -4\. It SHALL contain the runtime signals required for policy evaluation. - -5\. It SHALL NOT contain execution artifacts. - -6\. It SHALL NOT contain a `Decision`. - -7\. It SHALL remain domain independent. - -8\. Identical `BusinessTransaction` instances SHALL produce identical policy evaluation inputs. - -\--- - -\# Relationship to Other Domain Objects - -The trust chain is defined as: - -```text - -Authority - - │ - - ▼ - -Authorization - - │ - - ▼ - -Intent - - │ - - ▼ - -BusinessTransaction - - │ - - ▼ - -Decision - - │ - - ▼ - -Execution - - │ - - ▼ - -ExecutionTrustRecord - - │ - - ▼ - -Receipt - - │ - - ▼ - -Verification - -``` - -Each domain object represents a distinct stage in the execution lifecycle and owns only the data created at that stage. - -\--- - -\# Benefits - -Separating the `BusinessTransaction` from the `Decision` provides: - -\* clear separation of inputs and outputs - -\* deterministic policy evaluation - -\* domain independence - -\* replayability - -\* immutable execution artifacts - -\* independently verifiable execution - -\* a clean execution trust chain - -\--- - -\# Status - -This RFC locks the canonical `BusinessTransaction` domain model for Parmana. - -The `BusinessTransaction` SHALL remain an immutable execution input and SHALL NOT embed downstream execution artifacts such as `Decision`, `Execution`, `Receipt`, or `Verification`. diff --git a/docs/site/architecture/execution-pipeline.mdx b/docs/site/architecture/execution-pipeline.mdx index 8efe5f3e..c02b2f07 100644 --- a/docs/site/architecture/execution-pipeline.mdx +++ b/docs/site/architecture/execution-pipeline.mdx @@ -34,6 +34,74 @@ silently skip stages 7-8. See [The gateway](/concepts/the-gateway) for the mecha binding) is the one stage that can reject a transaction before stage 4 (policy evaluation) ever runs a single rule, see [Security](/security/overview) for why that ordering matters. +## One request, class by class + +The same pipeline, traced through the classes that run it for one `POST /execute`. + +1. **Route.** `packages/api/src/routes/execute.ts` calls `ExecutionTrustApplication.execute()` + (`packages/runtime/src/ExecutionTrustApplication.ts`). The application is built once at + startup by `RuntimeFactory.create()` and wired in `packages/api/src/application.ts`. +2. **Accept.** `BusinessTransactionService.accept()` stores the transaction as received, + before any policy work. +3. **Decide.** `Runtime.execute()` calls `RuntimeEngine.execute()` + (`packages/runtime/src/RuntimeEngine.ts`), which in order: loads the policy + (`PolicyRouter.load`), hashes its content, checks the policy's approval record when policy + governance is enforced (`matchedRuleId: "policy-execution-verification-violation"`), + checks the capability is paired with its bound policy (`CapabilityPolicyBinder`, + `"capability-policy-binding-violation"`), checks signals against the intent + (`SignalIntentBinder`, `"signal-intent-binding-violation"`), and only then evaluates rules + (`PolicyEngine.evaluate`). Each earlier check that fails becomes an ordinary `REJECT` with + no rule evaluated. +4. **Verify signals against real state.** Only for a provisional `APPROVE`, the configured + `SignalStateVerifier` runs. In production it is a `CompositeSignalStateVerifier` of the + HubSpot verifier and the `ApprovalSignalVerifier` (the policy's `approvalSignals`). A + mismatch overrides the decision to `REJECT` (`"signal-state-verification-violation"`). +5. **Refuse or authorize.** Any decision that is not `APPROVED` gets a signed Refusal Record + (RFC-0021), then `ExecutionGate.enforce()` throws. An approved decision is signed by + `RuntimeAuthorizationSigner`: the frozen `ExecutableContent`, the policy content hash, the + signals hash and a TTL. +6. **Before release.** The signing readiness check (G-52) proves the evidence signing path + works, and `ExecutionIntentService.prepare()` signs and stores an Execution Intent + (ADR-0012). If either fails, the caller gets 503 and nothing is released. +7. **Runtime pipeline.** `RuntimePipeline` runs `TrustChainValidationComponent`, then + `ExecutionComponent`, which builds the `ExecutionRequest` and calls the injected + `ExecutionSystem`. In production that is always `ExecutionGateway`, bound in + `packages/api/src/bootstrap/createExecutionSystem.ts`. This is the boundary between runtime + and gateway. +8. **Gateway.** `ExecutionGateway.execute()` verifies again, independently: version, + signature, expiry, TTL, the business transaction hash, that the policy is still current, + its approval record, that the signals are still current, and finally the nonce, which is + consumed last and only if everything else passed. It then deep freezes the content and + calls `ExecutionControlService.execute()` with a gateway attestation minted for this + authorization. +9. **Execution control.** `ExecutionControlService` authenticates the gateway, resolves the + connector for the action, opens a one time `GatewaySession`, and records `session.created` + on the audit sink. +10. **Credential isolation.** `SessionCredentialSecureConnector` checks the connector's own + `ConnectorPolicy` again, issues a single use session credential, calls + `SdkConnectorExecutor`, which calls the vendor adapter (`GatewayHubSpotAdapter`, + `GatewayPaytmAdapter`, `GatewayGitHubAdapter`, `GatewaySlackAdapter` or + `GatewayHttpAdapter`), then revokes the credential on every exit path and records the + outcome. +11. **Record.** Back in `RuntimeEngine`, `BusinessTrustPipeline` builds the signed Execution + Trust Record and `Runtime` stores it. If the action ran but the record cannot be made or + stored, the error says so explicitly rather than inviting a blind retry. +12. **Verify and receipt.** `ExecutionTrustApplication` calls `VerificationService.verify()` + and `ReceiptService.generate()`, then returns the stored record as the response. + +### Where a request can stop + +| Stop point | Step | What is produced instead | +| ------------------------------------------------------------ | ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Policy approval, capability binding or signal/intent binding | 3 | `REJECT` and a Refusal Record, no external call | +| Ordinary policy `REJECT` | 3 | Refusal Record, no external call | +| Signal state or approval signal mismatch | 4 | `REJECT` and a Refusal Record; the state check itself may call out | +| Signing not ready or Execution Intent not stored | 6 | 503, nothing released | +| Gateway verification failure | 8 | A replayed nonce returns its own error; any other failed check is thrown naming every failed check, the intent is marked `ERRORED` and the caller gets `EXECUTION_OUTCOME_UNKNOWN` | +| Gateway authentication or connector policy failure | 9, 10 | Thrown before the vendor is called; reported as above | +| Vendor answers with a failure | 10 | Recorded as evidence with `success: false` in the Trust Record | +| Vendor unreachable, timeout or thrown error | 10 | Intent marked `ERRORED`, `EXECUTION_OUTCOME_UNKNOWN` with the transaction and authorization ids | + ## Fail-closed properties that do hold today - A rejected Decision **never** produces a Signed Execution Authorization, authorization diff --git a/docs/site/deployment/aws-kms-signing.mdx b/docs/site/deployment/aws-kms-signing.mdx index 4e942e20..2b88750a 100644 --- a/docs/site/deployment/aws-kms-signing.mdx +++ b/docs/site/deployment/aws-kms-signing.mdx @@ -257,18 +257,29 @@ The gateway key is still read from `PARMANA_KEY_DIR`. A connector that verifies ## Troubleshooting -| You see | Cause | Fix | -| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `KEY_PROVIDER=aws-kms is configured, but no KMS key named "default" is reachable` | Wrong region, missing alias, or the role cannot describe the key | Check `AWS_REGION`, that `alias/default` exists in that region, and that the role has `kms:DescribeKey` on the key. | -| `KmsSigner only supports ECC_NIST_EDWARDS25519 keys` | The key has a different key spec | Create the key as in step 1. A key's spec cannot be changed. | -| `503` with code `SIGNING_UNAVAILABLE` and `AccessDeniedException` in the message | The role lacks `kms:Sign` or `kms:GetPublicKey`, or the key is disabled | Fix the permission or enable the key, then retry with a new `businessTransactionId`. Nothing was executed. See [Troubleshooting](/troubleshooting). | -| `VercelOidcTokenError: The 'x-vercel-oidc-token' header is missing` | Something called AWS while the module loaded, before any request | Use the current `api/index.ts`, which builds the app on the first request. | -| `ValidationException ... length less than or equal to 4096` | A build without the large message rule signed a message over the limit | Deploy a build that includes ADR-0010, then run `npm run verify:kms`. | -| `503` with code `EXECUTION_INTENT_UNAVAILABLE` | The signed [Execution Intent](/concepts/execution-intents) could not be signed and stored before release. Under KMS the usual causes are the same as `SIGNING_UNAVAILABLE`, or a missing `execution_intents` table | Fix the permission or apply `supabase/migrations/20260921120000_add_execution_intents.sql`, then retry with a new `businessTransactionId`. Nothing was executed. | -| `500` with code `EXECUTION_RECORD_INCOMPLETE` | The action was released and the signed record could not be produced afterwards | Do not retry as a new transaction. A signed Execution Intent exists, and an operator can rebuild the record without repeating the action. See [Execution Intents](/concepts/execution-intents) and [Troubleshooting](/troubleshooting). | -| `npm run verify:kms` says the session expired | The local AWS login has expired | Run `aws login --profile ` again. | -| A record fails offline verification against your local key | It was signed by KMS, and your local `default.public.pem` is a different key | Verify against the public key from `GET /keys/default`. | -| `AccessDenied` for `iam:` actions when you use the limited user | This is correct. The limited user has no such permission | Nothing to fix. | +| You see | Cause | Fix | +| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `KEY_PROVIDER=aws-kms is configured, but no KMS key named "default" is reachable` | Wrong region, missing alias, or the role cannot describe the key | Check `AWS_REGION`, that `alias/default` exists in that region, and that the role has `kms:DescribeKey` on the key. | +| `KmsSigner only supports ECC_NIST_EDWARDS25519 keys` | The key has a different key spec | Create the key as in step 1. A key's spec cannot be changed. | +| `503` with code `SIGNING_UNAVAILABLE` and `AccessDeniedException` in the message | The role lacks `kms:Sign` or `kms:GetPublicKey`, or the key is disabled | Fix the permission or enable the key, then retry with a new `businessTransactionId`. Nothing was executed. See [Troubleshooting](/troubleshooting). | +| `VercelOidcTokenError: The 'x-vercel-oidc-token' header is missing` | Something called AWS while the module loaded, before any request | Use the current `api/index.ts`, which builds the app on the first request. | +| `ValidationException ... length less than or equal to 4096` | A build without the large message rule signed a message over the limit | Deploy a build that includes ADR-0010, then run `npm run verify:kms`. | +| `503` with code `EXECUTION_INTENT_UNAVAILABLE` | The signed [Execution Intent](/concepts/execution-intents) could not be signed and stored before release. Under KMS the usual causes are the same as `SIGNING_UNAVAILABLE`, or a missing `execution_intents` table | Fix the permission or apply `supabase/migrations/20260921120000_add_execution_intents.sql`, then retry with a new `businessTransactionId`. Nothing was executed. | +| `500` with code `EXECUTION_RECORD_INCOMPLETE` | The action was released and the signed record could not be produced afterwards | Do not retry as a new transaction. A signed Execution Intent exists, and an operator can rebuild the record without repeating the action. See [Execution Intents](/concepts/execution-intents) and [Troubleshooting](/troubleshooting). | +| `npm run verify:kms` says the session expired | The local AWS login has expired | Run `aws login --profile ` again. | +| A record fails offline verification against your local key | It was signed by KMS, and your local `default.public.pem` is a different key | Verify against the public key from `GET /keys/default`. | +| `AccessDenied` for `iam:` actions when you use the limited user | This is correct. The limited user has no such permission | Nothing to fix. | +| `Gateway private key not found` or `Gateway public key not found` | The gateway attestation key is still a file, and KMS does not supply it | Provide `gateway.private.pem` and `gateway.public.pem` in `PARMANA_KEY_DIR` or through `PARMANA_KEY_MATERIAL_JSON`. | +| The gateway rejects a real execution with `failed checks [signatureVerified, ...]` | Signing and verification are using different keys | Check that the build includes the 2026-09-16 fix in `packages/api/src/bootstrap/createExecutionGateway.ts`, where the gateway verifies with the same KMS backed key provider the runtime signs with. | +| A connector service reports only a generic HTTP 500 | The real reason is recorded on the other side | Query `execution_audit_events` by `business_transaction_id`. Both Parmana and `parmana-paytm-agent` write a `reason` there, see the [Database Schema Reference](/storage/schema-reference). | + +## Keep in mind + +- Do not store a static AWS access key on Vercel for runtime signing. The OIDC role in step 2 exists so that no such key is needed. +- The code resolves the logical key id `default` to `alias/default`. A descriptive alias on the same key does not replace it. +- Keep `PARMANA_KEY_MATERIAL_JSON` (or the key files) after moving to KMS: it still carries the gateway key. +- A tenant key `tenant.` resolves to `alias/tenant.`. Nothing creates these aliases for you; create each one by hand with `aws kms create-alias`. +- After the key exists, narrow the administrator's own policy. `kms:CreateKey` with no resource limit is more than day to day work needs. ## What has been verified diff --git a/docs/site/guides/connect-an-agent.mdx b/docs/site/guides/connect-an-agent.mdx index f28778e0..e18b6395 100644 --- a/docs/site/guides/connect-an-agent.mdx +++ b/docs/site/guides/connect-an-agent.mdx @@ -71,8 +71,8 @@ single-purpose agent; `/callers/me`'s `unrestrictedCapabilities` is literally appending it to the array (every caller gets a 500 until fixed — the server fails closed on malformed JSON), and losing existing entries by replacing rather than appending. Always paste the complete array — every existing entry - plus the new one. Full account: - `docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md`. + plus the new one. Full account: the 2026-09-15 KMS migration troubleshooting + guide (in git history). **4. Verify the key before writing agent code:** @@ -168,7 +168,7 @@ Every row is cited to the exact source producing it: | `403` | `POLICY_DENIED` | `ExecutionGate.enforce` — uniform for _every_ rejection cause | A real, correct decision. `message` names the actual rule that matched (excessive amount, failed fraud check, etc.). | Respect it. Never retry with altered parameters. | | `404` | — | `PolicyNotFoundError` | `policy.name`/`version` not deployed on this instance. | Check spelling/version and confirm deployment. | | `409` | — | `DuplicateBusinessTransactionError` | Reused `businessTransactionId`. | Fresh UUID per attempt; design idempotency separately if needed. | -| `500` | — | Generic fallback — catches an unregistered-connector error **and** `ExecutionGateway` rejecting a signed envelope (bad signature, tampered content, replayed nonce) | Deliberately ambiguous by design, not a flaw in the error handler. Very often "APPROVED, but no connector registered" — or a Parmana-side signing/verification bug with nothing to do with your request. | Check server logs for the exact message before assuming it's your bug — `docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md` (repo root) documents a real example of this exact failure mode caused entirely server-side. | +| `500` | — | Generic fallback — catches an unregistered-connector error **and** `ExecutionGateway` rejecting a signed envelope (bad signature, tampered content, replayed nonce) | Deliberately ambiguous by design, not a flaw in the error handler. Very often "APPROVED, but no connector registered" — or a Parmana-side signing/verification bug with nothing to do with your request. | Check server logs for the exact message before assuming it's your bug — the 2026-09-15 KMS migration troubleshooting guide (in git history) (repo root) documents a real example of this exact failure mode caused entirely server-side. | | (none) | — | Timeout/network | Unknown whether Parmana ever received it. | Treat as unresolved — never guess APPROVED or REJECTED. | ## Common mistakes @@ -197,7 +197,7 @@ entry (500 for every caller) → no `allowedPrincipalIds` grant for the asserted genuine Parmana-side bug where the gateway's signing and verification paths silently used different keys after a KMS migration (500, unrelated to the request itself — `examples/tutorials/114-signing-verification-key-agreement/` reproduces it). None of these were agent -bugs. Full account: `docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md`. +bugs. Full account: the 2026-09-15 KMS migration troubleshooting guide (in git history). ## Next diff --git a/docs/site/guides/policy-lifecycle-and-approvals.mdx b/docs/site/guides/policy-lifecycle-and-approvals.mdx index 43be9d24..df6454a7 100644 --- a/docs/site/guides/policy-lifecycle-and-approvals.mdx +++ b/docs/site/guides/policy-lifecycle-and-approvals.mdx @@ -160,6 +160,33 @@ Binding an action to a different policy name, or adding a new action, still need - The gateway checks the approval record again just before release. - At startup, the server checks every policy against its latest approval record and logs the result. +These checks are on whenever `NODE_ENV` is anything other than `test` or `development`, and nothing switches them off in production. In `test` or `development` they are off unless `POLICY_EXECUTION_VERIFICATION_ENFORCED=true`. So approve every policy a deployment executes against **before** promoting it; a policy still `PENDING_APPROVAL`, or rejected with no approved replacement, is refused on the first request. + +### 8. Check what is approved + +`content_hash_after` is a hash of the canonical JSON (keys sorted), not of the file bytes, so a plain file hash will not match. Use the script that compares every policy on disk with its latest approval record: + +```bash +npx tsx scripts/verify-policy-changes-approved.ts --full-scan +``` + +The CI job `verify-policy-approvals` runs the same check for policy files changed in a push or pull request. A rejection writes no approval record: the change's `rejectionReason` is the record it leaves. + +### Sign and submit in one step + +Signing and sending by hand leaves a gap in which the 120 second step up lifetime can run out. `scripts/local-review-action.ts` signs and submits in one process, on the approver's own machine: + +```powershell +$env:REVIEWER_KEY = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR((Read-Host "Approver API key" -AsSecureString))) +npx tsx scripts/local-review-action.ts --pending-policy-change-id --action approve ` + --private-key-file --key-id ` + --api-base https://your-parmana-host +``` + +Always pass `--private-key-file`, `--key-id` and `--api-base`: the defaults name a local file, a caller id and a host from this project's own setup. The script prints the HTTP status and response only, never a key. + +`scripts/refresh-approved-policy-content.ts` proposes the file on disk and approves it in one run, so it needs both the proposer's and the approver's keys in one process. That is one person acting as both; use it only in a test environment, never where the approval record must stand for a second person. + ## Part 2: a person signs off on one action Some actions need a person each time, such as a refund above 10000. The policy declares which signal needs a signed approval, and where the request says what the approval is for: @@ -225,6 +252,27 @@ To find refusals waiting for a manager, query the Refusal Records for `matchedRu | `consumed_approval_nonces` | Action approvals already used | | `refusal_records` | Every refused request, signed | +## Troubleshooting + +| You see | Cause | Fix | +| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `401` `authentication required` | The API key is missing, wrong or rotated, or the header is not `Authorization: Bearer ` | Check the variable is set in this terminal (`$env:REVIEWER_KEY.Length` prints a number, not `0`). | +| `403 STEP_UP_AUTHORIZATION_INVALID` | The signature expired (over 120 seconds), was already used, or was made with a private key that does not match the `stepUpPublicKey` registered for this caller | Sign again and send at once, or use `local-review-action.ts`. If it persists, check your private key matches the registered public key (after a rotation, replace the `.pem` too). | +| `403 SAME_ACTOR_CANNOT_APPROVE_OWN_CHANGE` | The approving API key belongs to the proposer | A different person approves with their own key. There is no override. | +| `403 NON_HUMAN_CALLER_DENIED` | The API key is not registered as a human (`credentialHolderType USER`) | The operator registers it as a human. | +| `400` naming a field on propose | The body is malformed, `policyId` does not match the name in the URL, or the version has characters outside `A-Z a-z 0-9 . _ -` | Read the `error` message; it names the field. | +| `400 Malformed JSON body` | The shell changed the quotes in an inline JSON body | Build the body in a variable or a file as shown above, not inline in the command. | +| `409 CONFLICT` on propose | A proposal for this policy version is already open | Approve or reject the open one first. | +| Approved content lacks fields the file has | Proposals are frozen snapshots. An old proposal approved later carries the old content | Reject stale proposals and propose again from the current file, then run the full scan above. | + +## Good to know + +- A decision is final. A change moves from `PENDING_APPROVAL` to `APPROVED` or `REJECTED` once and never back; to change your mind, propose again. +- The step up private key is never sent. Only the signed authorization it produces is. +- With a database configured, approved content is written to the `policies` table through `SupabasePolicyRepository`, and requests read it from there. With `PARMANA_STORAGE=memory` it is written to the local file. +- `packages/governance-ui` can propose, list and show a diff, but cannot approve or reject: a web page that collected a step up private key would break the rule that the key never leaves its owner's machine. +- The code for this flow is small: `packages/api/src/routes/pending-policy-changes.ts`, `packages/api/src/governance/PolicyChangeApprovalService.ts` and `packages/api/src/auth/PolicyChangeStepUpVerifier.ts`. + ## Keep it honest - Two people, two machines, two keys. A single person holding both API keys makes the record claim a second person who does not exist. diff --git a/docs/site/handbook/03-cryptography.mdx b/docs/site/handbook/03-cryptography.mdx index a70fd34b..e21f3cf0 100644 --- a/docs/site/handbook/03-cryptography.mdx +++ b/docs/site/handbook/03-cryptography.mdx @@ -234,7 +234,7 @@ those two, not `KMS_REGION`/`KMS_KEY_ALIAS`, the latter pair currently does noth comments explaining exactly why they're separate. - `packages/crypto/src/providers/signer/KmsSigner.ts`, `providers/SignerKeyProviderAdapter.ts` , the real AWS KMS implementation and the incident it closes. -- `docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md`, the real incident +- the 2026-09-15 KMS migration troubleshooting guide (in git history), the real incident timeline this chapter draws from; each entry names its symptom, root cause, and fix separately, and is worth reading end to end for the migration's own stated lesson: "a migration that changes _where_ a system's trust boundary sits needs to be verified against diff --git a/docs/site/handbook/07-policy-governance-maker-checker.mdx b/docs/site/handbook/07-policy-governance-maker-checker.mdx index 6f6efd04..a678a86e 100644 --- a/docs/site/handbook/07-policy-governance-maker-checker.mdx +++ b/docs/site/handbook/07-policy-governance-maker-checker.mdx @@ -254,12 +254,11 @@ to `policies/{name}/{version}/policy.json` that skips the pending-change API ent `packages/crypto/src/PolicyChangeStepUpAuthorizationCrypto.ts`, plus `packages/api/tests/unit/verifyPolicyGovernanceIntegrityAtStartup.test.ts` (7 cases) for the integrity checker specifically. -- To actually run the real flow against a real deployment, the two operational documents - already written for exactly this purpose are `docs/operations/policy-approval-runbook.md` - (a copy-pasteable checklist for a specific batch of pending policies) and - `docs/operations/policy-governance-developer-guide.md` (the full lifecycle, every - command, and a troubleshooting table for every failure mode actually hit while building - this feature). This chapter explains the system; those two are the how-to. +- To actually run the real flow against a real deployment, use + [Policy lifecycle and approvals](/guides/policy-lifecycle-and-approvals) (the full + lifecycle, every command, and a troubleshooting table) and + [Credentials for maker, checker, approver and agent](/guides/credentials-maker-checker-approver). + This chapter explains the system; those two are the how to. ## Integration requirements diff --git a/docs/site/handbook/13-storage-layer.mdx b/docs/site/handbook/13-storage-layer.mdx index a5ffc349..6fd7460c 100644 --- a/docs/site/handbook/13-storage-layer.mdx +++ b/docs/site/handbook/13-storage-layer.mdx @@ -154,9 +154,9 @@ policy content does not. Neither the code nor any comment currently explains why repository sits outside the facade rather than being added to it as a sixth field, treat this as an open question rather than an assumed design decision. -`docs/architecture/DATABASE_SCHEMA_REFERENCE.md` is a detailed, table-by-table reference -covering every table in this schema; this chapter verified one table directly against the -migration SQL rather than only citing that document, and recommends the same discipline for +The [Database Schema Reference](/storage/schema-reference) is a detailed, table by table +reference covering every table in this schema; this chapter verified one table directly +against the migration SQL rather than only citing that page, and recommends the same discipline for any other table you need to rely on. ## How It Enables Things, With a Concrete Example diff --git a/docs/site/handbook/16-rate-limiting.mdx b/docs/site/handbook/16-rate-limiting.mdx index 3de71160..64414926 100644 --- a/docs/site/handbook/16-rate-limiting.mdx +++ b/docs/site/handbook/16-rate-limiting.mdx @@ -51,8 +51,8 @@ instance to both `createExecuteRateLimiter` and `createHealthReadyRateLimiter` c request on a fresh cold start. **A documented correction, not silently fixed:** it was first assumed this crashed the whole -request (`docs/VERIFICATION-GAPS.md`, `docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md` -item 5), inferred from seeing the `ERR_ERL_STORE_REUSE` log line next to a real HTTP 500. +request (`docs/VERIFICATION-GAPS.md`, and item 5 of the 2026-09-15 KMS migration troubleshooting +guide, in git history), inferred from seeing the `ERR_ERL_STORE_REUSE` log line next to a real HTTP 500. Reading `express-rate-limit`'s actual installed source shows every validation is wrapped in a try/catch that only logs the violation and never re-throws, the reuse warning is real, but harmless on its own. The 500 that request actually returned was caused by a completely diff --git a/docs/site/handbook/17-audit-and-evidence-trails.mdx b/docs/site/handbook/17-audit-and-evidence-trails.mdx index 91d6967a..eae3981f 100644 --- a/docs/site/handbook/17-audit-and-evidence-trails.mdx +++ b/docs/site/handbook/17-audit-and-evidence-trails.mdx @@ -73,8 +73,8 @@ next, documented rather than silently changed. ### Execution Audit Events `SupabaseExecutionAuditSink` (`packages/storage/src/supabase/SupabaseExecutionAuditSink.ts`) -covers the execution-control lifecycle separately from caller authentication. Per -`docs/architecture/DATABASE_SCHEMA_REFERENCE.md`'s own note (verify this claim yourself if you +covers the execution-control lifecycle separately from caller authentication. Per the +[Database Schema Reference](/storage/schema-reference) (verify this claim yourself if you touch this table, it is a real operational fact worth confirming against current deployment config, not just trusting a comment), the underlying `execution_audit_events` table is shared cross-repository with a separate `parmana-paytm-agent` repository, meaning this table's schema diff --git a/docs/site/handbook/21-deployment.mdx b/docs/site/handbook/21-deployment.mdx index d9cdf31d..d081f38d 100644 --- a/docs/site/handbook/21-deployment.mdx +++ b/docs/site/handbook/21-deployment.mdx @@ -95,9 +95,9 @@ AWS_ROLE_ARN # the IAM role Vercel's OIDC token assumes (Confirmed by grepping `packages/crypto/src/providers/signer/KmsSigner.ts` directly; these are the only two `process.env` reads in that file, `KmsSigner requires AWS_REGION to be set` is a -real thrown error if `AWS_REGION` is missing.) `docs/operations/aws-kms-vercel-oidc-setup-guide.md` +real thrown error if `AWS_REGION` is missing.) [AWS KMS signing](/deployment/aws-kms-signing) documents the Vercel side of the setup (`vercel env add AWS_REGION production`, and the trust -policy connecting Vercel's OIDC issuer to the IAM role). `docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md` +policy connecting Vercel's OIDC issuer to the IAM role). The 2026-09-15 KMS migration troubleshooting guide (in git history) records the real incidents hit while migrating to this from static keys, cross-check its specific claims against current source rather than assuming they still describe live bugs; the migration referenced there is complete as of this session. @@ -114,7 +114,7 @@ independently of that specific `KEY_PROVIDER` flag, verify the current wiring in There is no example tutorial that actually deploys to either platform (that would require real cloud credentials and is out of scope for a hermetic tutorial suite), but -`docs/site/guides/deploy-patterns.mdx` and `docs/operations/aws-kms-vercel-oidc-setup-guide.md` +`docs/site/guides/deploy-patterns.mdx` and [AWS KMS signing](/deployment/aws-kms-signing) walk through the real, current deployment steps for each target. ## How to validate this yourself @@ -124,8 +124,8 @@ walk through the real, current deployment steps for each target. - `packages/policy/src/SupabasePolicyRepository.ts`'s own doc comment for the EROFS incident account. - `supabase/migrations/20260916060000_add_policies_table.sql` for the schema the fix depends on. -- `docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md` and - `docs/operations/aws-kms-vercel-oidc-setup-guide.md` for the full operational history. +- the 2026-09-15 KMS migration troubleshooting guide (in git history) and + [AWS KMS signing](/deployment/aws-kms-signing) for the full operational history. ## Requirements diff --git a/docs/site/handbook/23-history-and-open-questions.mdx b/docs/site/handbook/23-history-and-open-questions.mdx index 713162a8..51c78697 100644 --- a/docs/site/handbook/23-history-and-open-questions.mdx +++ b/docs/site/handbook/23-history-and-open-questions.mdx @@ -61,7 +61,7 @@ distinct closure sessions), this section names the eras and points at where to r The gateway's signing key moved from a local file-based key to AWS KMS, authenticated via Vercel OIDC rather than static AWS credentials. Six real bugs were found and fixed in the -course of this migration; `docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md` +course of this migration; the 2026-09-15 KMS migration troubleshooting guide (in git history) is the detailed account. The one worth naming specifically here, because of how it was found: `SignerKeyProviderAdapter` (packages/crypto) had a divergence between the key used to _sign_ and the key used to _verify_, a signing/verification key mismatch that would have diff --git a/docs/site/llms-full.txt b/docs/site/llms-full.txt index 703999aa..c4e7177d 100644 --- a/docs/site/llms-full.txt +++ b/docs/site/llms-full.txt @@ -3960,18 +3960,29 @@ The gateway key is still read from `PARMANA_KEY_DIR`. A connector that verifies ## Troubleshooting -| You see | Cause | Fix | -| --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `KEY_PROVIDER=aws-kms is configured, but no KMS key named "default" is reachable` | Wrong region, missing alias, or the role cannot describe the key | Check `AWS_REGION`, that `alias/default` exists in that region, and that the role has `kms:DescribeKey` on the key. | -| `KmsSigner only supports ECC_NIST_EDWARDS25519 keys` | The key has a different key spec | Create the key as in step 1. A key's spec cannot be changed. | -| `503` with code `SIGNING_UNAVAILABLE` and `AccessDeniedException` in the message | The role lacks `kms:Sign` or `kms:GetPublicKey`, or the key is disabled | Fix the permission or enable the key, then retry with a new `businessTransactionId`. Nothing was executed. See [Troubleshooting](/troubleshooting). | -| `VercelOidcTokenError: The 'x-vercel-oidc-token' header is missing` | Something called AWS while the module loaded, before any request | Use the current `api/index.ts`, which builds the app on the first request. | -| `ValidationException ... length less than or equal to 4096` | A build without the large message rule signed a message over the limit | Deploy a build that includes ADR-0010, then run `npm run verify:kms`. | -| `503` with code `EXECUTION_INTENT_UNAVAILABLE` | The signed [Execution Intent](/concepts/execution-intents) could not be signed and stored before release. Under KMS the usual causes are the same as `SIGNING_UNAVAILABLE`, or a missing `execution_intents` table | Fix the permission or apply `supabase/migrations/20260921120000_add_execution_intents.sql`, then retry with a new `businessTransactionId`. Nothing was executed. | -| `500` with code `EXECUTION_RECORD_INCOMPLETE` | The action was released and the signed record could not be produced afterwards | Do not retry as a new transaction. A signed Execution Intent exists, and an operator can rebuild the record without repeating the action. See [Execution Intents](/concepts/execution-intents) and [Troubleshooting](/troubleshooting). | -| `npm run verify:kms` says the session expired | The local AWS login has expired | Run `aws login --profile ` again. | -| A record fails offline verification against your local key | It was signed by KMS, and your local `default.public.pem` is a different key | Verify against the public key from `GET /keys/default`. | -| `AccessDenied` for `iam:` actions when you use the limited user | This is correct. The limited user has no such permission | Nothing to fix. | +| You see | Cause | Fix | +| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `KEY_PROVIDER=aws-kms is configured, but no KMS key named "default" is reachable` | Wrong region, missing alias, or the role cannot describe the key | Check `AWS_REGION`, that `alias/default` exists in that region, and that the role has `kms:DescribeKey` on the key. | +| `KmsSigner only supports ECC_NIST_EDWARDS25519 keys` | The key has a different key spec | Create the key as in step 1. A key's spec cannot be changed. | +| `503` with code `SIGNING_UNAVAILABLE` and `AccessDeniedException` in the message | The role lacks `kms:Sign` or `kms:GetPublicKey`, or the key is disabled | Fix the permission or enable the key, then retry with a new `businessTransactionId`. Nothing was executed. See [Troubleshooting](/troubleshooting). | +| `VercelOidcTokenError: The 'x-vercel-oidc-token' header is missing` | Something called AWS while the module loaded, before any request | Use the current `api/index.ts`, which builds the app on the first request. | +| `ValidationException ... length less than or equal to 4096` | A build without the large message rule signed a message over the limit | Deploy a build that includes ADR-0010, then run `npm run verify:kms`. | +| `503` with code `EXECUTION_INTENT_UNAVAILABLE` | The signed [Execution Intent](/concepts/execution-intents) could not be signed and stored before release. Under KMS the usual causes are the same as `SIGNING_UNAVAILABLE`, or a missing `execution_intents` table | Fix the permission or apply `supabase/migrations/20260921120000_add_execution_intents.sql`, then retry with a new `businessTransactionId`. Nothing was executed. | +| `500` with code `EXECUTION_RECORD_INCOMPLETE` | The action was released and the signed record could not be produced afterwards | Do not retry as a new transaction. A signed Execution Intent exists, and an operator can rebuild the record without repeating the action. See [Execution Intents](/concepts/execution-intents) and [Troubleshooting](/troubleshooting). | +| `npm run verify:kms` says the session expired | The local AWS login has expired | Run `aws login --profile ` again. | +| A record fails offline verification against your local key | It was signed by KMS, and your local `default.public.pem` is a different key | Verify against the public key from `GET /keys/default`. | +| `AccessDenied` for `iam:` actions when you use the limited user | This is correct. The limited user has no such permission | Nothing to fix. | +| `Gateway private key not found` or `Gateway public key not found` | The gateway attestation key is still a file, and KMS does not supply it | Provide `gateway.private.pem` and `gateway.public.pem` in `PARMANA_KEY_DIR` or through `PARMANA_KEY_MATERIAL_JSON`. | +| The gateway rejects a real execution with `failed checks [signatureVerified, ...]` | Signing and verification are using different keys | Check that the build includes the 2026-09-16 fix in `packages/api/src/bootstrap/createExecutionGateway.ts`, where the gateway verifies with the same KMS backed key provider the runtime signs with. | +| A connector service reports only a generic HTTP 500 | The real reason is recorded on the other side | Query `execution_audit_events` by `business_transaction_id`. Both Parmana and `parmana-paytm-agent` write a `reason` there, see the [Database Schema Reference](/storage/schema-reference). | + +## Keep in mind + +- Do not store a static AWS access key on Vercel for runtime signing. The OIDC role in step 2 exists so that no such key is needed. +- The code resolves the logical key id `default` to `alias/default`. A descriptive alias on the same key does not replace it. +- Keep `PARMANA_KEY_MATERIAL_JSON` (or the key files) after moving to KMS: it still carries the gateway key. +- A tenant key `tenant.` resolves to `alias/tenant.`. Nothing creates these aliases for you; create each one by hand with `aws kms create-alias`. +- After the key exists, narrow the administrator's own policy. `kms:CreateKey` with no resource limit is more than day to day work needs. ## What has been verified diff --git a/docs/site/llms.txt b/docs/site/llms.txt index e48a02f0..41df6f1a 100644 --- a/docs/site/llms.txt +++ b/docs/site/llms.txt @@ -202,7 +202,7 @@ This file is generated from the documentation navigation. Do not edit it by hand ## Deploy and operate: Operate * [Troubleshooting](https://docs.parmanasystems.com/troubleshooting): Real problems encountered while building and using this repo, with real causes. -* [Database Schema Reference](https://docs.parmanasystems.com/storage/schema-reference): Every live table: what it's for, its columns, which migration built it, what code owns it — plus a library of real SQL for troubleshooting, audit, and verification. +* [Database Schema Reference](https://docs.parmanasystems.com/storage/schema-reference): Every live table: what it is for, its columns, which migration built it, what code owns it, plus a library of real SQL for troubleshooting, audit and verification. ## Reference: Packages * [@parmana/shared](https://docs.parmanasystems.com/reference/shared): Domain types, config loading, and error classes every other package depends on. No business logic of its own. diff --git a/docs/site/reference/api.mdx b/docs/site/reference/api.mdx index 09e4b627..b86680a6 100644 --- a/docs/site/reference/api.mdx +++ b/docs/site/reference/api.mdx @@ -48,7 +48,7 @@ composition root for the same responsibility as `createExecutionControl.ts` — not the `ConnectorFactory` interface from `@parmana/connector-sdk` described on [this page](/reference/connector-sdk)) previously sat alongside the real path with zero callers anywhere in the repository. Removed in Phase 2I -(`docs/architecture/phase2i-composition-root-cleanup.md`) after independent verification +(the Phase 2I composition root cleanup record, in git history) after independent verification confirmed they were never wired into production, tests, or any published surface. diff --git a/docs/site/storage/schema-reference.mdx b/docs/site/storage/schema-reference.mdx index 3e9be595..591c2535 100644 --- a/docs/site/storage/schema-reference.mdx +++ b/docs/site/storage/schema-reference.mdx @@ -1,107 +1,325 @@ --- title: "Database Schema Reference" -description: "Every live table: what it's for, its columns, which migration built it, what code owns it — plus a library of real SQL for troubleshooting, audit, and verification." +description: "Every live table: what it is for, its columns, which migration built it, what code owns it, plus a library of real SQL for troubleshooting, audit and verification." --- - Repo copy of this guide: `docs/architecture/DATABASE_SCHEMA_REFERENCE.md`, - kept in sync with this page. Verified against the live production database and - all 28 `supabase/migrations/*.sql` files. See [Storage](/storage/overview) - first for the conceptual model. + Checked against all 31 `supabase/migrations/*.sql` files, read in order. + Several tables were altered by later migrations, so the shapes below are the + final, cumulative schema, not only what the first `CREATE TABLE` said. See + [Storage](/storage/overview) first for the conceptual model. -**Connecting:** the application connects directly via Postgres (`DATABASE_URL`), not through -PostgREST — a direct connection bypasses Row Level Security the same way a table owner does. -RLS below matters for what a different credential (e.g. Supabase's `anon` role) could see, -not what the application's own connection can. +**Connecting:** the application connects directly to Postgres (`DATABASE_URL`), not through +PostgREST. A direct connection bypasses Row Level Security the same way a table owner does, +so RLS below matters for what a different credential (for example Supabase's `anon` role) +could see, not for what the application's own connection can. + +```bash +psql "$DATABASE_URL" +``` ## Quick index -| Table | Purpose | Owning code | -| --------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------- | -| `business_transactions` | The initial received request, as submitted | `SupabaseBusinessTransactionRepository` | -| `executions` | Append-only log of execution attempts | `SupabaseExecutionTrustRecordRepository` | -| `execution_trust_records` | The signed, final trust record | `SupabaseExecutionTrustRecordRepository` | -| `verifications` | Log of independent verification calls | `SupabaseExecutionTrustRecordRepository` | -| `receipts` | Signed receipts | `SupabaseExecutionTrustRecordRepository` | -| `settlement_confirmations` | Signed settlement confirmations | `SupabaseExecutionTrustRecordRepository` | -| `overrides` | `OverrideService` backing table — deliberately unreachable, expect empty | `SupabaseExecutionTrustRecordRepository` | -| `refusal_records` | Signed record of a policy/binding REJECT | `SupabaseRefusalRecordRepository` | -| `caller_audit_events` | Every caller-auth decision — highest write volume | `SupabaseCallerAuditSink` | -| `execution_audit_events` | Execution lifecycle events — **shared cross-repo with `parmana-paytm-agent`** | `SupabaseExecutionAuditSink` | -| `pending_policy_changes` | Maker-checker: a proposed change awaiting approval | `SupabasePendingPolicyChangeRepository` | -| `policy_change_approval_records` | Maker-checker: signed approval/rejection | `SupabasePolicyChangeApprovalRecordRepository` | -| `consumed_nonces` | Replay protection: Gateway Authorization envelopes | `SupabaseNonceStore` | -| `consumed_approval_nonces` | Replay protection: Approval Artifacts (separate namespace) | `SupabaseApprovalNonceStore` | -| `consumed_policy_change_step_up_nonces` | Replay protection: checker step-up key (separate namespace) | `SupabasePolicyChangeStepUpNonceStore` | -| `challenge_records` | Investigation records — the only mutable table | `PostgresChallengeRecordRepository` | -| `rate_limit_counters` | Fleet-wide rate limits — the only table without RLS | `PostgresRateLimitStore` | +| Table | Purpose | Owning code | +| --------------------------------------- | ----------------------------------------------------------------------- | ---------------------------------------------- | +| `business_transactions` | The received request, as submitted | `SupabaseBusinessTransactionRepository` | +| `executions` | Append only log of execution attempts | `SupabaseExecutionTrustRecordRepository` | +| `execution_trust_records` | The signed, final trust record | `SupabaseExecutionTrustRecordRepository` | +| `verifications` | Log of independent verification calls | `SupabaseExecutionTrustRecordRepository` | +| `receipts` | Signed receipts | `SupabaseExecutionTrustRecordRepository` | +| `settlement_confirmations` | Signed settlement confirmations | `SupabaseExecutionTrustRecordRepository` | +| `overrides` | `OverrideService` backing table, deliberately unreachable, expect empty | `SupabaseExecutionTrustRecordRepository` | +| `refusal_records` | Signed record of a refused decision | `SupabaseRefusalRecordRepository` | +| `execution_intents` | Signed intent stored before an action is released (ADR-0012) | `SupabaseExecutionIntentRepository` | +| `caller_audit_events` | Every caller authentication decision, the highest write volume | `SupabaseCallerAuditSink` | +| `execution_audit_events` | Execution lifecycle events, **shared with `parmana-paytm-agent`** | `SupabaseExecutionAuditSink` | +| `policies` | Policy content written on approval (Vercel has a read only filesystem) | `SupabasePolicyRepository` | +| `pending_policy_changes` | Maker and checker: a proposed change awaiting approval | `SupabasePendingPolicyChangeRepository` | +| `policy_change_approval_records` | Maker and checker: signed approval or rejection | `SupabasePolicyChangeApprovalRecordRepository` | +| `consumed_nonces` | Replay protection: gateway authorization envelopes | `SupabaseNonceStore` | +| `consumed_approval_nonces` | Replay protection: approval artifacts (separate namespace) | `SupabaseApprovalNonceStore` | +| `consumed_policy_change_step_up_nonces` | Replay protection: the checker's step up key (separate namespace) | `SupabasePolicyChangeStepUpNonceStore` | +| `challenge_records` | Investigation records, the only mutable table | `PostgresChallengeRecordRepository` | +| `rate_limit_counters` | Fleet wide rate limits, the only table without RLS | `PostgresRateLimitStore` | +| `handbook_download_leads` | Email captured before a handbook PDF download | `SupabaseHandbookDownloadLeadRepository` | Three Razorpay tables (`razorpay_webhook_events`, `razorpay_webhook_audit_events`, `razorpay_daily_refund_reservations`) were - dropped 2026-09-16 — orphaned schema from the Razorpay connector, removed from - this codebase entirely on 2026-08-12. + dropped on 2026-09-16 by `20260916120000_drop_razorpay_tables.sql`. The + Razorpay connector itself was removed on 2026-08-12. -## Tables that share one shape - -`consumed_nonces`, `consumed_approval_nonces`, `consumed_policy_change_step_up_nonces` are -three **deliberately separate** tables, not duplication — each backs a distinct trust -domain's nonce namespace (a Gateway envelope, an Approval Artifact, a checker's step-up -key), issued by different parties. Sharing one table would let a coincidental collision -between unrelated namespaces falsely report "already consumed." All three: `TEXT PRIMARY KEY` -nonce column (the PK **is** the atomicity mechanism — a concurrent duplicate insert hits -Postgres `23505 unique_violation`, mapped to "already consumed," no separate locking -needed), `expires_at TIMESTAMPTZ NOT NULL`, `consumed_at TIMESTAMPTZ DEFAULT now()`. - -`executions`, `verifications`, `receipts`, `overrides` also share a near-identical shape: -an id, a `business_transaction_id` FK (`ON DELETE RESTRICT`), a JSONB payload column, a -timestamp, and a `seq BIGSERIAL` (added by `20260711120000` for exact insertion-order — -millisecond timestamps can tie under fast concurrent appends). - -## Tables worth extra attention - -**`caller_audit_events`** — the most-altered table in the schema (8 migrations). Its `type` -CHECK constraint was widened 5 times. One of those widenings -(`20260911090000_add_capability_granted_to_caller_audit_events.sql`) was a **bug-fix**: -application code had already been writing `'caller.capability_granted'` before any -migration allowed it, so every successful authenticated request failed closed with `503 -AUDIT_UNAVAILABLE` until fixed. Adding a new event type here and forgetting the matching -migration reproduces this exact outage. Chained **per `caller_id`** via a Postgres advisory -lock on `hashtext(caller_id)`, not one global lock. - -**`execution_audit_events`** — the only table written to by code outside this repository. -`parmana-paytm-agent` writes directly to it (its own `src/parmana/audit.ts`), correlated by -`business_transaction_id` (not `authorization_id`, which is Parmana-internal and never -crosses the trust boundary). Its rows are unsigned/unchained — that service holds only -Parmana's public key, never a private key. Chained **per `authorization_id`** on this -repo's own side. - -**`policy_change_approval_records`** — the only table with a real RLS policy attached -(`anon` role, read-only, for CI). As of this writing, zero rows — none of the 10 real -production policies have been approved by a distinct human checker yet. Expected, not a bug. - -**`challenge_records`** — the only mutable table in the schema. Every other table here is -append-only by convention; this one is explicitly updated as an investigation proceeds. -Deliberately unsigned: the writer and the party who could misrepresent it are the same -party, so a signature would prove tamper-evidence of bytes without addressing the actual -trust question. - -**`rate_limit_counters`** — the only table without RLS enabled (deliberately — no business -data, no dashboard access path). See `docs/VERIFICATION-GAPS.md` G-49 (repo root) for why -this table's keys are now prefixed per rate limiter. +Not covered here: `supabase_migrations.schema_migrations`, the Supabase CLI's own tracking +table in a different schema (see the migration sync query below). + +## Table by table + +### `business_transactions` + +The durable record of a request exactly as submitted to `POST /execute`, before any +decision. Created by `20260629013035_initial_schema.sql` and never altered. + +| Column | Type | Notes | +| ------------------------- | ---------------------- | ----------- | +| `business_transaction_id` | `TEXT` | Primary key | +| `status` | `TEXT NOT NULL` | | +| `authority_json` | `JSONB NOT NULL` | | +| `authorization_json` | `JSONB NOT NULL` | | +| `intent_json` | `JSONB NOT NULL` | | +| `metadata_json` | `JSONB NOT NULL` | | +| `policy_json` | `JSONB NOT NULL` | | +| `signals_json` | `JSONB NOT NULL` | | +| `created_at` | `TIMESTAMPTZ NOT NULL` | Indexed | + +`status` is never updated after receipt. The current outcome of a transaction lives in +`executions`. RLS enabled, no policies. + +### `executions`, `verifications`, `receipts`, `overrides` + +These four share one shape: an id primary key (`execution_id`, `verification_id`, +`receipt_id`, `override_id`), a `business_transaction_id` foreign key with +`ON DELETE RESTRICT`, one JSONB payload column (`execution_json`, `verification_json`, +`receipt_json`, `override_json`), a timestamp (`created_at`, `verified_at`, `issued_at`, +`created_at`), and a `seq BIGSERIAL` added by `20260711120000` as an exact insertion order, +because millisecond timestamps can tie under fast concurrent appends. All created by +`20260629013035_initial_schema.sql`, indexed on `business_transaction_id` and the timestamp, +RLS enabled. + +- A transaction can have several `executions` rows: it is an append only sequence, not one + mutable status. +- `overrides` is expected to stay empty: `OverrideService` is deliberately not reachable from + the API. + +### `execution_trust_records` + +The final, signed Execution Trust Record, the artifact an independent verifier checks. Not +every transaction gets one: a refused decision gets a `refusal_records` row instead. + +Created by `20260629013035_initial_schema.sql` (`trust_record_id` primary key, +`business_transaction_id TEXT NOT NULL UNIQUE` with a restricting foreign key, +`transaction_json`, `trust_record_hash`, `created_at`, `updated_at`). Altered twice: + +- `20260702183000` added `signature_json JSONB` (nullable, GIN indexed), storing the + record's own signature. +- `20260907120000` added `authorization_json JSONB` (GIN indexed, the + `SignedExecutionAuthorization`), `schema_version INTEGER` and `signatures_json JSONB` + (hybrid signatures; without it a hybrid record would lose its second signature on the way + through this table). + +RLS enabled. + +### `settlement_confirmations` + +Signed confirmations that a transaction settled. Created by +`20260718190412_add_settlement_confirmations_and_audit_severity.sql` with the same shape as +`receipts`: `confirmation_id TEXT` primary key, `business_transaction_id TEXT NOT NULL`, +`confirmation_json JSONB NOT NULL`, `issued_at TIMESTAMPTZ NOT NULL`, `seq BIGSERIAL`, +`created_at TIMESTAMPTZ NOT NULL DEFAULT now()`. Indexed on `business_transaction_id`, RLS +enabled, append only. + +Unlike the tables in the initial schema, this one has **no foreign key** back to +`business_transactions`. Do not rely on referential integrity here the way you can for +`executions` or `execution_trust_records`. + +### `refusal_records` + +The signed record of a refused decision (RFC-0021). `RuntimeEngine` writes one for every +decision that is not `APPROVED`: a policy rule rejection, a policy approval check failure, a +capability binding violation, a signal to intent binding violation, or a signal state or +approval signal mismatch. Caller authentication failures go to `caller_audit_events` +instead, and errors where the policy could not be evaluated at all write nothing here. + +Created by `20260802120000_add_refusal_records.sql`: `refusal_record_id` primary key, +`business_transaction_id TEXT NOT NULL UNIQUE` with a restricting foreign key, +`decision_json NOT NULL`, `evaluated_intent_json NOT NULL`, `binding_violations_json` +(nullable), `submitted_by` (nullable), `refusal_record_hash TEXT NOT NULL`, +`signature_json JSONB NOT NULL`. Signed from creation. Indexed on `created_at`, RLS enabled. + +### `execution_intents` + +The signed Execution Intent written before an action is released to a connector +(ADR-0012). If it cannot be stored, nothing is released and the caller gets `503 +EXECUTION_INTENT_UNAVAILABLE`. Created by `20260921120000_add_execution_intents.sql`. + +- Identity and binding: `intent_id` primary key, `business_transaction_id TEXT NOT NULL +UNIQUE` with a restricting foreign key, `decision_id`, `authorization_id`, `policy_name`, + `policy_version`, `business_transaction_hash`, `action`, `target`, `intent_hash`, + `signature_json JSONB`, `created_at` (all `NOT NULL`); `policy_content_hash`, + `signals_hash`, `submitted_by`, `granted_capability` (nullable). +- Lifecycle: `state` is one of `PREPARED`, `RELEASED`, `FINALIZED`, `ERRORED`, `RESOLVED` + (default `PREPARED`); `released_context_json`, `released_at`, `finalized_at`, + `finalization_mode` (`INLINE` or `REPAIRED`), `trust_record_id`, `failure_reason`. +- Resolution by an operator: `resolution` (`NOT_EXECUTED` or `EXECUTED`), + `resolution_note`, `resolved_by`, `resolved_at`. A check constraint requires resolution, + note and time whenever `state` is `RESOLVED`. + +A partial index on `created_at` covers rows not yet `FINALIZED` or `RESOLVED`: these are the +actions that were, or may have been, released without a finished record. RLS enabled. + +### `caller_audit_events` + +Every caller authentication and scoping decision; it is written on every authenticated +request. Created by `20260718090000_add_nonce_and_caller_audit_tables.sql` and altered by +seven later migrations. Final shape: + +| Column | Type | Added by | Notes | +| ----------------------------------------------------- | ------------------------------------ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| `id` | `BIGSERIAL PRIMARY KEY` | initial | | +| `type` | `TEXT NOT NULL CHECK (...)` | initial | Widened five times, see below | +| `occurred_at` | `TIMESTAMPTZ NOT NULL` | initial | Indexed | +| `route` | `TEXT NOT NULL` | initial | | +| `caller_id` | `TEXT` | initial | Present for authenticated callers. Indexed | +| `reason` | `TEXT` | initial | Rejections only, never a credential value | +| `inserted_at` | `TIMESTAMPTZ NOT NULL DEFAULT now()` | initial | | +| `signature_json` | `JSONB` | `20260802130000` | Signed from this migration on; older rows stay unsigned | +| `capability` | `TEXT` | `20260812120000` | Indexed where not null | +| `principal_id` | `TEXT` | `20260816120000` | Indexed where not null | +| `severity` | `TEXT` | `20260818130000` | | +| `business_transaction_id` | `TEXT` | `20260824090000` | Indexed where not null | +| `chain_hash`, `previous_chain_hash`, `chain_position` | `TEXT`, `TEXT`, `BIGINT` | `20260906120000` | Chained **per `caller_id`** under a Postgres advisory lock on `hashtext(caller_id)`; rows with no `caller_id` stay unchained | + +Allowed `type` values, after the widenings in `20260812120000`, `20260816120000`, +`20260818130000`, `20260824090000` and `20260911090000`: `caller.authenticated`, +`caller.rejected`, `caller.capability_denied`, `caller.principal_denied`, +`caller.non_human_denied`, `caller.structural_rejected`, `caller.capability_granted`. + + + `20260911090000` fixed an outage: the application wrote + `caller.capability_granted` before any migration allowed it, so every + successful authenticated request failed closed with `503 AUDIT_UNAVAILABLE`. + Adding a new caller audit event type in code without the matching migration + reproduces that outage. + + +RLS enabled. + +### `execution_audit_events` + +Execution control lifecycle events. It is the only table written by code outside this +repository: `parmana-paytm-agent` writes to it directly, correlated by +`business_transaction_id` (`authorization_id` is internal to Parmana and never crosses to the +agent). Created by `20260914120000_add_execution_audit_events.sql`, then: + +- `20260914130000` added `business_transaction_id TEXT` (nullable, indexed) as the cross + service correlation key, and made `signature_json`, `chain_hash` and `chain_position` + nullable, because the agent holds only Parmana's public key and cannot sign or chain. +- `20260914140000` widened `type` to add `authorization.verified`, the event the agent + records after it verifies Parmana's signature. + +Columns: `id BIGSERIAL` primary key, `type` (`session.created`, `execution.completed`, +`execution.rejected`, `authorization.verified`), `occurred_at TIMESTAMPTZ NOT NULL`, +`connector_id`, `authorization_id`, `session_id` (all `TEXT NOT NULL`), `action`, `reason` +(rejections only, never a credential), `credential_id`, `gateway_id`, `signature_json`, +`chain_hash`, `previous_chain_hash`, `chain_position`, `inserted_at`, +`business_transaction_id`. Chained **per `authorization_id`**. Indexed on `occurred_at`, +`authorization_id`, `connector_id`, `business_transaction_id`. RLS enabled. + +### `policies` + +Policy content keyed by `(policy_name, policy_version)`, with `content_json JSONB NOT NULL` +and `updated_at`. Created by `20260916060000_add_policies_table.sql`. It exists because +Vercel functions run on a read only filesystem, so approving a pending policy change there +cannot write a file; `SupabasePolicyRepository` stores the approved content here instead. +RLS enabled. + +### `pending_policy_changes` + +A proposed policy change awaiting a checker's approval or rejection. Created by +`20260818120000_add_policy_governance_tables.sql`: `pending_policy_change_id` primary key, +`policy_name` and `policy_version` (`TEXT NOT NULL`), `proposed_content_json JSONB NOT +NULL`, `proposed_by TEXT NOT NULL`, `proposed_at TIMESTAMPTZ NOT NULL`, `status` (one of +`PENDING_APPROVAL`, `APPROVED`, `REJECTED`), `reason TEXT NOT NULL`, `resolved_by`, +`resolved_at`, `rejection_reason`. + +The partial unique index `ux_pending_policy_changes_open` on `(policy_name, policy_version)` +where `status = 'PENDING_APPROVAL'` enforces at most one open change per policy version in +the database itself. Indexed on `status`, RLS enabled. + +### `policy_change_approval_records` + +The signed record of a checker's decision on a pending change. Created with +`pending_policy_changes`: `policy_change_approval_record_id` primary key, +`pending_policy_change_id` (a restricting foreign key to `pending_policy_changes`), +`policy_name`, `policy_version`, `proposed_by`, `approved_by`, `proposed_at`, `approved_at`, +`content_hash_before` (nullable), `content_hash_after TEXT NOT NULL`, +`signature_json JSONB NOT NULL`. `20260907130000` added `previous_record_hash` (nullable), +which chains the approval history itself; `content_hash_after` only proves the live policy +matches what was approved. Indexed on `(policy_name, policy_version, approved_at DESC)`. + +This is the only table with a real RLS policy: `20260818150000` grants the `anon` role read +only `SELECT` here, for the CI job that checks policy approvals with `SUPABASE_ANON_KEY`. +Never write rows here by hand. + +### `consumed_nonces`, `consumed_approval_nonces`, `consumed_policy_change_step_up_nonces` + +Three **deliberately separate** tables, not duplication. Each backs a distinct nonce +namespace (a gateway envelope, an approval artifact, a checker's step up key) issued by +different parties; sharing one table would let a collision between unrelated namespaces +falsely report "already consumed". + +| Column | Type | Notes | +| -------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| (nonce column) | `TEXT PRIMARY KEY` | The primary key is the atomicity mechanism: a concurrent duplicate insert gets Postgres `23505 unique_violation`, mapped to "already consumed", no locks | +| `expires_at` | `TIMESTAMPTZ NOT NULL` | Indexed; not read back by application code today | +| `consumed_at` | `TIMESTAMPTZ DEFAULT now()` | | + +Created by `20260718090000_add_nonce_and_caller_audit_tables.sql`, +`20260805180000_add_consumed_approval_nonces.sql` and +`20260818140000_add_consumed_policy_change_step_up_nonces.sql`. Never altered, RLS enabled. + +### `challenge_records` + +Investigation records for a disputed claim (RFC-0022), and the only mutable table: `status`, +`investigation_steps_json`, `finding_json`, `outcome_json` and `disclosure_json` are updated +as an investigation proceeds. Created by `20260803150000_add_challenge_records.sql`: +`challenge_record_id` primary key, `status` (`open`, `investigating`, `resolved`), +`claim_challenged TEXT NOT NULL`, `source_json JSONB NOT NULL`, +`investigation_steps_json JSONB NOT NULL DEFAULT '[]'`, the three nullable JSONB result +columns, `supersedes TEXT` (deliberately no foreign key), `created_at`, `updated_at`. +Indexed on `status` and `created_at`, RLS enabled. + +Deliberately unsigned: the party that writes it is the party that could misrepresent it, so +a signature would prove the bytes were not altered without answering the real trust +question. Append only history inside a record is enforced by +`PostgresChallengeRecordRepository.append`, not by the database. + +### `rate_limit_counters` + +Fleet wide rate limit counters shared by every process on the same database, the durable +counterpart to the in process store of `express-rate-limit`. Created by +`20260910120000_add_rate_limit_counters.sql`: `key TEXT PRIMARY KEY` (prefixed per limiter, +`execute:` plus a caller id or `health:` plus a client IP, so the two limiters never share a +key), `count INTEGER NOT NULL`, `reset_time TIMESTAMPTZ NOT NULL` (indexed). An expired row +resets on its next hit, so no cleanup job is needed. + +The only table without RLS, deliberately: it holds no business or trust data and has no +dashboard access path. See `docs/VERIFICATION-GAPS.md` G-49 for why the keys are prefixed. + +### `handbook_download_leads` + +The email address given before a handbook PDF download (`packages/api/src/routes/handbook.ts`). +Created by `20260916150000_add_handbook_download_leads.sql`: `handbook_download_lead_id TEXT` +primary key, `email TEXT NOT NULL`, `captured_at TIMESTAMPTZ NOT NULL DEFAULT now()` +(indexed descending). RLS enabled. ## Common SQL for troubleshooting, audit, and verification +Every example assumes a `psql` or `pg` session on `DATABASE_URL`. Replace `$1`, `$2` with real +values. + ### Trace one transaction across every table -The single most useful query — everything that happened for one `business_transaction_id`: +Everything that happened for one `business_transaction_id`: ```sql SELECT 'business_transactions' AS source, created_at AS at, status::text AS detail FROM business_transactions WHERE business_transaction_id = $1 UNION ALL +SELECT 'execution_intents', created_at, state + FROM execution_intents WHERE business_transaction_id = $1 +UNION ALL SELECT 'executions', created_at, (execution_json->>'status') FROM executions WHERE business_transaction_id = $1 UNION ALL @@ -119,9 +337,17 @@ SELECT 'caller_audit_events', occurred_at, type ORDER BY at; ``` -This is the exact pattern used to find that `parmana-paytm-agent` had recorded -`authorization.verified` for a transaction Parmana's own logs only showed a generic `500` -for. +This pattern found a case where `parmana-paytm-agent` had recorded `authorization.verified` +for a transaction that Parmana's own logs only showed as a generic `500`. + +### Find released actions without a finished record + +```sql +SELECT intent_id, business_transaction_id, state, created_at, failure_reason +FROM execution_intents +WHERE state NOT IN ('FINALIZED', 'RESOLVED') +ORDER BY created_at; +``` ### Trace one caller's audit history @@ -132,24 +358,83 @@ WHERE caller_id = $1 ORDER BY occurred_at DESC LIMIT 50; ``` +### Walk a caller's audit chain + +A break between one row's `chain_hash` and the next row's `previous_chain_hash` means a gap +or an altered row. + +```sql +SELECT id, occurred_at, chain_hash, previous_chain_hash +FROM caller_audit_events +WHERE caller_id = $1 +ORDER BY chain_position; +``` + +### Capability grants and denials in the last hour + +```sql +SELECT capability, type, COUNT(*) +FROM caller_audit_events +WHERE occurred_at > now() - interval '1 hour' +GROUP BY capability, type +ORDER BY COUNT(*) DESC; +``` + +### Recent refusal reasons + +```sql +SELECT decision_json->>'reason' AS reason, COUNT(*) +FROM refusal_records +WHERE created_at > now() - interval '24 hours' +GROUP BY reason +ORDER BY COUNT(*) DESC; +``` + ### Check a rate limiter's current state for a caller ```sql SELECT key, count, reset_time, (reset_time > now()) AS window_still_active FROM rate_limit_counters -WHERE key = 'execute:' || $1; +WHERE key = 'execute:' || $1; -- or 'health:' || +``` + +### Confirm a nonce was consumed + +```sql +SELECT nonce, consumed_at, expires_at FROM consumed_nonces WHERE nonce = $1; ``` -### Audit the maker-checker approval queue +### Audit the maker and checker approval queue ```sql +-- Everything still awaiting a checker SELECT policy_name, policy_version, proposed_by, proposed_at, reason FROM pending_policy_changes WHERE status = 'PENDING_APPROVAL' ORDER BY proposed_at; + +-- Full approval history for one policy version +SELECT approved_by, approved_at, content_hash_before, content_hash_after +FROM policy_change_approval_records +WHERE policy_name = $1 AND policy_version = $2 +ORDER BY approved_at; +``` + +### Row counts (quick health snapshot) + +```sql +SELECT 'business_transactions', COUNT(*) FROM business_transactions +UNION ALL SELECT 'executions', COUNT(*) FROM executions +UNION ALL SELECT 'execution_trust_records', COUNT(*) FROM execution_trust_records +UNION ALL SELECT 'execution_intents', COUNT(*) FROM execution_intents +UNION ALL SELECT 'caller_audit_events', COUNT(*) FROM caller_audit_events +UNION ALL SELECT 'execution_audit_events', COUNT(*) FROM execution_audit_events +UNION ALL SELECT 'refusal_records', COUNT(*) FROM refusal_records +UNION ALL SELECT 'pending_policy_changes', COUNT(*) FROM pending_policy_changes +UNION ALL SELECT 'policy_change_approval_records', COUNT(*) FROM policy_change_approval_records; ``` -### Migration-sync verification (files vs. tracking table) +### Migration sync (files against the tracking table) ```bash node -e ' @@ -163,16 +448,27 @@ const fileVersions = fs.readdirSync("supabase/migrations") const { rows } = await pool.query("SELECT version FROM supabase_migrations.schema_migrations ORDER BY version"); const dbVersions = rows.map(r => r.version).sort(); console.log("Files:", fileVersions.length, "Tracked:", dbVersions.length); - console.log("Untracked:", fileVersions.filter(v => !dbVersions.includes(v))); + console.log("In files, not tracked:", fileVersions.filter(v => !dbVersions.includes(v))); + console.log("Tracked, no file:", dbVersions.filter(v => !fileVersions.includes(v))); await pool.end(); })(); ' ``` +### List live tables and their RLS status + +```sql +SELECT c.relname AS table_name, c.relrowsecurity AS rls_enabled +FROM pg_class c +JOIN pg_namespace n ON n.oid = c.relnamespace +WHERE n.nspname = 'public' AND c.relkind = 'r' +ORDER BY c.relname; +``` + ### Check a table's actual CHECK constraints -Several tables' constraints have been widened repeatedly — don't trust the original -`CREATE TABLE` statement alone: +Several constraints were widened more than once, so do not trust the first `CREATE TABLE` +alone: ```sql SELECT conname, pg_get_constraintdef(oid) AS definition @@ -190,10 +486,6 @@ JOIN information_schema.constraint_column_usage ccu WHERE tc.constraint_type = 'FOREIGN KEY' AND ccu.table_name = $1; ``` -See the repo copy, `docs/architecture/DATABASE_SCHEMA_REFERENCE.md`, for the complete -per-table column reference and additional queries (row-count health snapshots, RLS status -listing, nonce-consumption checks). - ## Next diff --git a/examples/tutorials/104-policy-governance-execution-verification/README.md b/examples/tutorials/104-policy-governance-execution-verification/README.md index e713db1c..9c706d9d 100644 --- a/examples/tutorials/104-policy-governance-execution-verification/README.md +++ b/examples/tutorials/104-policy-governance-execution-verification/README.md @@ -23,7 +23,7 @@ Writes to a scratch temp directory (`mkdtempSync`), never the real `policies/` t ## Why This Matters -`docs/CLAIMS.md` §2.34 closed the _detection_ gap: a bypass of Policy Governance would eventually be noticed, at startup or on a 5-minute interval. This tutorial demonstrates the _prevention_ half added afterward: the same bypass simply doesn't execute in the first place, once `PolicyGovernanceExecutionVerifier` is wired in. In production this is feature-flagged (`POLICY_EXECUTION_VERIFICATION_ENFORCED`, default `false`) — every real policy in this system is still `PENDING_APPROVAL` (see `docs/operations/policy-approval-runbook.md`), so turning it on unconditionally today would refuse all of them, not just a genuine bypass. This tutorial builds the same verifier directly, against a scratch policy of its own, specifically so it can show all four outcomes without depending on that real-world state. +`docs/CLAIMS.md` §2.34 closed the _detection_ gap: a bypass of Policy Governance would eventually be noticed, at startup or on a 5-minute interval. This tutorial demonstrates the _prevention_ half added afterward: the same bypass simply doesn't execute in the first place, once `PolicyGovernanceExecutionVerifier` is wired in. In production this is feature-flagged (`POLICY_EXECUTION_VERIFICATION_ENFORCED`, default `false`) — every real policy in this system is still `PENDING_APPROVAL` (see the policy approval runbook, in git history), so turning it on unconditionally today would refuse all of them, not just a genuine bypass. This tutorial builds the same verifier directly, against a scratch policy of its own, specifically so it can show all four outcomes without depending on that real-world state. ## Next Tutorial diff --git a/examples/tutorials/113-kms-key-id-resolution/README.md b/examples/tutorials/113-kms-key-id-resolution/README.md index 3576f600..0eeaaf35 100644 --- a/examples/tutorials/113-kms-key-id-resolution/README.md +++ b/examples/tutorials/113-kms-key-id-resolution/README.md @@ -43,9 +43,9 @@ Before this fix, `KmsSigner.sign("default", data)` called AWS KMS's `SignCommand can make a bare, unprefixed string resolve to a real key, regardless of how the actual KMS key or its aliases are named. Every real signing and verification call failed the moment `KEY_PROVIDER=aws-kms` was first turned on in production -(`docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md`, item 1). The fix +(item 1 of the 2026-09-15 KMS migration troubleshooting guide, in git history). The fix also means `alias/default` **must exist in AWS** for the logical `"default"` key to -resolve at all — see `docs/operations/aws-kms-vercel-oidc-setup-guide.md`, Part 2. +resolve at all — see `docs/site/deployment/aws-kms-signing.mdx`, step 1. ## Next Tutorial diff --git a/examples/tutorials/113-kms-key-id-resolution/run.ts b/examples/tutorials/113-kms-key-id-resolution/run.ts index 86a1db55..0afe3e2e 100644 --- a/examples/tutorials/113-kms-key-id-resolution/run.ts +++ b/examples/tutorials/113-kms-key-id-resolution/run.ts @@ -1,8 +1,8 @@ import { resolveKmsKeyId } from "@parmana/crypto"; // -// docs/VERIFICATION-GAPS.md G-48 / docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md -// item 1: every signing call site in this codebase passes a logical +// docs/VERIFICATION-GAPS.md G-48, and item 1 of the 2026-09-15 KMS migration +// troubleshooting guide (in git history): every signing call site in this codebase passes a logical // keyId ("default", "tenant.acme") to Signer.sign(keyId, data). // KmsSigner used to pass that string straight through as AWS KMS's // KeyId parameter -- which AWS rejects outright, since a bare word is diff --git a/examples/tutorials/114-signing-verification-key-agreement/README.md b/examples/tutorials/114-signing-verification-key-agreement/README.md index 5eb0dd01..e2b332c5 100644 --- a/examples/tutorials/114-signing-verification-key-agreement/README.md +++ b/examples/tutorials/114-signing-verification-key-agreement/README.md @@ -6,7 +6,7 @@ Reproduce the most significant bug found migrating the gateway signing key to AW signing and verification silently resolving _different_ keys, producing exactly the "ambiguous outcome" failure this codebase's fail-closed design otherwise works hard to avoid — and demonstrate the real fix. Mirrors `docs/VERIFICATION-GAPS.md` G-48 and -`docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md` item 7. +item 7 of the 2026-09-15 KMS migration troubleshooting guide (in git history). ## What You'll Learn diff --git a/examples/tutorials/114-signing-verification-key-agreement/run.ts b/examples/tutorials/114-signing-verification-key-agreement/run.ts index 90162f60..737472e2 100644 --- a/examples/tutorials/114-signing-verification-key-agreement/run.ts +++ b/examples/tutorials/114-signing-verification-key-agreement/run.ts @@ -4,8 +4,8 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; // -// docs/VERIFICATION-GAPS.md G-48 / docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md -// item 7 -- the most significant bug found migrating the gateway signing +// docs/VERIFICATION-GAPS.md G-48, and item 7 of the 2026-09-15 KMS migration +// troubleshooting guide (in git history) -- the most significant bug found migrating the gateway signing // key to AWS KMS, and the one that produced the classic "ambiguous // outcome" failure: a real, legitimate authorization failed // verification with [signatureVerified, businessTransactionHashMatches, @@ -56,8 +56,8 @@ writeKeyPair("default"); // A SEPARATE, never-materialized-to-this-directory keypair, standing in // for "a stale key file left over from before a migration" -- the exact -// scenario found in production (docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md -// item 4's fix kept an old default.public.pem present on disk as a side +// scenario found in production (item 4 of the 2026-09-15 KMS migration troubleshooting +// guide, in git history: its fix kept an old default.public.pem present on disk as a side // effect, which is exactly what this "stale" key represents here). const stalePair = generateKeyPairSync("ed25519"); diff --git a/examples/tutorials/115-per-limiter-rate-limit-stores/README.md b/examples/tutorials/115-per-limiter-rate-limit-stores/README.md index c46106b4..80f85966 100644 --- a/examples/tutorials/115-per-limiter-rate-limit-stores/README.md +++ b/examples/tutorials/115-per-limiter-rate-limit-stores/README.md @@ -7,7 +7,7 @@ Reproduce a real, pre-existing bug found and fixed the same night as the KMS mig between the `/execute` and `/health`,`/ready` rate limiters, which `express-rate-limit`'s own documented contract disallows. Mirrors `docs/VERIFICATION-GAPS.md` G-49 and -`docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md` item 5 — **including +item 5 of the 2026-09-15 KMS migration troubleshooting guide (in git history) — **including a correction to the initial diagnosis**, kept visible rather than silently rewritten. ## What You'll Learn @@ -55,7 +55,7 @@ evidence that it caused the failure.** This project's own documentation initiall conflated the two, stated the causal claim as fact, and only caught the error while building this tutorial and checking the claim against the library's actual source. The correction is kept visible in `docs/VERIFICATION-GAPS.md` G-49, -`docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md`, and here — not +the 2026-09-15 KMS migration troubleshooting guide (in git history), and here — not quietly rewritten — because the mistake itself (assuming causation from proximity in a log) is exactly the kind of thing worth remembering for the next incident. diff --git a/examples/tutorials/115-per-limiter-rate-limit-stores/run.ts b/examples/tutorials/115-per-limiter-rate-limit-stores/run.ts index 1a3d4d6c..c453365d 100644 --- a/examples/tutorials/115-per-limiter-rate-limit-stores/run.ts +++ b/examples/tutorials/115-per-limiter-rate-limit-stores/run.ts @@ -1,8 +1,8 @@ import type { Pool } from "pg"; // -// docs/VERIFICATION-GAPS.md G-49 / docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md -// item 5: createApp() used to pass ONE shared PostgresRateLimitStore +// docs/VERIFICATION-GAPS.md G-49, and item 5 of the 2026-09-15 KMS migration +// troubleshooting guide (in git history): createApp() used to pass ONE shared PostgresRateLimitStore // instance to both the /execute and /health,/ready rate limiters -- // express-rate-limit v8 explicitly documents that a Store instance // must not back more than one limiter. @@ -24,7 +24,7 @@ import type { Pool } from "pg"; // express-rate-limit version, or under stricter `validate` config) -- // just not the mechanism that was actually crashing requests that // night. Corrected in docs/VERIFICATION-GAPS.md G-49 and -// docs/operations/2026-09-15-kms-migration-troubleshooting-guide.md at +// the 2026-09-15 KMS migration troubleshooting guide (in git history) at // the same time this tutorial was corrected. // // Hermetic: a minimal fake `pg.Pool` stands in for a real database diff --git a/examples/tutorials/117-maker-checker-one-shot-scripts/run.ts b/examples/tutorials/117-maker-checker-one-shot-scripts/run.ts index d976b48b..523ea2f4 100644 --- a/examples/tutorials/117-maker-checker-one-shot-scripts/run.ts +++ b/examples/tutorials/117-maker-checker-one-shot-scripts/run.ts @@ -16,7 +16,7 @@ import { MemoryNonceStore } from "@parmana/envelope-verifier"; import { MemoryPolicyChangeApprovalRecordRepository } from "@parmana/storage"; // -// docs/operations/policy-governance-developer-guide.md §5: chaining +// "Sign and submit in one step" in docs/site/guides/policy-lifecycle-and-approvals.mdx: chaining // sign-then-submit by hand across the 120-second step-up window is // fragile in practice -- shell-quoting mangles the JSON body, a human // relay between the two steps burns the window, a stale credential diff --git a/examples/tutorials/95-approval-verifier-generic/run.ts b/examples/tutorials/95-approval-verifier-generic/run.ts index fbb579d8..35e234e4 100644 --- a/examples/tutorials/95-approval-verifier-generic/run.ts +++ b/examples/tutorials/95-approval-verifier-generic/run.ts @@ -13,7 +13,7 @@ import type { ApprovalPayload, SignedApproval } from "@parmana/shared"; // HubSpot-specific pipeline (HubSpotSignalStateVerifier). This // tutorial exercises ApprovalVerifier itself, generically, connector- // agnostic -- the deterministic verification algorithm frozen in -// docs/architecture/phase3a-authorization-artifact-design.md, with +// the Phase 3A approval artifact design (in git history), with // its full per-check breakdown (`result.checks`), independent // verifier instances, and durable cross-process replay protection. A // regression that breaks the shared component for a non-HubSpot shape diff --git a/packages/api/src/bootstrap/createApprovalIssuerRegistry.ts b/packages/api/src/bootstrap/createApprovalIssuerRegistry.ts index f821047c..3f4a1ebe 100644 --- a/packages/api/src/bootstrap/createApprovalIssuerRegistry.ts +++ b/packages/api/src/bootstrap/createApprovalIssuerRegistry.ts @@ -34,7 +34,7 @@ export interface ConfiguredApprovalIssuer { * operational model already established for trusted connector * identities. A self-service onboarding flow for approvers is * deliberately out of scope (see - * docs/architecture/phase3a-authorization-artifact-design.md §17, + * §17 of the Phase 3A approval artifact design (in git history), * Open Question #1) -- this list only verifies against whatever * entries it is deployed with. * diff --git a/packages/api/tests/integration/execution-failure.integration.test.ts b/packages/api/tests/integration/execution-failure.integration.test.ts index 0e2729be..69cbf014 100644 --- a/packages/api/tests/integration/execution-failure.integration.test.ts +++ b/packages/api/tests/integration/execution-failure.integration.test.ts @@ -23,7 +23,7 @@ import { createInspectableExecutionSystem } from "../bootstrap/createInspectable * thrown error to a generic `{ error: "Internal Server Error" }` 500, * by design, specifically so internal failure detail is never leaked to * a caller — confirmed empirically before rewriting this file, not - * assumed. See docs/architecture/phase2d-execution-failure-testing.md + * assumed. See the Phase 2D execution failure testing record (in git history) * for the full investigation. * * Since G-63 a connector failure no longer reaches errorHandler diff --git a/packages/approval/src/ApprovalIssuerRegistry.ts b/packages/approval/src/ApprovalIssuerRegistry.ts index 80bc035d..dc00480e 100644 --- a/packages/approval/src/ApprovalIssuerRegistry.ts +++ b/packages/approval/src/ApprovalIssuerRegistry.ts @@ -12,7 +12,7 @@ import type { KeyObject } from "node:crypto"; * invalidates every artifact they ever signed, a coarser but simpler * and operationally real guarantee than tracking individual * approvalIds. Per-artifact revocation remains a possible future - * refinement (see docs/architecture/phase3c-approval-artifact.md, + * refinement (see the Phase 3C approval artifact record, in git history, * Remaining Limitations) -- not implemented here. */ export interface TrustedApprovalIssuer { diff --git a/packages/approval/src/ApprovalScopeEvaluator.ts b/packages/approval/src/ApprovalScopeEvaluator.ts index 19ad3bf7..fe50faa7 100644 --- a/packages/approval/src/ApprovalScopeEvaluator.ts +++ b/packages/approval/src/ApprovalScopeEvaluator.ts @@ -5,8 +5,8 @@ import type { ApprovalScope } from "@parmana/shared"; * an ApprovalScope's bound. * * A small, self-contained implementation of exactly the six - * comparators docs/architecture/phase3a-authorization-artifact-design.md - * §7.1 restricts ApprovalScope.comparator to (eq, lte, gte, lt, gt, + * comparators §7.1 of the Phase 3A approval artifact design (in + * git history) restricts ApprovalScope.comparator to (eq, lte, gte, lt, gt, * between) -- deliberately not importing @parmana/policy's * OperatorEvaluator, which supports a much larger operator set for * arbitrary policy conditions and represents "between" as a 2-element diff --git a/packages/approval/src/ApprovalVerifier.ts b/packages/approval/src/ApprovalVerifier.ts index 1ab72a74..8db239f2 100644 --- a/packages/approval/src/ApprovalVerifier.ts +++ b/packages/approval/src/ApprovalVerifier.ts @@ -68,9 +68,9 @@ const FAILED_BEFORE_SIGNATURE: ApprovalVerificationResult = { }; /** - * Verifies a SignedApproval per - * docs/architecture/phase3a-authorization-artifact-design.md §10's - * frozen algorithm: deterministic, fixed order, no early return + * Verifies a SignedApproval per the frozen algorithm in + * §10 of the Phase 3A approval artifact design (in git history): + * deterministic, fixed order, no early return * between independent checks (mirroring AuthorizationVerifier.verify()'s * own no-timing-oracle discipline), nonce consumption attempted last * and only when every other check has already independently passed diff --git a/packages/approval/tests/unit/ApprovalVerifier.test.ts b/packages/approval/tests/unit/ApprovalVerifier.test.ts index 33f3c21e..e62d6c99 100644 --- a/packages/approval/tests/unit/ApprovalVerifier.test.ts +++ b/packages/approval/tests/unit/ApprovalVerifier.test.ts @@ -12,7 +12,7 @@ import { StaticApprovalIssuerRegistry } from "../../src/ApprovalIssuerRegistry.j /** * TD-23 closure, Phase 3C. Implements the exact deterministic * verification algorithm frozen in - * docs/architecture/phase3a-authorization-artifact-design.md §10. + * §10 of the Phase 3A approval artifact design (in git history). */ const crypto = CryptoBootstrap.create(); diff --git a/packages/connector-hubspot/src/HubSpotSignalStateVerifier.ts b/packages/connector-hubspot/src/HubSpotSignalStateVerifier.ts index e4942b39..d5a68e96 100644 --- a/packages/connector-hubspot/src/HubSpotSignalStateVerifier.ts +++ b/packages/connector-hubspot/src/HubSpotSignalStateVerifier.ts @@ -41,7 +41,7 @@ export interface HubSpotSignalStateVerifierOptions { * that field). When supplied, an over-threshold amount change's * preAuthorizedForAmountChange claim is verified against a real, * independently-issued SignedApproval carried in - * signals.approvalArtifact (docs/architecture/phase3a-authorization-artifact-design.md), + * signals.approvalArtifact (the Phase 3A approval artifact design, in git history), * rather than trusted from the caller -- closing the gap where a * caller could declare preAuthorizedForAmountChange: true with * nothing behind it. diff --git a/packages/envelope-verifier/README.md b/packages/envelope-verifier/README.md index c2456b54..fc38faea 100644 --- a/packages/envelope-verifier/README.md +++ b/packages/envelope-verifier/README.md @@ -122,7 +122,7 @@ so a durable store never needs to retain a nonce longer than - Migrating a deployment from one algorithm to another (e.g. Ed25519 to ML-DSA-65) while retaining the ability to verify authorizations issued before the switch is not currently supported — see - `docs/architecture/post-quantum-cryptography.md`. + the post quantum cryptography design note (in git history). ## Claims diff --git a/packages/shared/src/domain/approval-artifact.ts b/packages/shared/src/domain/approval-artifact.ts index 5c67b064..bd2b60b1 100644 --- a/packages/shared/src/domain/approval-artifact.ts +++ b/packages/shared/src/domain/approval-artifact.ts @@ -16,8 +16,8 @@ import type { Signature } from "./signature.js"; * Decision, not that an independent business authority approved an * exception to it. * - * Design frozen in docs/architecture/phase3a-authorization-artifact-design.md - * (§7). This file implements that specification's schema exactly -- + * Design frozen in §7 of the Phase 3A approval artifact design (in git + * history). This file implements that specification's schema exactly -- * field names, types, and mandatory/optional status are not * reinterpreted here. * diff --git a/packages/storage/src/supabase/SupabaseApprovalNonceStore.ts b/packages/storage/src/supabase/SupabaseApprovalNonceStore.ts index 466fc8c5..b26689dd 100644 --- a/packages/storage/src/supabase/SupabaseApprovalNonceStore.ts +++ b/packages/storage/src/supabase/SupabaseApprovalNonceStore.ts @@ -11,7 +11,7 @@ import { isUniqueViolation } from "../errors/PostgresErrorCodes.js"; * Deliberately backed by its own table (consumed_approval_nonces), * separate from consumed_nonces (SupabaseNonceStore, ExecutionGateway's * own Authorization-envelope replay protection) -- per - * docs/architecture/phase3a-authorization-artifact-design.md §13, an + * §13 of the Phase 3A approval artifact design (in git history), an * Approval Artifact's nonce and a Gateway authorization envelope's * nonce are distinct trust domains issued by distinct parties (an * external business approver vs. Parmana's own runtime); sharing one diff --git a/tests/architecture/documentation-references.test.ts b/tests/architecture/documentation-references.test.ts index 44a11dfe..a63c015e 100644 --- a/tests/architecture/documentation-references.test.ts +++ b/tests/architecture/documentation-references.test.ts @@ -19,9 +19,7 @@ const repoRoot = dirname(dirname(dirname(fileURLToPath(import.meta.url)))); const docsToCheck = [ "docs/architecture/system-architecture.md", - "docs/architecture/execution-walkthrough.md", "docs/developer/extending-parmana.md", - "docs/architecture/execution-pipeline-report.md", "docs/architecture/repository-invariants.md", "docs/CLAIMS.md", ]; diff --git a/tests/architecture/execution-boundary.test.ts b/tests/architecture/execution-boundary.test.ts index 9d196554..88d30959 100644 --- a/tests/architecture/execution-boundary.test.ts +++ b/tests/architecture/execution-boundary.test.ts @@ -115,10 +115,10 @@ describe("execution boundary — exactly one production execution pipeline", () // mode it returns undefined and "vendor-payment" is not registered at // all, so payments:execute fails closed like any other unimplemented // capability (Phase 2A — see - // docs/architecture/phase2a-production-connectors.md; the prior + // the Phase 2A production connectors record (in git history); the prior // unconditional-registration gap is documented in - // docs/architecture/execution-pipeline-report.md §8 and - // docs/architecture/repository-certification.md §4.9/TD-1 as the + // §8 of the Phase 1E execution pipeline report (in git history) and + // §4.9/TD-1 of the repository certification record (in git history) as the // finding this fixed). Still the one class outside execution-gateway // allowed to `implement Connector`; when it is constructed, it flows // through the full ExecutionGateway pipeline like any other. diff --git a/tests/architecture/terminology-guard.test.ts b/tests/architecture/terminology-guard.test.ts index 9673c0a5..e442a614 100644 --- a/tests/architecture/terminology-guard.test.ts +++ b/tests/architecture/terminology-guard.test.ts @@ -9,9 +9,9 @@ import { describe, expect, it } from "vitest"; * (.github/workflows/ci.yml, "Guard against retired terminology" step). * * The guard itself is inline bash with no test of its own — this is what - * let TD-2 (repository-certification.md TD-2) go unnoticed: the guard - * silently failed against the committed tree with nobody finding out until - * someone happened to run it by hand. This file re-implements the guard's + * let TD-2 (the repository certification record, in git history) go + * unnoticed: the guard silently failed against the committed tree with + * nobody finding out until someone happened to run it by hand. This file re-implements the guard's * exact matching logic in TypeScript so its behavior is asserted on every * `npm test` run, not just when CI happens to execute the workflow step. * @@ -37,9 +37,6 @@ const EXCLUDED_BASENAMES = new Set([ "VERIFICATION-GAPS.md", "how-parmana-thinks.mdx", "execution-authorization.mdx", - "repository-certification.md", - "phase2b-technical-debt-assessment.md", - "phase2c-terminology-guard.md", "changelog.mdx", "ci.yml", // This file: its own matching pattern and test fixtures necessarily @@ -100,7 +97,7 @@ describe("CI terminology guard (Phase 2C regression coverage)", () => { const activeDocs = [ "README.md", "docs/architecture/system-architecture.md", - "docs/architecture/execution-walkthrough.md", + "docs/site/architecture/execution-pipeline.mdx", "docs/architecture/repository-invariants.md", "docs/developer/extending-parmana.md", ]; @@ -115,12 +112,7 @@ describe("CI terminology guard (Phase 2C regression coverage)", () => { }); it("historical self-narration files are excluded and legitimately contain the phrase", () => { - const historical = [ - "docs/VERIFICATION-GAPS.md", - "docs/architecture/repository-certification.md", - "docs/architecture/phase2b-technical-debt-assessment.md", - "docs/site/changelog.mdx", - ]; + const historical = ["docs/VERIFICATION-GAPS.md", "docs/site/changelog.mdx"]; for (const doc of historical) { const basename = doc.split("/").pop()!;