@emilia-protocol/gate 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +252 -0
- package/action-control-manifest.js +3 -268
- package/action-escrow-custodian.js +3 -394
- package/action-escrow-evidence.js +3 -858
- package/action-escrow-package.js +3 -1668
- package/action-escrow-postgres.js +3 -337
- package/action-escrow-state.js +3 -396
- package/action-escrow-verifiers.js +3 -331
- package/action-escrow.js +3 -2447
- package/action-packs.js +3 -140
- package/adapters/_kit.js +3 -104
- package/adapters/aws.js +3 -108
- package/adapters/cloudflare.js +3 -54
- package/adapters/gcp.js +3 -70
- package/adapters/github.js +3 -109
- package/adapters/jira.js +3 -55
- package/adapters/k8s.js +3 -70
- package/adapters/linear.js +3 -55
- package/adapters/salesforce.js +3 -55
- package/adapters/stripe.js +3 -81
- package/adapters/supabase.js +3 -121
- package/adapters/terraform.js +3 -59
- package/adapters/vercel.js +3 -84
- package/aec-execution.js +3 -312
- package/breakglass.js +3 -582
- package/capability-receipt.js +4 -0
- package/cf1-conformance.js +3 -106
- package/challenge-store.js +3 -69
- package/control-plane.js +3 -340
- package/coverage.js +3 -721
- package/demo.mjs +1 -1
- package/deployment-attestation.js +3 -247
- package/dist/action-control-manifest.d.ts +82 -0
- package/dist/action-control-manifest.d.ts.map +1 -0
- package/dist/action-control-manifest.js +422 -0
- package/dist/action-control-manifest.js.map +1 -0
- package/dist/action-escrow-custodian.d.ts +74 -0
- package/dist/action-escrow-custodian.d.ts.map +1 -0
- package/dist/action-escrow-custodian.js +363 -0
- package/dist/action-escrow-custodian.js.map +1 -0
- package/dist/action-escrow-evidence.d.ts +130 -0
- package/dist/action-escrow-evidence.d.ts.map +1 -0
- package/dist/action-escrow-evidence.js +1012 -0
- package/dist/action-escrow-evidence.js.map +1 -0
- package/dist/action-escrow-package.d.ts +10 -0
- package/dist/action-escrow-package.d.ts.map +1 -0
- package/dist/action-escrow-package.js +1583 -0
- package/dist/action-escrow-package.js.map +1 -0
- package/dist/action-escrow-postgres.d.ts +62 -0
- package/dist/action-escrow-postgres.d.ts.map +1 -0
- package/dist/action-escrow-postgres.js +339 -0
- package/dist/action-escrow-postgres.js.map +1 -0
- package/dist/action-escrow-state.d.ts +141 -0
- package/dist/action-escrow-state.d.ts.map +1 -0
- package/dist/action-escrow-state.js +413 -0
- package/dist/action-escrow-state.js.map +1 -0
- package/dist/action-escrow-verifiers.d.ts +65 -0
- package/dist/action-escrow-verifiers.d.ts.map +1 -0
- package/dist/action-escrow-verifiers.js +421 -0
- package/dist/action-escrow-verifiers.js.map +1 -0
- package/dist/action-escrow.d.ts +112 -0
- package/dist/action-escrow.d.ts.map +1 -0
- package/dist/action-escrow.js +2661 -0
- package/dist/action-escrow.js.map +1 -0
- package/dist/action-packs.d.ts +54 -0
- package/dist/action-packs.d.ts.map +1 -0
- package/dist/action-packs.js +143 -0
- package/dist/action-packs.js.map +1 -0
- package/dist/adapters/_kit.d.ts +50 -0
- package/dist/adapters/_kit.d.ts.map +1 -0
- package/dist/adapters/_kit.js +98 -0
- package/dist/adapters/_kit.js.map +1 -0
- package/dist/adapters/aws.d.ts +158 -0
- package/dist/adapters/aws.d.ts.map +1 -0
- package/dist/adapters/aws.js +136 -0
- package/dist/adapters/aws.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts +116 -0
- package/dist/adapters/cloudflare.d.ts.map +1 -0
- package/dist/adapters/cloudflare.js +75 -0
- package/dist/adapters/cloudflare.js.map +1 -0
- package/dist/adapters/gcp.d.ts +139 -0
- package/dist/adapters/gcp.d.ts.map +1 -0
- package/dist/adapters/gcp.js +69 -0
- package/dist/adapters/gcp.js.map +1 -0
- package/dist/adapters/github.d.ts +123 -0
- package/dist/adapters/github.d.ts.map +1 -0
- package/dist/adapters/github.js +105 -0
- package/dist/adapters/github.js.map +1 -0
- package/dist/adapters/jira.d.ts +109 -0
- package/dist/adapters/jira.d.ts.map +1 -0
- package/dist/adapters/jira.js +54 -0
- package/dist/adapters/jira.js.map +1 -0
- package/dist/adapters/k8s.d.ts +139 -0
- package/dist/adapters/k8s.d.ts.map +1 -0
- package/dist/adapters/k8s.js +69 -0
- package/dist/adapters/k8s.js.map +1 -0
- package/dist/adapters/linear.d.ts +109 -0
- package/dist/adapters/linear.d.ts.map +1 -0
- package/dist/adapters/linear.js +54 -0
- package/dist/adapters/linear.js.map +1 -0
- package/dist/adapters/salesforce.d.ts +109 -0
- package/dist/adapters/salesforce.d.ts.map +1 -0
- package/dist/adapters/salesforce.js +54 -0
- package/dist/adapters/salesforce.js.map +1 -0
- package/dist/adapters/stripe.d.ts +116 -0
- package/dist/adapters/stripe.d.ts.map +1 -0
- package/dist/adapters/stripe.js +77 -0
- package/dist/adapters/stripe.js.map +1 -0
- package/dist/adapters/supabase.d.ts +127 -0
- package/dist/adapters/supabase.d.ts.map +1 -0
- package/dist/adapters/supabase.js +112 -0
- package/dist/adapters/supabase.js.map +1 -0
- package/dist/adapters/terraform.d.ts +109 -0
- package/dist/adapters/terraform.d.ts.map +1 -0
- package/dist/adapters/terraform.js +58 -0
- package/dist/adapters/terraform.js.map +1 -0
- package/dist/adapters/vercel.d.ts +114 -0
- package/dist/adapters/vercel.d.ts.map +1 -0
- package/dist/adapters/vercel.js +81 -0
- package/dist/adapters/vercel.js.map +1 -0
- package/dist/aec-execution.d.ts +107 -0
- package/dist/aec-execution.d.ts.map +1 -0
- package/dist/aec-execution.js +334 -0
- package/dist/aec-execution.js.map +1 -0
- package/dist/breakglass.d.ts +205 -0
- package/dist/breakglass.d.ts.map +1 -0
- package/dist/breakglass.js +627 -0
- package/dist/breakglass.js.map +1 -0
- package/dist/capability-receipt.d.ts +601 -0
- package/dist/capability-receipt.d.ts.map +1 -0
- package/dist/capability-receipt.js +1177 -0
- package/dist/capability-receipt.js.map +1 -0
- package/dist/cf1-conformance.d.ts +83 -0
- package/dist/cf1-conformance.d.ts.map +1 -0
- package/dist/cf1-conformance.js +94 -0
- package/dist/cf1-conformance.js.map +1 -0
- package/dist/challenge-store.d.ts +20 -0
- package/dist/challenge-store.d.ts.map +1 -0
- package/dist/challenge-store.js +62 -0
- package/dist/challenge-store.js.map +1 -0
- package/dist/control-plane.d.ts +136 -0
- package/dist/control-plane.d.ts.map +1 -0
- package/dist/control-plane.js +349 -0
- package/dist/control-plane.js.map +1 -0
- package/dist/coverage.d.ts +156 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.js +768 -0
- package/dist/coverage.js.map +1 -0
- package/dist/deployment-attestation.d.ts +93 -0
- package/dist/deployment-attestation.d.ts.map +1 -0
- package/dist/deployment-attestation.js +262 -0
- package/dist/deployment-attestation.js.map +1 -0
- package/dist/eg1-conformance.d.ts +286 -0
- package/dist/eg1-conformance.d.ts.map +1 -0
- package/dist/eg1-conformance.js +379 -0
- package/dist/eg1-conformance.js.map +1 -0
- package/dist/enterprise.d.ts +88 -0
- package/dist/enterprise.d.ts.map +1 -0
- package/dist/enterprise.js +194 -0
- package/dist/enterprise.js.map +1 -0
- package/dist/evidence-postgres.d.ts +123 -0
- package/dist/evidence-postgres.d.ts.map +1 -0
- package/dist/evidence-postgres.js +343 -0
- package/dist/evidence-postgres.js.map +1 -0
- package/dist/evidence.d.ts +113 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +369 -0
- package/dist/evidence.js.map +1 -0
- package/dist/execution-binding.d.ts +49 -0
- package/dist/execution-binding.d.ts.map +1 -0
- package/dist/execution-binding.js +243 -0
- package/dist/execution-binding.js.map +1 -0
- package/dist/formal-runtime-map.d.ts +42 -0
- package/dist/formal-runtime-map.d.ts.map +1 -0
- package/dist/formal-runtime-map.js +23 -0
- package/dist/formal-runtime-map.js.map +1 -0
- package/dist/index.d.ts +1002 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1736 -0
- package/dist/index.js.map +1 -0
- package/dist/key-registry.d.ts +60 -0
- package/dist/key-registry.d.ts.map +1 -0
- package/dist/key-registry.js +144 -0
- package/dist/key-registry.js.map +1 -0
- package/dist/mcp.d.ts +53 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +112 -0
- package/dist/mcp.js.map +1 -0
- package/dist/metering.d.ts +80 -0
- package/dist/metering.d.ts.map +1 -0
- package/dist/metering.js +227 -0
- package/dist/metering.js.map +1 -0
- package/dist/metrics.d.ts +68 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +242 -0
- package/dist/metrics.js.map +1 -0
- package/dist/network-witness.d.ts +167 -0
- package/dist/network-witness.d.ts.map +1 -0
- package/dist/network-witness.js +520 -0
- package/dist/network-witness.js.map +1 -0
- package/dist/protocol-state.d.ts +141 -0
- package/dist/protocol-state.d.ts.map +1 -0
- package/dist/protocol-state.js +106 -0
- package/dist/protocol-state.js.map +1 -0
- package/dist/receipt-program.d.ts +59 -0
- package/dist/receipt-program.d.ts.map +1 -0
- package/dist/receipt-program.js +1053 -0
- package/dist/receipt-program.js.map +1 -0
- package/dist/reliance-kernel.d.ts +66 -0
- package/dist/reliance-kernel.d.ts.map +1 -0
- package/dist/reliance-kernel.js +103 -0
- package/dist/reliance-kernel.js.map +1 -0
- package/dist/reliance-packet.d.ts +67 -0
- package/dist/reliance-packet.d.ts.map +1 -0
- package/dist/reliance-packet.js +176 -0
- package/dist/reliance-packet.js.map +1 -0
- package/dist/remedy-program-postgres.d.ts +37 -0
- package/dist/remedy-program-postgres.d.ts.map +1 -0
- package/dist/remedy-program-postgres.js +291 -0
- package/dist/remedy-program-postgres.js.map +1 -0
- package/dist/remedy-program-receipt.d.ts +97 -0
- package/dist/remedy-program-receipt.d.ts.map +1 -0
- package/dist/remedy-program-receipt.js +817 -0
- package/dist/remedy-program-receipt.js.map +1 -0
- package/dist/remedy-program.d.ts +76 -0
- package/dist/remedy-program.d.ts.map +1 -0
- package/dist/remedy-program.js +1346 -0
- package/dist/remedy-program.js.map +1 -0
- package/dist/reports/art14.d.ts +101 -0
- package/dist/reports/art14.d.ts.map +1 -0
- package/dist/reports/art14.js +340 -0
- package/dist/reports/art14.js.map +1 -0
- package/dist/reports/assurance-package.d.ts +197 -0
- package/dist/reports/assurance-package.d.ts.map +1 -0
- package/dist/reports/assurance-package.js +313 -0
- package/dist/reports/assurance-package.js.map +1 -0
- package/dist/reports/auditor-workpaper.d.ts +194 -0
- package/dist/reports/auditor-workpaper.d.ts.map +1 -0
- package/dist/reports/auditor-workpaper.js +545 -0
- package/dist/reports/auditor-workpaper.js.map +1 -0
- package/dist/reports/external-verification.d.ts +76 -0
- package/dist/reports/external-verification.d.ts.map +1 -0
- package/dist/reports/external-verification.js +209 -0
- package/dist/reports/external-verification.js.map +1 -0
- package/dist/reports/reperform.d.ts +122 -0
- package/dist/reports/reperform.d.ts.map +1 -0
- package/dist/reports/reperform.js +432 -0
- package/dist/reports/reperform.js.map +1 -0
- package/dist/reports/underwriter.d.ts +127 -0
- package/dist/reports/underwriter.d.ts.map +1 -0
- package/dist/reports/underwriter.js +348 -0
- package/dist/reports/underwriter.js.map +1 -0
- package/dist/retention.d.ts +88 -0
- package/dist/retention.d.ts.map +1 -0
- package/dist/retention.js +90 -0
- package/dist/retention.js.map +1 -0
- package/dist/roster.d.ts +113 -0
- package/dist/roster.d.ts.map +1 -0
- package/dist/roster.js +261 -0
- package/dist/roster.js.map +1 -0
- package/dist/runtime-monitor.d.ts +132 -0
- package/dist/runtime-monitor.d.ts.map +1 -0
- package/dist/runtime-monitor.js +197 -0
- package/dist/runtime-monitor.js.map +1 -0
- package/dist/settlement.d.ts +68 -0
- package/dist/settlement.d.ts.map +1 -0
- package/dist/settlement.js +306 -0
- package/dist/settlement.js.map +1 -0
- package/dist/siem.d.ts +261 -0
- package/dist/siem.d.ts.map +1 -0
- package/dist/siem.js +247 -0
- package/dist/siem.js.map +1 -0
- package/dist/store-postgres.d.ts +107 -0
- package/dist/store-postgres.d.ts.map +1 -0
- package/dist/store-postgres.js +152 -0
- package/dist/store-postgres.js.map +1 -0
- package/dist/store.d.ts +95 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +202 -0
- package/dist/store.js.map +1 -0
- package/dist/strict-json.d.ts +14 -0
- package/dist/strict-json.d.ts.map +1 -0
- package/dist/strict-json.js +132 -0
- package/dist/strict-json.js.map +1 -0
- package/dist/trust-program-adapters.d.ts +122 -0
- package/dist/trust-program-adapters.d.ts.map +1 -0
- package/dist/trust-program-adapters.js +802 -0
- package/dist/trust-program-adapters.js.map +1 -0
- package/dist/trust-program-postgres.d.ts +41 -0
- package/dist/trust-program-postgres.d.ts.map +1 -0
- package/dist/trust-program-postgres.js +346 -0
- package/dist/trust-program-postgres.js.map +1 -0
- package/dist/trust-program-revocation.d.ts +206 -0
- package/dist/trust-program-revocation.d.ts.map +1 -0
- package/dist/trust-program-revocation.js +600 -0
- package/dist/trust-program-revocation.js.map +1 -0
- package/dist/trust-program.d.ts +197 -0
- package/dist/trust-program.d.ts.map +1 -0
- package/dist/trust-program.js +1351 -0
- package/dist/trust-program.js.map +1 -0
- package/dist/witness-postgres.d.ts +43 -0
- package/dist/witness-postgres.d.ts.map +1 -0
- package/dist/witness-postgres.js +92 -0
- package/dist/witness-postgres.js.map +1 -0
- package/dist/zk-range-proof.d.ts +78 -0
- package/dist/zk-range-proof.d.ts.map +1 -0
- package/dist/zk-range-proof.js +228 -0
- package/dist/zk-range-proof.js.map +1 -0
- package/eg1-conformance.js +3 -404
- package/enterprise.js +3 -180
- package/ep-assure.mjs +1 -0
- package/evidence-postgres.js +3 -356
- package/evidence.js +4 -360
- package/execution-binding.js +3 -212
- package/formal-runtime-map.js +4 -0
- package/index.js +4 -1249
- package/key-registry.js +3 -134
- package/mcp.js +3 -109
- package/metering.js +3 -226
- package/metrics.js +3 -231
- package/network-witness.js +3 -499
- package/package.json +31 -2
- package/receipt-program.js +4 -0
- package/reliance-kernel.js +3 -112
- package/reliance-packet.js +3 -175
- package/remedy-program-postgres.js +4 -0
- package/remedy-program-receipt.js +4 -0
- package/remedy-program.js +4 -0
- package/reports/art14.js +0 -0
- package/reports/assurance-package.js +2 -312
- package/reports/auditor-workpaper.js +3 -537
- package/reports/external-verification.js +3 -222
- package/reports/reperform.js +3 -424
- package/reports/underwriter.js +3 -339
- package/retention.js +3 -84
- package/roster.js +3 -264
- package/runtime-monitor.js +4 -0
- package/settlement.js +3 -299
- package/siem.js +3 -235
- package/src/action-control-manifest.ts +424 -0
- package/src/action-escrow-custodian.ts +427 -0
- package/src/action-escrow-evidence.ts +1178 -0
- package/src/action-escrow-package.ts +1821 -0
- package/src/action-escrow-postgres.ts +366 -0
- package/src/action-escrow-state.ts +498 -0
- package/src/action-escrow-verifiers.ts +484 -0
- package/src/action-escrow.ts +3039 -0
- package/src/action-packs.ts +170 -0
- package/src/adapters/_kit.ts +117 -0
- package/src/adapters/aws.ts +142 -0
- package/src/adapters/cloudflare.ts +78 -0
- package/src/adapters/gcp.ts +71 -0
- package/src/adapters/github.ts +110 -0
- package/src/adapters/jira.ts +56 -0
- package/src/adapters/k8s.ts +71 -0
- package/src/adapters/linear.ts +56 -0
- package/src/adapters/salesforce.ts +56 -0
- package/src/adapters/stripe.ts +82 -0
- package/src/adapters/supabase.ts +122 -0
- package/src/adapters/terraform.ts +60 -0
- package/src/adapters/vercel.ts +85 -0
- package/src/aec-execution.ts +354 -0
- package/src/breakglass.ts +647 -0
- package/src/capability-receipt.ts +1258 -0
- package/src/cf1-conformance.ts +120 -0
- package/src/challenge-store.ts +70 -0
- package/src/control-plane.ts +374 -0
- package/src/coverage.ts +832 -0
- package/src/deployment-attestation.ts +259 -0
- package/src/eg1-conformance.ts +471 -0
- package/src/enterprise.ts +195 -0
- package/src/evidence-postgres.ts +374 -0
- package/src/evidence.ts +367 -0
- package/src/execution-binding.ts +236 -0
- package/src/formal-runtime-map.ts +24 -0
- package/src/index.ts +2122 -0
- package/src/key-registry.ts +150 -0
- package/src/mcp.ts +120 -0
- package/src/metering.ts +237 -0
- package/src/metrics.ts +232 -0
- package/src/network-witness.ts +585 -0
- package/src/protocol-state.ts +143 -0
- package/src/protocol-state.typecheck.ts +94 -0
- package/src/receipt-program.ts +1105 -0
- package/src/reliance-kernel.ts +119 -0
- package/src/reliance-packet.ts +206 -0
- package/src/remedy-program-postgres.ts +367 -0
- package/src/remedy-program-receipt.ts +891 -0
- package/src/remedy-program.ts +1388 -0
- package/src/reports/art14.ts +361 -0
- package/src/reports/assurance-package.ts +330 -0
- package/src/reports/auditor-workpaper.ts +558 -0
- package/src/reports/external-verification.ts +232 -0
- package/src/reports/reperform.ts +441 -0
- package/src/reports/underwriter.ts +356 -0
- package/src/retention.ts +100 -0
- package/src/roster.ts +273 -0
- package/src/runtime-monitor.ts +217 -0
- package/src/settlement.ts +313 -0
- package/src/siem.ts +247 -0
- package/src/store-postgres.ts +167 -0
- package/src/store.ts +214 -0
- package/src/strict-json.ts +115 -0
- package/src/trust-program-adapters.ts +847 -0
- package/src/trust-program-postgres.ts +429 -0
- package/src/trust-program-revocation.ts +751 -0
- package/src/trust-program.ts +1460 -0
- package/src/witness-postgres.ts +106 -0
- package/src/zk-range-proof.ts +250 -0
- package/store-postgres.js +3 -161
- package/store.js +4 -198
- package/strict-json.js +3 -102
- package/trust-program-adapters.js +4 -0
- package/trust-program-postgres.js +4 -0
- package/trust-program-revocation.js +4 -0
- package/trust-program.js +4 -0
- package/witness-postgres.js +3 -96
- package/zk-range-proof.js +4 -0
package/reliance-kernel.js
CHANGED
|
@@ -1,113 +1,4 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
* The pure verdict lives in @emilia-protocol/verify/reliance (evaluateReliance).
|
|
6
|
-
* This is the deny-by-default RUNTIME point a relying party puts in front of a
|
|
7
|
-
* consequential action: it evaluates the evidence packet against the relying
|
|
8
|
-
* party's pinned EP-RELIANCE-PROFILE-v1, appends the decision to a tamper-evident
|
|
9
|
-
* evidence log, and — on anything other than `rely` — returns a machine-readable
|
|
10
|
-
* refusal (HTTP 428, the same Receipt-Required status the Gate uses) naming the
|
|
11
|
-
* closed verdict and what evidence was required.
|
|
12
|
-
*
|
|
13
|
-
* ALLOW iff verdict === 'rely'. Every other closed verdict, a thrown verifier,
|
|
14
|
-
* or a strict evidence-log failure denies. The kernel never re-derives a verdict
|
|
15
|
-
* of its own — it enforces the one the pure offline verifier computed.
|
|
16
|
-
*/
|
|
17
|
-
import { createEvidenceLog } from './evidence.js';
|
|
18
|
-
import {
|
|
19
|
-
evaluateReliance,
|
|
20
|
-
RELIANCE_PROFILE_VERSION,
|
|
21
|
-
RELIANCE_VERDICTS,
|
|
22
|
-
} from '@emilia-protocol/verify/reliance';
|
|
23
|
-
|
|
24
|
-
const RECEIPT_REQUIRED_STATUS = 428;
|
|
25
|
-
|
|
26
|
-
export { RELIANCE_VERDICTS, RELIANCE_PROFILE_VERSION };
|
|
27
|
-
|
|
28
|
-
/** Build the 428 refusal for a non-`rely` verdict. */
|
|
29
|
-
function relianceChallenge(verdict, reasons, profile) {
|
|
30
|
-
return {
|
|
31
|
-
status: RECEIPT_REQUIRED_STATUS,
|
|
32
|
-
error: 'do_not_rely',
|
|
33
|
-
verdict,
|
|
34
|
-
reasons: Array.isArray(reasons) ? reasons : [],
|
|
35
|
-
required_assurance: profile?.required_assurance ?? null,
|
|
36
|
-
required_authority: profile?.required_authority === true,
|
|
37
|
-
required_evidence: Array.isArray(profile?.required_evidence) ? profile.required_evidence : [],
|
|
38
|
-
header: { name: 'Reliance-Refused', value: verdict },
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Create a reliance kernel bound to one relying-party profile.
|
|
44
|
-
*
|
|
45
|
-
* @param {object} cfg
|
|
46
|
-
* @param {object} cfg.profile the pinned EP-RELIANCE-PROFILE-v1
|
|
47
|
-
* @param {object} [cfg.log] an evidence log (createEvidenceLog); one is created if absent
|
|
48
|
-
* @param {boolean} [cfg.strictEvidence=true] fail closed if the evidence log sink fails
|
|
49
|
-
* @returns {{ check: Function, evidence: object }}
|
|
50
|
-
*/
|
|
51
|
-
export function createRelianceKernel({ profile, log, strictEvidence = true } = {}) {
|
|
52
|
-
const evidence = log || createEvidenceLog({ strict: strictEvidence });
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Evaluate + enforce one evidence packet.
|
|
56
|
-
* @param {object} input the evaluateReliance input MINUS relying_party_profile (bound here)
|
|
57
|
-
* @param {object} [opts] verifier options { approverKeys, logPublicKey, rpId, revokerKeys }
|
|
58
|
-
* @returns {Promise<{ allow:boolean, status:number, verdict:string, reasons:string[], checks:object, challenge:(object|null), decision:object }>}
|
|
59
|
-
*/
|
|
60
|
-
async function check(input = {}, opts = {}) {
|
|
61
|
-
let result;
|
|
62
|
-
try {
|
|
63
|
-
result = evaluateReliance({ ...input, relying_party_profile: profile }, opts);
|
|
64
|
-
} catch (err) {
|
|
65
|
-
// A thrown verifier is not a maybe — it is a refusal.
|
|
66
|
-
result = { verdict: 'do_not_rely_unsigned', rely: false, reasons: [`verifier_error:${err?.message || 'threw'}`], checks: {} };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const allow = result.verdict === 'rely';
|
|
70
|
-
const actionHash = input?.action?.action_hash ?? input?.receipt?.action_hash ?? null;
|
|
71
|
-
|
|
72
|
-
// Deny-by-default: record the decision to the tamper-evident log first. In
|
|
73
|
-
// strict mode a log-sink failure THROWS, which we convert to a refusal — an
|
|
74
|
-
// action whose decision cannot be durably recorded must not proceed.
|
|
75
|
-
let decision;
|
|
76
|
-
try {
|
|
77
|
-
decision = await evidence.record({
|
|
78
|
-
type: 'reliance.decision',
|
|
79
|
-
verdict: result.verdict,
|
|
80
|
-
allow,
|
|
81
|
-
action_hash: actionHash,
|
|
82
|
-
reasons: result.reasons,
|
|
83
|
-
checks: result.checks,
|
|
84
|
-
profile: result.profile ?? null,
|
|
85
|
-
});
|
|
86
|
-
} catch (err) {
|
|
87
|
-
return {
|
|
88
|
-
allow: false,
|
|
89
|
-
status: RECEIPT_REQUIRED_STATUS,
|
|
90
|
-
verdict: 'do_not_rely_unsigned',
|
|
91
|
-
reasons: [`evidence_log_failed:${err?.message || 'sink'}`],
|
|
92
|
-
checks: result.checks || {},
|
|
93
|
-
challenge: relianceChallenge('do_not_rely_unsigned', ['evidence_log_failed'], profile),
|
|
94
|
-
decision: null,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
allow,
|
|
100
|
-
status: allow ? 200 : RECEIPT_REQUIRED_STATUS,
|
|
101
|
-
verdict: result.verdict,
|
|
102
|
-
reasons: result.reasons,
|
|
103
|
-
checks: result.checks,
|
|
104
|
-
challenge: allow ? null : relianceChallenge(result.verdict, result.reasons, profile),
|
|
105
|
-
decision,
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return { check, evidence };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const relianceKernelApi = { createRelianceKernel };
|
|
113
|
-
export default relianceKernelApi;
|
|
2
|
+
// Compatibility entry point for the TypeScript reliance-kernel module.
|
|
3
|
+
export * from './dist/reliance-kernel.js';
|
|
4
|
+
export { default } from './dist/reliance-kernel.js';
|
package/reliance-packet.js
CHANGED
|
@@ -1,176 +1,4 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
// The CLOSED admissibility verdict set (mirror of lib/evidence/admissibility.js
|
|
7
|
-
// ADMISSIBILITY_VERDICTS). The gate does NOT import that app module — it stays a
|
|
8
|
-
// dependency-light published package — so the closed set is restated here as a
|
|
9
|
-
// value contract. 'admissible' is the ONLY verdict that reads as success; every
|
|
10
|
-
// other member, and anything not in this set, fails closed. Precedence lives in
|
|
11
|
-
// the evaluator; the gate never re-derives a verdict, it only checks the one the
|
|
12
|
-
// relying party's offline evaluator already computed.
|
|
13
|
-
export const ADMISSIBILITY_VERDICTS = Object.freeze([
|
|
14
|
-
'admissible', 'missing_evidence', 'stale', 'conflicted', 'unverifiable',
|
|
15
|
-
]);
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Normalize an admissibility block onto the packet. FAIL CLOSED: a missing block,
|
|
19
|
-
* a missing/unrecognized verdict, or a non-'admissible' verdict all produce
|
|
20
|
-
* admissible:false. The block is only carried when the caller supplies one; when
|
|
21
|
-
* absent, `admissibility` is null and no admissibility gating applies (backwards
|
|
22
|
-
* compatible with pre-profile packets).
|
|
23
|
-
*
|
|
24
|
-
* @param {object|null} adm the relying-party evaluator's result, carrying
|
|
25
|
-
* { admissibility_profile:{id,version}, profile_hash, verdict, replay_digest, challenge_id?, challenge_digest? }
|
|
26
|
-
*/
|
|
27
|
-
function normalizeAdmissibility(adm) {
|
|
28
|
-
if (!adm || typeof adm !== 'object') return null;
|
|
29
|
-
const verdict = typeof adm.verdict === 'string' ? adm.verdict : null;
|
|
30
|
-
const recognized = verdict !== null && ADMISSIBILITY_VERDICTS.includes(verdict);
|
|
31
|
-
// Only a recognized 'admissible' verdict reads as success. Missing, malformed,
|
|
32
|
-
// or any other closed-set member (missing_evidence/stale/conflicted/unverifiable)
|
|
33
|
-
// is non-admissible.
|
|
34
|
-
const admissible = recognized && verdict === 'admissible';
|
|
35
|
-
const profile = adm.admissibility_profile && typeof adm.admissibility_profile === 'object'
|
|
36
|
-
? { id: adm.admissibility_profile.id ?? null, version: adm.admissibility_profile.version ?? null }
|
|
37
|
-
: null;
|
|
38
|
-
return {
|
|
39
|
-
admissibility_profile: profile,
|
|
40
|
-
profile_hash: typeof adm.profile_hash === 'string' ? adm.profile_hash : null,
|
|
41
|
-
verdict: verdict, // preserved verbatim (including null / unrecognized) for the auditor
|
|
42
|
-
verdict_recognized: recognized,
|
|
43
|
-
admissible,
|
|
44
|
-
replay_digest: typeof adm.replay_digest === 'string' ? adm.replay_digest : null,
|
|
45
|
-
// The evidence-challenge loop (lib/negotiate/evidence-challenge.js) keys each
|
|
46
|
-
// round by challenge_id; challenge_digest is carried through when the caller
|
|
47
|
-
// hashes the challenge. Either identifies which challenge round this verdict
|
|
48
|
-
// answers. Both null-safe.
|
|
49
|
-
challenge_id: adm.challenge_id ?? null,
|
|
50
|
-
challenge_digest: typeof adm.challenge_digest === 'string' ? adm.challenge_digest : null,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function evidenceStatus(evidence) {
|
|
55
|
-
if (!evidence) {
|
|
56
|
-
return { ok: false, length: null, head: null, reason: 'evidence_verification_unavailable' };
|
|
57
|
-
}
|
|
58
|
-
try {
|
|
59
|
-
const status = typeof evidence.verify === 'function'
|
|
60
|
-
? await evidence.verify()
|
|
61
|
-
: await evidence;
|
|
62
|
-
if (!status || typeof status !== 'object' || Array.isArray(status)) {
|
|
63
|
-
return { ok: false, length: null, head: null, reason: 'evidence_verification_malformed' };
|
|
64
|
-
}
|
|
65
|
-
if (status.ok !== true) {
|
|
66
|
-
return {
|
|
67
|
-
ok: false,
|
|
68
|
-
length: Number.isSafeInteger(status.length) ? status.length : null,
|
|
69
|
-
head: typeof status.head === 'string' ? status.head : null,
|
|
70
|
-
reason: status.reason || 'evidence_verification_rejected',
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
return {
|
|
74
|
-
...status,
|
|
75
|
-
ok: true,
|
|
76
|
-
length: Number.isSafeInteger(status.length) ? status.length : null,
|
|
77
|
-
head: typeof status.head === 'string' ? status.head : null,
|
|
78
|
-
};
|
|
79
|
-
} catch {
|
|
80
|
-
return { ok: false, length: null, head: null, reason: 'evidence_verification_failed' };
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function check(id, ok, detail = null) {
|
|
85
|
-
return { id, ok, ...(detail ? { detail } : {}) };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export async function buildReliancePacket({
|
|
89
|
-
decision,
|
|
90
|
-
execution = null,
|
|
91
|
-
evidence = null,
|
|
92
|
-
manifest = null,
|
|
93
|
-
binding = null,
|
|
94
|
-
admissibility = null,
|
|
95
|
-
verifier = '@emilia-protocol/gate',
|
|
96
|
-
} = {}) {
|
|
97
|
-
const evidenceCheck = await evidenceStatus(evidence);
|
|
98
|
-
const decisionHash = decision?.evidence?.hash || decision?.hash || null;
|
|
99
|
-
const executionBound = Boolean(
|
|
100
|
-
execution
|
|
101
|
-
&& execution.kind === 'execution'
|
|
102
|
-
&& decisionHash
|
|
103
|
-
&& execution.authorizes_decision === decisionHash,
|
|
104
|
-
);
|
|
105
|
-
const bindingCheck = binding || decision?.evidence?.execution_binding || decision?.execution_binding || null;
|
|
106
|
-
const allowed = decision?.allow === true;
|
|
107
|
-
const evidenceOk = evidenceCheck.ok === true;
|
|
108
|
-
const bindingOk = bindingCheck ? bindingCheck.ok === true : true;
|
|
109
|
-
|
|
110
|
-
// Admissibility block (relying-party evaluator output, computed OFFLINE against
|
|
111
|
-
// its PINNED profile — never re-derived here). When present it is an additional
|
|
112
|
-
// gate on 'rely': a non-'admissible' verdict (or a missing/unrecognized one)
|
|
113
|
-
// fails closed. When absent, admissibility does not affect the verdict.
|
|
114
|
-
const adm = normalizeAdmissibility(admissibility);
|
|
115
|
-
const admissibilityOk = adm === null ? true : adm.admissible === true;
|
|
116
|
-
|
|
117
|
-
const verdict = allowed && executionBound && evidenceOk && bindingOk && admissibilityOk ? 'rely' : 'do_not_rely';
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
'@version': RELIANCE_PACKET_VERSION,
|
|
121
|
-
product: 'EMILIA Gate',
|
|
122
|
-
verifier,
|
|
123
|
-
verdict,
|
|
124
|
-
summary: {
|
|
125
|
-
action: decision?.action || null,
|
|
126
|
-
receipt_id: decision?.evidence?.receipt_id || decision?.receipt_id || null,
|
|
127
|
-
subject: decision?.evidence?.subject || null,
|
|
128
|
-
policy_id: decision?.evidence?.evaluated_policy_id || null,
|
|
129
|
-
policy_hash: decision?.evidence?.evaluated_policy_hash || null,
|
|
130
|
-
tenant_id: decision?.evidence?.evaluated_tenant_id || null,
|
|
131
|
-
approvers: decision?.evidence?.evaluated_approvers || [],
|
|
132
|
-
required_tier: decision?.evidence?.required_tier || decision?.required_tier || null,
|
|
133
|
-
observed_tier: decision?.evidence?.have_tier || decision?.have_tier || null,
|
|
134
|
-
decision_hash: decisionHash,
|
|
135
|
-
execution_hash: execution?.hash || null,
|
|
136
|
-
evidence_head: evidenceCheck.head || null,
|
|
137
|
-
// Surface the admissibility verdict + pinned profile in the summary so an
|
|
138
|
-
// auditor sees WHICH bar was cleared without digging into checks. Null when
|
|
139
|
-
// no admissibility block was supplied.
|
|
140
|
-
admissibility_verdict: adm ? adm.verdict : null,
|
|
141
|
-
admissibility_profile: adm ? adm.admissibility_profile : null,
|
|
142
|
-
admissibility_profile_hash: adm ? adm.profile_hash : null,
|
|
143
|
-
},
|
|
144
|
-
// Full admissibility block (or null). The auditor / gate can re-check
|
|
145
|
-
// {profile_hash, verdict, replay_digest} against the profile the relying party
|
|
146
|
-
// pinned. Carried verbatim, fail-closed by construction (see normalizeAdmissibility).
|
|
147
|
-
admissibility: adm,
|
|
148
|
-
checks: [
|
|
149
|
-
check('receipt_present_and_valid', allowed && !String(decision?.reason || '').startsWith('receipt_rejected'), decision?.reason || null),
|
|
150
|
-
check('assurance_sufficient', allowed || decision?.reason !== 'assurance_too_low', decision?.reason === 'assurance_too_low' ? 'receipt tier below action requirement' : null),
|
|
151
|
-
check('receipt_one_time_consumed', allowed || decision?.reason === 'replay_refused' ? decision?.reason !== 'replay_refused' : null),
|
|
152
|
-
check('execution_fields_bound', bindingCheck ? bindingCheck.ok === true : null, bindingCheck ? { missing_observed_fields: bindingCheck.missing_observed_fields || [], mismatched_fields: bindingCheck.mismatched_fields || [] } : 'no material execution-field binding required by this action'),
|
|
153
|
-
check('execution_attests_decision', executionBound, execution ? null : 'no execution record supplied'),
|
|
154
|
-
check('evidence_log_intact', evidenceCheck.ok === true, evidenceCheck.reason || null),
|
|
155
|
-
// Admissibility verdict against the relying party's PINNED profile. null when
|
|
156
|
-
// no profile/verdict was supplied (this reliance did not gate on admissibility);
|
|
157
|
-
// true only for a recognized 'admissible' verdict; false otherwise (fail closed).
|
|
158
|
-
check(
|
|
159
|
-
'admissibility_verdict_admissible',
|
|
160
|
-
adm === null ? null : adm.admissible === true,
|
|
161
|
-
adm === null
|
|
162
|
-
? 'no admissibility profile / verdict supplied for this reliance'
|
|
163
|
-
: { verdict: adm.verdict, verdict_recognized: adm.verdict_recognized, profile: adm.admissibility_profile, profile_hash: adm.profile_hash, replay_digest: adm.replay_digest },
|
|
164
|
-
),
|
|
165
|
-
],
|
|
166
|
-
manifest_version: manifest?.['@version'] || null,
|
|
167
|
-
limitations: [
|
|
168
|
-
'The packet proves the gate verified a receipt and enforced its configured policy; it does not prove the human made a wise decision.',
|
|
169
|
-
'Identity, authority enrollment, and key custody remain external trust roots that must be operated correctly.',
|
|
170
|
-
'For execution-field binding, observedAction must come from the system of record, not from attacker-controlled request input.',
|
|
171
|
-
'An admissible verdict means the evidence bundle cleared the bar THIS relying party pinned; it does not establish the action is correct, safe, or currently valid beyond the freshness bounds evaluated. The verdict is computed OFFLINE by the relying party against its own pinned profile — EMILIA neither hosts the authoritative registry nor adjudicates.',
|
|
172
|
-
],
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export default { RELIANCE_PACKET_VERSION, ADMISSIBILITY_VERDICTS, buildReliancePacket };
|
|
2
|
+
// Compatibility entry point for the TypeScript reliance-packet module.
|
|
3
|
+
export * from './dist/reliance-packet.js';
|
|
4
|
+
export { default } from './dist/reliance-packet.js';
|
package/reports/art14.js
CHANGED
|
Binary file
|
|
@@ -1,313 +1,3 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* The layer above the reliance kernel. The kernel answers "may this party rely on
|
|
6
|
-
* this action?" The assurance package answers the question an INDEPENDENT assurer
|
|
7
|
-
* (an audit firm, a regulator, an insurer) asks: "can I reproduce, test, and
|
|
8
|
-
* attest that an organization's automated actions were governed by admissible
|
|
9
|
-
* evidence under the organization's OWN pinned rule?"
|
|
10
|
-
*
|
|
11
|
-
* Two halves, mirroring the re-performance discipline of reperform.js:
|
|
12
|
-
* buildAssurancePackage(decisions, ...) the organization bundles its
|
|
13
|
-
* automated decisions + the evidence each relied on into ONE portable,
|
|
14
|
-
* content-addressed package (action, receipt, profile, authority proof,
|
|
15
|
-
* revocation check, consumption, denial/exception history, policy hash).
|
|
16
|
-
* reperformAssurancePackage(pkg, ...) the assurer RE-PERFORMS every
|
|
17
|
-
* reliance verdict offline from that evidence, TRUSTING NOTHING the package
|
|
18
|
-
* asserts: it recomputes each verdict with evaluateReliance, compares to the
|
|
19
|
-
* verdict the org's runtime CLAIMED (drift = a control failure the assurer
|
|
20
|
-
* caught), maps every verdict to a control objective, and emits an
|
|
21
|
-
* auditor-style workpaper. It does not conclude; the assurer concludes.
|
|
22
|
-
*
|
|
23
|
-
* PCAOB AS 1105 alignment (why this is audit evidence): the source is
|
|
24
|
-
* cryptographic, the decision trail is immutable and content-addressed, the rule
|
|
25
|
-
* is a pinned profile, and the verdict is re-performable directly. This module
|
|
26
|
-
* SUPPORTS a re-performance procedure; it never issues an opinion.
|
|
27
|
-
*/
|
|
28
|
-
import { hashCanonical } from '../execution-binding.js';
|
|
29
|
-
import { evaluateReliance, RELIANCE_VERDICTS } from '@emilia-protocol/verify/reliance';
|
|
30
|
-
|
|
31
|
-
export const ASSURANCE_PACKAGE_VERSION = 'EP-ASSURANCE-PACKAGE-v1';
|
|
32
|
-
export const ASSURANCE_REPERFORMANCE_VERSION = 'EP-ASSURANCE-REPERFORMANCE-v1';
|
|
33
|
-
|
|
34
|
-
export { RELIANCE_VERDICTS };
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* The reliance control catalog: every reliance verdict maps to the control
|
|
38
|
-
* objective it exercises. A `rely` shows the control PASSING; every do_not_rely_*
|
|
39
|
-
* shows the control OPERATING (it refused a non-admissible action). Denials are
|
|
40
|
-
* the control working, not the control failing.
|
|
41
|
-
*/
|
|
42
|
-
export const RELIANCE_CONTROL_CATALOG = Object.freeze({
|
|
43
|
-
'RC-1': { objective: 'Only a human with valid organization-bound, scoped authority for THIS exact action may authorize it', verdicts: ['do_not_rely_authority_missing', 'do_not_rely_authority_subject_mismatch', 'do_not_rely_authority_organization_mismatch', 'do_not_rely_authority_revoked', 'do_not_rely_authority_expired', 'do_not_rely_scope_mismatch', 'do_not_rely_amount_exceeded', 'do_not_rely_registry_unavailable'] },
|
|
44
|
-
'RC-2': { objective: 'Authorization uses a device-bound named-human ceremony (Class-A or quorum)', verdicts: ['do_not_rely_no_class_a', 'do_not_rely_quorum_unsatisfied'] },
|
|
45
|
-
'RC-3': { objective: 'The action conforms to a pinned, accepted policy', verdicts: ['do_not_rely_policy_mismatch'] },
|
|
46
|
-
'RC-4': { objective: 'Authorization is consumed exactly once (no replay)', verdicts: ['do_not_rely_already_consumed'] },
|
|
47
|
-
'RC-5': { objective: 'Reliance is evaluated against fresh revocation state', verdicts: ['do_not_rely_stale_revocation'] },
|
|
48
|
-
'RC-6': { objective: 'Evidence is signed by a trusted issuer and evaluated under a pinned rule', verdicts: ['do_not_rely_unsigned', 'do_not_rely_untrusted_issuer', 'do_not_rely_no_profile'] },
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
const VERDICT_TO_CONTROL = Object.freeze(
|
|
52
|
-
Object.entries(RELIANCE_CONTROL_CATALOG).reduce((m, [cid, c]) => {
|
|
53
|
-
for (const v of c.verdicts) m[v] = cid;
|
|
54
|
-
return m;
|
|
55
|
-
}, {}),
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
/** Which control objective a verdict exercises (rely passes ALL, so returns null). */
|
|
59
|
-
function controlForVerdict(verdict) {
|
|
60
|
-
if (verdict === 'rely') return null;
|
|
61
|
-
return VERDICT_TO_CONTROL[verdict] || null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function toIso(now) {
|
|
65
|
-
const ms = typeof now === 'function' ? now() : now;
|
|
66
|
-
return new Date(ms == null ? 0 : ms).toISOString();
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function portableJsonCopy(value, active = new Set()) {
|
|
70
|
-
if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;
|
|
71
|
-
if (typeof value === 'number') {
|
|
72
|
-
if (!Number.isFinite(value) || Object.is(value, -0)) {
|
|
73
|
-
throw new TypeError('assurance-package: value is not canonical JSON');
|
|
74
|
-
}
|
|
75
|
-
return value;
|
|
76
|
-
}
|
|
77
|
-
if (typeof value !== 'object') {
|
|
78
|
-
throw new TypeError('assurance-package: value is not JSON');
|
|
79
|
-
}
|
|
80
|
-
if (active.has(value)) throw new TypeError('assurance-package: cyclic value');
|
|
81
|
-
active.add(value);
|
|
82
|
-
try {
|
|
83
|
-
if (Array.isArray(value)) return value.map((entry) => portableJsonCopy(entry, active));
|
|
84
|
-
const prototype = Object.getPrototypeOf(value);
|
|
85
|
-
if (prototype !== Object.prototype && prototype !== null) {
|
|
86
|
-
throw new TypeError('assurance-package: value must use plain JSON objects');
|
|
87
|
-
}
|
|
88
|
-
return Object.fromEntries(
|
|
89
|
-
Object.entries(value).map(([key, entry]) => [key, portableJsonCopy(entry, active)]),
|
|
90
|
-
);
|
|
91
|
-
} finally {
|
|
92
|
-
active.delete(value);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Bundle N automated reliance decisions + the evidence each relied on into one
|
|
98
|
-
* portable, content-addressed assurance package. Does NOT re-perform (that is the
|
|
99
|
-
* assurer's independent step); it packages faithfully, including the verdict the
|
|
100
|
-
* org's runtime CLAIMED, so drift is checkable later.
|
|
101
|
-
*
|
|
102
|
-
* @param {Array<object>} decisions each: { decision_id, action, receipt, quorum?,
|
|
103
|
-
* authority_proof?, revocation_state?, consumption?, stated_verdict? }
|
|
104
|
-
* @param {object} opts
|
|
105
|
-
* @param {object} opts.profile the pinned EP-RELIANCE-PROFILE-v1 the org operated under
|
|
106
|
-
* @param {object} [opts.organization] { id, name } (no PHI)
|
|
107
|
-
* @param {number|function} [opts.now]
|
|
108
|
-
* @returns {object} EP-ASSURANCE-PACKAGE-v1
|
|
109
|
-
*/
|
|
110
|
-
export function buildAssurancePackage(decisions = [], { profile, organization = null, now = 0 } = {}) {
|
|
111
|
-
if (!Array.isArray(decisions)) throw new Error('assurance-package: decisions must be an array');
|
|
112
|
-
const items = decisions.map((d, i) => {
|
|
113
|
-
const source = portableJsonCopy(d ?? {});
|
|
114
|
-
const decision_id = source.decision_id ?? `decision-${i}`;
|
|
115
|
-
return {
|
|
116
|
-
decision_id,
|
|
117
|
-
action: source.action ?? null,
|
|
118
|
-
policy_hash: source.action?.policy_hash ?? null,
|
|
119
|
-
stated_verdict: typeof source.stated_verdict === 'string' ? source.stated_verdict : null,
|
|
120
|
-
evidence: {
|
|
121
|
-
receipt: source.receipt ?? null,
|
|
122
|
-
quorum: source.quorum ?? null,
|
|
123
|
-
authority_proof: source.authority_proof ?? null,
|
|
124
|
-
revocation_state: source.revocation_state ?? null,
|
|
125
|
-
consumption: source.consumption ?? null,
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
|
-
});
|
|
129
|
-
// Denial/exception history = the decisions the org itself recorded as refused.
|
|
130
|
-
const exceptions = items.filter((it) => it.stated_verdict && it.stated_verdict !== 'rely')
|
|
131
|
-
.map((it) => ({ decision_id: it.decision_id, stated_verdict: it.stated_verdict, control_id: controlForVerdict(it.stated_verdict) }));
|
|
132
|
-
|
|
133
|
-
const profileCopy = profile == null ? null : portableJsonCopy(profile);
|
|
134
|
-
const body = {
|
|
135
|
-
'@version': ASSURANCE_PACKAGE_VERSION,
|
|
136
|
-
organization: organization == null ? null : portableJsonCopy(organization),
|
|
137
|
-
reliance_profile: profileCopy,
|
|
138
|
-
profile_hash: profileCopy ? hashCanonical(profileCopy) : null,
|
|
139
|
-
control_catalog: RELIANCE_CONTROL_CATALOG,
|
|
140
|
-
decisions: items,
|
|
141
|
-
exception_history: exceptions,
|
|
142
|
-
counts: {
|
|
143
|
-
decisions: items.length,
|
|
144
|
-
stated_admissible: items.filter((it) => it.stated_verdict === 'rely').length,
|
|
145
|
-
stated_refused: items.filter((it) => it.stated_verdict && it.stated_verdict !== 'rely').length,
|
|
146
|
-
stated_unknown: items.filter((it) => !it.stated_verdict).length,
|
|
147
|
-
},
|
|
148
|
-
assembled_at: toIso(now),
|
|
149
|
-
};
|
|
150
|
-
// Content address over everything EXCEPT the timestamp (so the same evidence
|
|
151
|
-
// yields the same digest regardless of when it was packaged).
|
|
152
|
-
const { assembled_at: _t, ...digestScope } = body;
|
|
153
|
-
return { ...body, package_digest: hashCanonical(digestScope) };
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* INDEPENDENT re-performance. Recompute every reliance verdict offline from the
|
|
158
|
-
* packaged evidence under the package's pinned profile and AUDITOR-supplied keys,
|
|
159
|
-
* trusting nothing the package asserts. Detect drift (recomputed ≠ stated), map
|
|
160
|
-
* to control objectives, and emit an auditor-style workpaper. Conclusion fields
|
|
161
|
-
* are ALWAYS null: the assurer concludes, not this tool.
|
|
162
|
-
*
|
|
163
|
-
* @param {object} pkg an EP-ASSURANCE-PACKAGE-v1
|
|
164
|
-
* @param {object} opts
|
|
165
|
-
* @param {object} [opts.approverKeys] auditor-pinned approver keys (out of band)
|
|
166
|
-
* @param {string} [opts.logPublicKey] auditor-pinned transparency-log key
|
|
167
|
-
* @param {string} [opts.rpId]
|
|
168
|
-
* @param {string[]} [opts.allowedOrigins]
|
|
169
|
-
* @param {object} [opts.revokerKeys]
|
|
170
|
-
* @param {(key:object)=>boolean} [opts.isConsumed] auditor-owned consumption lookup
|
|
171
|
-
* @param {number|string|Date} [opts.now] reliance-evaluation clock (pin for determinism)
|
|
172
|
-
* @returns {object} EP-ASSURANCE-REPERFORMANCE-v1
|
|
173
|
-
*/
|
|
174
|
-
export function reperformAssurancePackage(pkg, { approverKeys = {}, logPublicKey = null, rpId = null, allowedOrigins = [], revokerKeys = {}, isConsumed, now = 0 } = {}) {
|
|
175
|
-
if (!pkg || pkg['@version'] !== ASSURANCE_PACKAGE_VERSION) throw new Error('assurance-reperform: not an EP-ASSURANCE-PACKAGE-v1');
|
|
176
|
-
const profile = pkg.reliance_profile;
|
|
177
|
-
const evalOpts = { approverKeys, logPublicKey, rpId, allowedOrigins, revokerKeys, ...(typeof isConsumed === 'function' ? { isConsumed } : {}) };
|
|
178
|
-
const relianceNow = typeof now === 'function' ? now() : now;
|
|
179
|
-
|
|
180
|
-
const results = (Array.isArray(pkg.decisions) ? pkg.decisions : []).map((it) => {
|
|
181
|
-
const ev = it.evidence || {};
|
|
182
|
-
const input = {
|
|
183
|
-
action: it.action || {},
|
|
184
|
-
receipt: ev.receipt,
|
|
185
|
-
quorum: ev.quorum || undefined,
|
|
186
|
-
authority_proof: ev.authority_proof || undefined,
|
|
187
|
-
revocation_state: ev.revocation_state || undefined,
|
|
188
|
-
consumption: ev.consumption || undefined,
|
|
189
|
-
relying_party_profile: profile,
|
|
190
|
-
now: relianceNow,
|
|
191
|
-
};
|
|
192
|
-
let recomputed;
|
|
193
|
-
try {
|
|
194
|
-
recomputed = evaluateReliance(input, evalOpts);
|
|
195
|
-
} catch (err) {
|
|
196
|
-
recomputed = { verdict: 'do_not_rely_unsigned', rely: false, reasons: [`reperform_error:${err?.message || 'threw'}`] };
|
|
197
|
-
}
|
|
198
|
-
const recomputed_verdict = recomputed.verdict;
|
|
199
|
-
const stated_verdict = it.stated_verdict ?? null;
|
|
200
|
-
// Drift: the org's runtime claimed one outcome, independent re-performance
|
|
201
|
-
// computed another. A claimed `rely` that recomputes to a refusal is the
|
|
202
|
-
// material finding — the org relied on evidence that does not support reliance.
|
|
203
|
-
const drift = stated_verdict !== null && stated_verdict !== recomputed_verdict;
|
|
204
|
-
return {
|
|
205
|
-
decision_id: it.decision_id,
|
|
206
|
-
action_type: it.action?.action_type ?? null,
|
|
207
|
-
stated_verdict,
|
|
208
|
-
recomputed_verdict,
|
|
209
|
-
admissible: recomputed_verdict === 'rely',
|
|
210
|
-
drift,
|
|
211
|
-
drift_severity: drift ? (stated_verdict === 'rely' ? 'relied_on_inadmissible_evidence' : 'refused_admissible_or_reclassified') : null,
|
|
212
|
-
control_id: controlForVerdict(recomputed_verdict),
|
|
213
|
-
reasons: recomputed.reasons || [],
|
|
214
|
-
};
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
const byVerdict = Object.create(null);
|
|
218
|
-
const byControl = Object.create(null);
|
|
219
|
-
let admissible = 0;
|
|
220
|
-
let refused = 0;
|
|
221
|
-
let drift = 0;
|
|
222
|
-
for (const r of results) {
|
|
223
|
-
byVerdict[r.recomputed_verdict] = (byVerdict[r.recomputed_verdict] || 0) + 1;
|
|
224
|
-
if (r.admissible) admissible += 1; else refused += 1;
|
|
225
|
-
if (r.drift) drift += 1;
|
|
226
|
-
if (r.control_id) byControl[r.control_id] = (byControl[r.control_id] || 0) + 1;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Recompute the package digest from the package's OWN contents rather than
|
|
230
|
-
// trusting pkg.package_digest. "No value the package asserts is trusted" has to
|
|
231
|
-
// include the digest: copying the stated one verbatim would let a tampered
|
|
232
|
-
// package carry a lying content-address through re-performance unchecked. Mirror
|
|
233
|
-
// buildAssurancePackage's digestScope exactly (body minus assembled_at and the
|
|
234
|
-
// digest field itself), then compare.
|
|
235
|
-
const { assembled_at: _statedAt, package_digest: _statedDigest, ...digestScope } = pkg;
|
|
236
|
-
const recomputedPackageDigest = hashCanonical(digestScope);
|
|
237
|
-
const packageDigestVerified = pkg.package_digest != null && recomputedPackageDigest === pkg.package_digest;
|
|
238
|
-
|
|
239
|
-
const doc = {
|
|
240
|
-
'@version': ASSURANCE_REPERFORMANCE_VERSION,
|
|
241
|
-
product: 'EMILIA Reliance Assurance',
|
|
242
|
-
package_digest: recomputedPackageDigest,
|
|
243
|
-
stated_package_digest: pkg.package_digest ?? null,
|
|
244
|
-
package_digest_verified: packageDigestVerified,
|
|
245
|
-
profile_hash: pkg.profile_hash ?? null,
|
|
246
|
-
generated_at: toIso(now),
|
|
247
|
-
honesty: {
|
|
248
|
-
reperforms:
|
|
249
|
-
'Independent recomputation of every reliance verdict from the packaged evidence, under the package\'s pinned '
|
|
250
|
-
+ 'EP-RELIANCE-PROFILE-v1 and auditor-supplied keys, using the offline reliance kernel. No value the package '
|
|
251
|
-
+ 'asserts (including the stated verdict) is trusted; the stated verdict is compared, never relied on.',
|
|
252
|
-
does_not_establish: [
|
|
253
|
-
'Completeness of the decision population: decisions withheld before packaging are not detectable from the package alone. Bind the population to an externally anchored count to close this.',
|
|
254
|
-
'Runtime freshness or one-time consumption AT THE MOMENT OF DECISION: those were live properties; re-performance checks the evidence as packaged, not the runtime state that existed then.',
|
|
255
|
-
'Issuer, approver, and registrar key custody, enrollment, or identity proofing, which remain external trust roots the auditor supplies out of band.',
|
|
256
|
-
'The business correctness or wisdom of any authorized action.',
|
|
257
|
-
],
|
|
258
|
-
status: 'Support for an audit re-performance procedure. This document does not conclude, opine, or certify; any conclusion is the auditor\'s.',
|
|
259
|
-
},
|
|
260
|
-
population: {
|
|
261
|
-
decisions: results.length,
|
|
262
|
-
admissible,
|
|
263
|
-
refused,
|
|
264
|
-
drift,
|
|
265
|
-
relied_on_inadmissible_evidence: results.filter((r) => r.drift_severity === 'relied_on_inadmissible_evidence').length,
|
|
266
|
-
by_recomputed_verdict: byVerdict,
|
|
267
|
-
by_control: byControl,
|
|
268
|
-
},
|
|
269
|
-
control_catalog: RELIANCE_CONTROL_CATALOG,
|
|
270
|
-
results,
|
|
271
|
-
reperformance_digest: null, // filled below
|
|
272
|
-
// Conclusion fields are ALWAYS null: a machine may support re-performance, it
|
|
273
|
-
// may never fill in the auditor's sign-off. A renderer must refuse to print a
|
|
274
|
-
// non-null conclusion here.
|
|
275
|
-
conclusion: { supportable: null, opinion: null, signed_off_by: null },
|
|
276
|
-
};
|
|
277
|
-
// Deterministic re-performance digest over the recomputed results + population
|
|
278
|
-
// (excludes timestamps), so a second assurer reproduces it byte-for-byte.
|
|
279
|
-
doc.reperformance_digest = hashCanonical({ package_digest: doc.package_digest, population: doc.population, results });
|
|
280
|
-
return doc;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/** Render a plain-text auditor workpaper. Refuses to print a filled conclusion. */
|
|
284
|
-
export function renderAssuranceWorkpaper(doc) {
|
|
285
|
-
if (!doc || doc['@version'] !== ASSURANCE_REPERFORMANCE_VERSION) throw new Error('render: not an EP-ASSURANCE-REPERFORMANCE-v1');
|
|
286
|
-
if (doc.conclusion && (doc.conclusion.supportable !== null || doc.conclusion.opinion !== null || doc.conclusion.signed_off_by !== null)) {
|
|
287
|
-
throw new Error('render refused: conclusion fields must be null (the auditor concludes, not the tool)');
|
|
288
|
-
}
|
|
289
|
-
const p = doc.population;
|
|
290
|
-
const lines = [];
|
|
291
|
-
lines.push(`EMILIA Reliance Assurance — re-performance workpaper (${doc['@version']})`);
|
|
292
|
-
lines.push(`package_digest: ${doc.package_digest} (recomputed)`);
|
|
293
|
-
lines.push(`package_digest match: ${doc.package_digest_verified ? 'YES — recomputed digest equals the package\'s stated digest' : 'NO — stated digest does NOT match recomputed contents (tamper or drift)'}`);
|
|
294
|
-
lines.push(`reperformance_digest: ${doc.reperformance_digest}`);
|
|
295
|
-
lines.push('');
|
|
296
|
-
lines.push(`Population: ${p.decisions} decisions | admissible(rely): ${p.admissible} | refused: ${p.refused} | drift: ${p.drift}`);
|
|
297
|
-
lines.push(`Relied on INADMISSIBLE evidence (claimed rely, recomputed refusal): ${p.relied_on_inadmissible_evidence}`);
|
|
298
|
-
lines.push('');
|
|
299
|
-
lines.push('By recomputed verdict:');
|
|
300
|
-
for (const [v, n] of Object.entries(p.by_recomputed_verdict)) lines.push(` ${v}: ${n}`);
|
|
301
|
-
lines.push('');
|
|
302
|
-
lines.push('Control objectives exercised (denials are the control operating):');
|
|
303
|
-
for (const [cid, n] of Object.entries(p.by_control)) lines.push(` ${cid} (${RELIANCE_CONTROL_CATALOG[cid].objective}): ${n}`);
|
|
304
|
-
lines.push('');
|
|
305
|
-
const drifts = doc.results.filter((r) => r.drift);
|
|
306
|
-
if (drifts.length) {
|
|
307
|
-
lines.push('DRIFT (independent re-performance disagrees with the runtime\'s stated verdict):');
|
|
308
|
-
for (const r of drifts) lines.push(` ${r.decision_id}: stated=${r.stated_verdict} recomputed=${r.recomputed_verdict} [${r.drift_severity}]`);
|
|
309
|
-
lines.push('');
|
|
310
|
-
}
|
|
311
|
-
lines.push('Conclusion: NULL by construction. The auditor concludes; this workpaper only supports the procedure.');
|
|
312
|
-
return lines.join('\n');
|
|
313
|
-
}
|
|
2
|
+
// Compatibility entry point for the TypeScript assurance-package report module.
|
|
3
|
+
export * from '../dist/reports/assurance-package.js';
|