@emilia-protocol/gate 0.11.0 → 0.14.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 +80 -0
- package/README.md +310 -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 +1003 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1737 -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/proposal-to-effect.d.ts +146 -0
- package/dist/proposal-to-effect.d.ts.map +1 -0
- package/dist/proposal-to-effect.js +427 -0
- package/dist/proposal-to-effect.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 +35 -4
- package/proposal-to-effect.js +4 -0
- 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 +2127 -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/proposal-to-effect.ts +571 -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/key-registry.js
CHANGED
|
@@ -1,135 +1,4 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
* A flat `trustedKeys: string[]` cannot express the two things a production
|
|
6
|
-
* deployment needs:
|
|
7
|
-
* 1. ROTATION — an issuer key is valid only for a window; a new key overlaps
|
|
8
|
-
* the old one, then the old one retires, without rejecting receipts that
|
|
9
|
-
* were legitimately issued while it was current.
|
|
10
|
-
* 2. REVOCATION — a COMPROMISED issuer key must be rejected immediately, for
|
|
11
|
-
* every receipt, regardless of the issuance time the (now-untrusted) key
|
|
12
|
-
* claims. Fail closed: once revoked, the key signs nothing the gate accepts.
|
|
13
|
-
*
|
|
14
|
-
* The registry resolves, for a given receipt issuance time, the set of public
|
|
15
|
-
* keys the gate should verify against — excluding revoked keys entirely and
|
|
16
|
-
* windowed keys whose [not_before, not_after] does not contain that time. The
|
|
17
|
-
* gate passes that resolved set to the receipt verifier, so an excluded key's
|
|
18
|
-
* signature simply does not verify and the action is refused.
|
|
19
|
-
*
|
|
20
|
-
* A key with no window and no revocation behaves exactly like a flat trustedKeys
|
|
21
|
-
* entry (back-compatible).
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
const RFC3339_INSTANT = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?(?:Z|([+-])(\d{2}):(\d{2}))$/;
|
|
25
|
-
|
|
26
|
-
function strictInstantMs(value) {
|
|
27
|
-
if (typeof value !== 'string') return NaN;
|
|
28
|
-
const match = value.match(RFC3339_INSTANT);
|
|
29
|
-
if (!match) return NaN;
|
|
30
|
-
const [, y, mo, d, h, mi, s, , oh, om] = match;
|
|
31
|
-
const calendar = new Date(0);
|
|
32
|
-
calendar.setUTCFullYear(Number(y), Number(mo) - 1, Number(d));
|
|
33
|
-
calendar.setUTCHours(Number(h), Number(mi), Number(s), 0);
|
|
34
|
-
if (calendar.toISOString().slice(0, 19) !== `${y}-${mo}-${d}T${h}:${mi}:${s}`) return NaN;
|
|
35
|
-
if (oh !== undefined && (Number(oh) > 23 || Number(om) > 59)) return NaN;
|
|
36
|
-
const ms = Date.parse(value);
|
|
37
|
-
return Number.isFinite(ms) ? ms : NaN;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function optionalInstant(entry, field) {
|
|
41
|
-
if (!Object.hasOwn(entry, field)) return null;
|
|
42
|
-
const ms = strictInstantMs(entry[field]);
|
|
43
|
-
if (!Number.isFinite(ms)) {
|
|
44
|
-
throw new Error(`key registry: ${field} must be a valid RFC3339 instant when supplied`);
|
|
45
|
-
}
|
|
46
|
-
return ms;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* @param {Array<object>} entries each: {
|
|
51
|
-
* kid?: string, key: string (base64url SPKI-DER public key),
|
|
52
|
-
* not_before?: string, not_after?: string, revoked_at?: string (strict RFC3339)
|
|
53
|
-
* }
|
|
54
|
-
*/
|
|
55
|
-
export function createKeyRegistry(entries = []) {
|
|
56
|
-
const list = [];
|
|
57
|
-
function normalize(e) {
|
|
58
|
-
if (!e || !e.key || typeof e.key !== 'string') throw new Error('key registry entry requires a base64url SPKI key');
|
|
59
|
-
const normalized = {
|
|
60
|
-
kid: e.kid || e.key.slice(0, 16),
|
|
61
|
-
key: e.key,
|
|
62
|
-
not_before: optionalInstant(e, 'not_before'),
|
|
63
|
-
not_after: optionalInstant(e, 'not_after'),
|
|
64
|
-
revoked_at: optionalInstant(e, 'revoked_at'),
|
|
65
|
-
};
|
|
66
|
-
if (normalized.not_before != null && normalized.not_after != null
|
|
67
|
-
&& normalized.not_after < normalized.not_before) {
|
|
68
|
-
throw new Error('key registry: not_after must not precede not_before');
|
|
69
|
-
}
|
|
70
|
-
return normalized;
|
|
71
|
-
}
|
|
72
|
-
for (const e of entries) list.push(normalize(e));
|
|
73
|
-
|
|
74
|
-
/** Is this entry usable to verify a receipt issued at `atMs`? */
|
|
75
|
-
function entryActiveAt(entry, atMs) {
|
|
76
|
-
if (entry.revoked_at != null) return false; // HARD revocation: never trust a revoked key
|
|
77
|
-
if (!Number.isFinite(atMs)) return false;
|
|
78
|
-
if (entry.not_before != null && atMs < entry.not_before) return false;
|
|
79
|
-
if (entry.not_after != null && atMs > entry.not_after) return false;
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
/** The base64url public keys to verify a receipt issued at strict RFC3339 `at`. */
|
|
85
|
-
keysValidAt(at) {
|
|
86
|
-
const atMs = strictInstantMs(at);
|
|
87
|
-
return list.filter((e) => entryActiveAt(e, atMs)).map((e) => e.key);
|
|
88
|
-
},
|
|
89
|
-
/** Mark a kid revoked as of `at` (default: now-as-supplied). Fail-closed thereafter. */
|
|
90
|
-
revoke(kid, at) {
|
|
91
|
-
let revokedAt = 0;
|
|
92
|
-
if (arguments.length > 1) {
|
|
93
|
-
revokedAt = strictInstantMs(at);
|
|
94
|
-
if (!Number.isFinite(revokedAt)) {
|
|
95
|
-
throw new Error('key registry: revoked_at must be a valid RFC3339 instant when supplied');
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
let n = 0;
|
|
99
|
-
for (const e of list) {
|
|
100
|
-
if (e.kid === kid && e.revoked_at == null) { e.revoked_at = revokedAt; n += 1; }
|
|
101
|
-
}
|
|
102
|
-
if (n === 0) throw new Error(`key registry: no active key with kid "${kid}" to revoke`);
|
|
103
|
-
return n;
|
|
104
|
-
},
|
|
105
|
-
/** Add/rotate in a new key. */
|
|
106
|
-
add(entry) { list.push(normalize(entry)); return this; },
|
|
107
|
-
/** Operational snapshot (no private material; keys are public). */
|
|
108
|
-
status(at) {
|
|
109
|
-
const supplied = arguments.length > 0;
|
|
110
|
-
const atMs = supplied ? strictInstantMs(at) : NaN;
|
|
111
|
-
return list.map((e) => ({
|
|
112
|
-
kid: e.kid,
|
|
113
|
-
revoked: e.revoked_at != null,
|
|
114
|
-
active: supplied
|
|
115
|
-
? entryActiveAt(e, atMs)
|
|
116
|
-
: e.revoked_at == null && e.not_before == null && e.not_after == null,
|
|
117
|
-
not_before: e.not_before,
|
|
118
|
-
not_after: e.not_after,
|
|
119
|
-
revoked_at: e.revoked_at,
|
|
120
|
-
}));
|
|
121
|
-
},
|
|
122
|
-
get size() { return list.length; },
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** Coerce a flat trustedKeys[] OR a registry into a registry (back-compat). */
|
|
127
|
-
export function asKeyRegistry(trustedKeysOrRegistry) {
|
|
128
|
-
if (trustedKeysOrRegistry && typeof trustedKeysOrRegistry.keysValidAt === 'function') {
|
|
129
|
-
return trustedKeysOrRegistry;
|
|
130
|
-
}
|
|
131
|
-
const keys = Array.isArray(trustedKeysOrRegistry) ? trustedKeysOrRegistry : [];
|
|
132
|
-
return createKeyRegistry(keys.map((key) => ({ key })));
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export default { createKeyRegistry, asKeyRegistry };
|
|
2
|
+
// Compatibility entry point for the TypeScript key-registry module.
|
|
3
|
+
export * from './dist/key-registry.js';
|
|
4
|
+
export { default } from './dist/key-registry.js';
|
package/mcp.js
CHANGED
|
@@ -1,110 +1,4 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
* a dangerous MCP tool into a receipt-required one.
|
|
6
|
-
*
|
|
7
|
-
* import { createTrustedActionFirewall } from '@emilia-protocol/gate';
|
|
8
|
-
* import { gateMcpTool } from '@emilia-protocol/gate/mcp';
|
|
9
|
-
* const gate = createTrustedActionFirewall({ trustedKeys: [ISSUER], store: sharedConsumptionStore });
|
|
10
|
-
*
|
|
11
|
-
* server.tool('release_payment',
|
|
12
|
-
* gateMcpTool(gate, { tool: 'release_payment' }, async (args) => actuallyPay(args)));
|
|
13
|
-
*
|
|
14
|
-
* A guarded call with no valid, sufficiently-assured, non-replayed receipt
|
|
15
|
-
* returns a structured MCP error (isError) carrying the Receipt-Required
|
|
16
|
-
* challenge, so the agent knows to go get a human/quorum to authorize THIS exact
|
|
17
|
-
* action. On success the tool runs and an execution proof + reliance packet are
|
|
18
|
-
* attached under `_emilia`.
|
|
19
|
-
*
|
|
20
|
-
* Receipt resolution order (override with opts.receipt): args._emilia_receipt,
|
|
21
|
-
* args.emilia_receipt, then a base64 string in args._emilia_receipt_b64.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import { parseReceiptCarrier } from '@emilia-protocol/require-receipt';
|
|
25
|
-
|
|
26
|
-
function resolveReceipt(args, opts) {
|
|
27
|
-
if (typeof opts.receipt === 'function') return opts.receipt(args);
|
|
28
|
-
if (opts.receipt) return opts.receipt;
|
|
29
|
-
if (args && typeof args === 'object') {
|
|
30
|
-
if (args._emilia_receipt) return args._emilia_receipt;
|
|
31
|
-
if (args.emilia_receipt) return args.emilia_receipt;
|
|
32
|
-
if (typeof args._emilia_receipt_b64 === 'string') {
|
|
33
|
-
return parseReceiptCarrier(args._emilia_receipt_b64);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Wrap a single MCP tool handler so it runs only behind a passing gate check.
|
|
41
|
-
* @param {object} gate an EMILIA Gate (createGate/createTrustedActionFirewall)
|
|
42
|
-
* @param {object} o
|
|
43
|
-
* @param {string} o.tool the MCP tool name (matched against the manifest)
|
|
44
|
-
* @param {string} [o.protocol='mcp']
|
|
45
|
-
* @param {string} [o.action] explicit action_type (else resolved by the manifest from {protocol,tool})
|
|
46
|
-
* @param {object|function} [o.observedAction] the system-of-record facts to bind (default: the tool args)
|
|
47
|
-
* @param {object|function} [o.receipt] override receipt resolution
|
|
48
|
-
* @param {function} handler the real tool implementation (args, extra) => result
|
|
49
|
-
* @returns {function} a gated MCP tool handler
|
|
50
|
-
*/
|
|
51
|
-
export function gateMcpTool(gate, o = {}, handler) {
|
|
52
|
-
if (!gate || typeof gate.run !== 'function') throw new Error('gateMcpTool requires an EMILIA Gate (with .run)');
|
|
53
|
-
if (typeof handler !== 'function') throw new Error('gateMcpTool requires a tool handler function');
|
|
54
|
-
const { tool, protocol = 'mcp', action } = o;
|
|
55
|
-
if (!tool) throw new Error('gateMcpTool requires { tool }');
|
|
56
|
-
|
|
57
|
-
const refused = (reason, body = null) => ({
|
|
58
|
-
isError: true,
|
|
59
|
-
content: [{
|
|
60
|
-
type: 'text',
|
|
61
|
-
text: `EMILIA Gate refused "${tool}": ${reason}. `
|
|
62
|
-
+ 'This is a high-risk action; present a valid, sufficiently-assured, unused human/quorum receipt.',
|
|
63
|
-
}],
|
|
64
|
-
_emilia: {
|
|
65
|
-
gate: 'refused',
|
|
66
|
-
status: 428,
|
|
67
|
-
reason,
|
|
68
|
-
challenge: body,
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
return async function gatedTool(args = {}, extra) {
|
|
73
|
-
const selector = { protocol, tool, ...(action ? { action_type: action } : {}) };
|
|
74
|
-
let receipt;
|
|
75
|
-
let observedAction;
|
|
76
|
-
try {
|
|
77
|
-
receipt = resolveReceipt(args, o);
|
|
78
|
-
observedAction = typeof o.observedAction === 'function'
|
|
79
|
-
? o.observedAction(args, extra)
|
|
80
|
-
: (o.observedAction ?? args);
|
|
81
|
-
} catch {
|
|
82
|
-
return refused('receipt_boundary_failed');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const out = await gate.run({ selector, receipt, observedAction }, () => handler(args, extra));
|
|
86
|
-
if (!out.ok) {
|
|
87
|
-
return refused(out.authorization.reason, out.body);
|
|
88
|
-
}
|
|
89
|
-
const result = out.result;
|
|
90
|
-
// Attach the proof without clobbering a structured tool result.
|
|
91
|
-
if (result && typeof result === 'object' && !Array.isArray(result)) {
|
|
92
|
-
return { ...result, _emilia: { gate: 'allowed', execution: out.execution, reliance: out.packet } };
|
|
93
|
-
}
|
|
94
|
-
return { result, _emilia: { gate: 'allowed', execution: out.execution, reliance: out.packet } };
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Convenience: wrap a map of { toolName: handler } in one call. Tools not named
|
|
100
|
-
* in the manifest still pass through the gate (which lets non-guarded tools run).
|
|
101
|
-
*/
|
|
102
|
-
export function gateMcpTools(gate, handlers = {}, opts = {}) {
|
|
103
|
-
const wrapped = {};
|
|
104
|
-
for (const [tool, handler] of Object.entries(handlers)) {
|
|
105
|
-
wrapped[tool] = gateMcpTool(gate, { ...opts, tool }, handler);
|
|
106
|
-
}
|
|
107
|
-
return wrapped;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export default { gateMcpTool, gateMcpTools };
|
|
2
|
+
// Compatibility entry point for the TypeScript mcp module.
|
|
3
|
+
export * from './dist/mcp.js';
|
|
4
|
+
export { default } from './dist/mcp.js';
|
package/metering.js
CHANGED
|
@@ -1,227 +1,4 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
* Pricing scales with what the gate actually protects: PROTECTED IRREVERSIBLE
|
|
6
|
-
* ACTIONS (guarded decisions — allows AND denies both consume enforcement) plus
|
|
7
|
-
* RECEIPT-YEARS under retention (one receipt kept one year = one receipt-year;
|
|
8
|
-
* horizons follow retention.js, default 6y = 2190 days). NEVER seats.
|
|
9
|
-
*
|
|
10
|
-
* Pure functions over evidence-log entries (`evidence.all()`); no clock, no
|
|
11
|
-
* network. The billing period is an explicit [periodStart, periodEnd) window —
|
|
12
|
-
* INCLUSIVE start, EXCLUSIVE end — so adjacent periods never double-count an
|
|
13
|
-
* entry sitting exactly on a boundary.
|
|
14
|
-
*
|
|
15
|
-
* Fail-closed posture for billing: an unbounded or reversed period is refused
|
|
16
|
-
* (thrown); malformed entries are surfaced in `integrity_warnings` rather than
|
|
17
|
-
* silently dropped; an entry whose allow flag is not literally `true` is
|
|
18
|
-
* counted as a deny; a statement over foreign or malformed usage is refused.
|
|
19
|
-
* `buildUsageStatement` output is deterministic (sorted keys + content hash)
|
|
20
|
-
* and UNSIGNED — the deployer signs it; the content hash binds that signature
|
|
21
|
-
* to exactly these numbers for billing reconciliation.
|
|
22
|
-
*/
|
|
23
|
-
import crypto from 'node:crypto';
|
|
24
|
-
|
|
25
|
-
export const USAGE_VERSION = 'EP-GATE-USAGE-v1';
|
|
26
|
-
|
|
27
|
-
const DAYS_PER_YEAR = 365;
|
|
28
|
-
// Mirrors retention.js's cold-horizon default (coldDays = 2190 = 6y): a receipt
|
|
29
|
-
// is presumed retained for the full audit horizon unless the entry states its own.
|
|
30
|
-
const DEFAULT_RETENTION_YEARS = 2190 / DAYS_PER_YEAR;
|
|
31
|
-
|
|
32
|
-
function sha256hex(s) {
|
|
33
|
-
return crypto.createHash('sha256').update(s).digest('hex');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Canonical JSON (recursive sorted keys) — matches evidence.js / @emilia-protocol/verify. */
|
|
37
|
-
function canonical(v) {
|
|
38
|
-
if (v === null || v === undefined) return JSON.stringify(v);
|
|
39
|
-
if (Array.isArray(v)) return `[${v.map(canonical).join(',')}]`;
|
|
40
|
-
if (typeof v === 'object') {
|
|
41
|
-
return `{${Object.keys(v).sort().map((k) => JSON.stringify(k) + ':' + canonical(v[k])).join(',')}}`;
|
|
42
|
-
}
|
|
43
|
-
return JSON.stringify(v);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function toMs(t) {
|
|
47
|
-
if (t == null) return null;
|
|
48
|
-
const ms = typeof t === 'number' ? t : Date.parse(t);
|
|
49
|
-
return Number.isFinite(ms) ? ms : null;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** Key-sorted copy so output is byte-stable regardless of entry order. */
|
|
53
|
-
function sortedCounts(map) {
|
|
54
|
-
const out = {};
|
|
55
|
-
for (const k of Object.keys(map).sort()) out[k] = map[k];
|
|
56
|
-
return out;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** Entry-level day resolution (1/365y ≈ 0.00274) without float noise. */
|
|
60
|
-
function round6(x) {
|
|
61
|
-
return Math.round(x * 1e6) / 1e6;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* The retention this entry STATES for itself, in years.
|
|
66
|
-
* @returns {number|null|NaN} years, `null` when unstated (default applies),
|
|
67
|
-
* `NaN` when stated but invalid (default applies + integrity warning — a
|
|
68
|
-
* malformed stated value must never silently shrink the metered total).
|
|
69
|
-
*/
|
|
70
|
-
function statedRetentionYears(e) {
|
|
71
|
-
if (e.retention_years != null) {
|
|
72
|
-
const y = Number(e.retention_years);
|
|
73
|
-
return Number.isFinite(y) && y >= 0 ? y : NaN;
|
|
74
|
-
}
|
|
75
|
-
if (e.retention_days != null) {
|
|
76
|
-
const d = Number(e.retention_days);
|
|
77
|
-
return Number.isFinite(d) && d >= 0 ? d / DAYS_PER_YEAR : NaN;
|
|
78
|
-
}
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Meter a billing period over evidence entries.
|
|
84
|
-
*
|
|
85
|
-
* Billable = `kind: 'decision'` entries on GUARDED actions (`not_guarded`
|
|
86
|
-
* pass-throughs are free; execution records are provenance, not enforcement).
|
|
87
|
-
* Window is [periodStart, periodEnd): inclusive start, exclusive end.
|
|
88
|
-
*
|
|
89
|
-
* @param {Array<object>} entries evidence.all()
|
|
90
|
-
* @param {object} o
|
|
91
|
-
* @param {string|number} o.periodStart ISO or ms — required
|
|
92
|
-
* @param {string|number} o.periodEnd ISO or ms — required, >= periodStart
|
|
93
|
-
* @param {number} [o.retentionYearsDefault=6] applied when an entry states no retention
|
|
94
|
-
* @returns {{protected_actions:number, allows:number, denies:number,
|
|
95
|
-
* replays_blocked:number, by_action_type:object, by_tier:object,
|
|
96
|
-
* receipt_years:number, period:object, integrity_warnings:object[]}}
|
|
97
|
-
*/
|
|
98
|
-
export function meterUsage(entries = [], { periodStart, periodEnd, retentionYearsDefault = DEFAULT_RETENTION_YEARS } = {}) {
|
|
99
|
-
const startMs = toMs(periodStart);
|
|
100
|
-
const endMs = toMs(periodEnd);
|
|
101
|
-
// An unbounded or reversed period is not meterable — refuse rather than emit
|
|
102
|
-
// a statement whose window cannot be reconciled.
|
|
103
|
-
if (startMs == null || endMs == null) {
|
|
104
|
-
throw new Error('meterUsage: periodStart and periodEnd are required (ISO string or ms)');
|
|
105
|
-
}
|
|
106
|
-
if (endMs < startMs) throw new Error('meterUsage: periodEnd must be >= periodStart');
|
|
107
|
-
if (!Number.isFinite(retentionYearsDefault) || retentionYearsDefault < 0) {
|
|
108
|
-
throw new Error('meterUsage: retentionYearsDefault must be a finite number >= 0');
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const warnings = [];
|
|
112
|
-
const byAction = Object.create(null);
|
|
113
|
-
const byTier = Object.create(null);
|
|
114
|
-
let protectedActions = 0;
|
|
115
|
-
let allows = 0;
|
|
116
|
-
let denies = 0;
|
|
117
|
-
let replaysBlocked = 0;
|
|
118
|
-
let receiptYears = 0;
|
|
119
|
-
|
|
120
|
-
entries.forEach((e, index) => {
|
|
121
|
-
if (!e || typeof e !== 'object' || Array.isArray(e)) {
|
|
122
|
-
warnings.push({ index, reason: 'not_an_object' });
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
const t = toMs(e.at);
|
|
126
|
-
if (t == null) {
|
|
127
|
-
// Unplaceable in ANY period — surfaced, never silently dropped.
|
|
128
|
-
warnings.push({ index, reason: 'unparseable_at' });
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
if (t < startMs || t >= endMs) return; // outside [start, end)
|
|
132
|
-
if (typeof e.kind !== 'string' || e.kind.length === 0) {
|
|
133
|
-
warnings.push({ index, reason: 'missing_kind' });
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
if (e.kind !== 'decision') return; // execution/other records are not billable
|
|
137
|
-
if (e.reason === 'not_guarded') return; // pass-throughs are free by design
|
|
138
|
-
|
|
139
|
-
protectedActions += 1;
|
|
140
|
-
// Fail closed: only a literal `true` counts as an allow.
|
|
141
|
-
if (e.allow === true) allows += 1; else denies += 1;
|
|
142
|
-
if (e.reason === 'replay_refused') replaysBlocked += 1;
|
|
143
|
-
|
|
144
|
-
const action = typeof e.action === 'string' && e.action ? e.action : 'unknown';
|
|
145
|
-
byAction[action] = (byAction[action] || 0) + 1;
|
|
146
|
-
const tier = typeof e.required_tier === 'string' && e.required_tier ? e.required_tier : 'unknown';
|
|
147
|
-
byTier[tier] = (byTier[tier] || 0) + 1;
|
|
148
|
-
|
|
149
|
-
const stated = statedRetentionYears(e);
|
|
150
|
-
if (Number.isNaN(stated)) {
|
|
151
|
-
warnings.push({ index, reason: 'invalid_stated_retention' });
|
|
152
|
-
receiptYears += retentionYearsDefault;
|
|
153
|
-
} else {
|
|
154
|
-
receiptYears += stated ?? retentionYearsDefault;
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
return {
|
|
159
|
-
'@version': USAGE_VERSION,
|
|
160
|
-
period: {
|
|
161
|
-
start: new Date(startMs).toISOString(),
|
|
162
|
-
end: new Date(endMs).toISOString(),
|
|
163
|
-
bounds: 'inclusive_start_exclusive_end',
|
|
164
|
-
},
|
|
165
|
-
protected_actions: protectedActions,
|
|
166
|
-
allows,
|
|
167
|
-
denies,
|
|
168
|
-
replays_blocked: replaysBlocked,
|
|
169
|
-
by_action_type: sortedCounts(byAction),
|
|
170
|
-
by_tier: sortedCounts(byTier),
|
|
171
|
-
receipt_years: round6(receiptYears),
|
|
172
|
-
retention_years_default: retentionYearsDefault,
|
|
173
|
-
integrity_warnings: warnings,
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Build the signed-ready usage statement handed to billing reconciliation.
|
|
179
|
-
* UNSIGNED — the deployer signs it; `content_hash` (sha256 over the canonical
|
|
180
|
-
* JSON of everything else) binds that signature to exactly these numbers.
|
|
181
|
-
* Deterministic: same usage + org → byte-identical statement, regardless of
|
|
182
|
-
* the entry order the usage was metered from.
|
|
183
|
-
*/
|
|
184
|
-
export function buildUsageStatement(usage, { org } = {}) {
|
|
185
|
-
// Never emit a statement over an artifact of a different or unknown format,
|
|
186
|
-
// and never one that fails to name who it bills.
|
|
187
|
-
if (!usage || usage['@version'] !== USAGE_VERSION) {
|
|
188
|
-
throw new Error(`buildUsageStatement: usage must be a ${USAGE_VERSION} object from meterUsage`);
|
|
189
|
-
}
|
|
190
|
-
if (!org || typeof org !== 'string') {
|
|
191
|
-
throw new Error('buildUsageStatement: org is required — a statement must name the billed party');
|
|
192
|
-
}
|
|
193
|
-
if (!usage.period || !usage.period.start || !usage.period.end) {
|
|
194
|
-
throw new Error('buildUsageStatement: usage.period is missing');
|
|
195
|
-
}
|
|
196
|
-
for (const k of ['protected_actions', 'allows', 'denies', 'replays_blocked', 'receipt_years']) {
|
|
197
|
-
if (!Number.isFinite(usage[k]) || usage[k] < 0) {
|
|
198
|
-
throw new Error(`buildUsageStatement: usage.${k} must be a finite number >= 0`);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
const warningCount = Array.isArray(usage.integrity_warnings) ? usage.integrity_warnings.length : 0;
|
|
202
|
-
const body = {
|
|
203
|
-
'@version': USAGE_VERSION,
|
|
204
|
-
kind: 'usage_statement',
|
|
205
|
-
org,
|
|
206
|
-
period: {
|
|
207
|
-
start: usage.period.start,
|
|
208
|
-
end: usage.period.end,
|
|
209
|
-
bounds: 'inclusive_start_exclusive_end',
|
|
210
|
-
},
|
|
211
|
-
protected_actions: usage.protected_actions,
|
|
212
|
-
allows: usage.allows,
|
|
213
|
-
denies: usage.denies,
|
|
214
|
-
replays_blocked: usage.replays_blocked,
|
|
215
|
-
by_action_type: sortedCounts(usage.by_action_type || {}),
|
|
216
|
-
by_tier: sortedCounts(usage.by_tier || {}),
|
|
217
|
-
receipt_years: usage.receipt_years,
|
|
218
|
-
retention_years_default: usage.retention_years_default ?? DEFAULT_RETENTION_YEARS,
|
|
219
|
-
// Data-quality signal for the reconciler: a statement metered over a log
|
|
220
|
-
// with integrity warnings is flagged incomplete, never silently clean.
|
|
221
|
-
integrity_warning_count: warningCount,
|
|
222
|
-
complete: warningCount === 0,
|
|
223
|
-
};
|
|
224
|
-
return { ...body, content_hash: sha256hex(canonical(body)) };
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export default { meterUsage, buildUsageStatement, USAGE_VERSION };
|
|
2
|
+
// Compatibility entry point for the TypeScript metering module.
|
|
3
|
+
export * from './dist/metering.js';
|
|
4
|
+
export { default } from './dist/metering.js';
|