@decionis/agent-safe-pipeline 0.2.0 → 0.3.1

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.
Files changed (34) hide show
  1. package/README.md +1 -0
  2. package/dist/Index.d.ts +1 -0
  3. package/dist/Index.d.ts.map +1 -1
  4. package/dist/Index.js +1 -0
  5. package/dist/Index.js.map +1 -1
  6. package/dist/execution/ActionRegistry.d.ts +11 -0
  7. package/dist/execution/ActionRegistry.d.ts.map +1 -1
  8. package/dist/execution/ActionRegistry.js +13 -3
  9. package/dist/execution/ActionRegistry.js.map +1 -1
  10. package/dist/execution/AuthorizationVerifier.d.ts +27 -0
  11. package/dist/execution/AuthorizationVerifier.d.ts.map +1 -1
  12. package/dist/execution/AuthorizationVerifier.js +34 -5
  13. package/dist/execution/AuthorizationVerifier.js.map +1 -1
  14. package/dist/execution/SafeExecutor.d.ts +2 -1
  15. package/dist/execution/SafeExecutor.d.ts.map +1 -1
  16. package/dist/execution/SafeExecutor.js +7 -5
  17. package/dist/execution/SafeExecutor.js.map +1 -1
  18. package/dist/intent/IntentBindingSchema.d.ts +50 -0
  19. package/dist/intent/IntentBindingSchema.d.ts.map +1 -0
  20. package/dist/intent/IntentBindingSchema.js +73 -0
  21. package/dist/intent/IntentBindingSchema.js.map +1 -0
  22. package/dist/intent/JsonValue.d.ts +5 -0
  23. package/dist/intent/JsonValue.d.ts.map +1 -1
  24. package/dist/intent/JsonValue.js +9 -2
  25. package/dist/intent/JsonValue.js.map +1 -1
  26. package/dist/testing/Index.d.ts +1 -1
  27. package/dist/testing/Index.d.ts.map +1 -1
  28. package/dist/testing/Index.js +1 -1
  29. package/dist/testing/Index.js.map +1 -1
  30. package/dist/testing/LocalAuthority.d.ts +67 -0
  31. package/dist/testing/LocalAuthority.d.ts.map +1 -1
  32. package/dist/testing/LocalAuthority.js +203 -3
  33. package/dist/testing/LocalAuthority.js.map +1 -1
  34. package/package.json +1 -1
@@ -0,0 +1,73 @@
1
+ import { z } from "zod";
2
+ import { JsonObjectSchema, JsonValueSchema } from "./JsonValue.js";
3
+ /** Where the published schema of the binding lives; the `$id` it carries. */
4
+ export const INTENT_SCHEMA_ID = "https://github.com/decionis/agent-safe-pipeline/blob/master/spec/intent/v1/schema.json";
5
+ const identifier = z.string().min(1).max(200);
6
+ const actionType = z
7
+ .string()
8
+ .min(1)
9
+ .max(120)
10
+ .regex(/^[a-z][a-z0-9._:-]*$/);
11
+ const sha256Digest = z.string().regex(/^sha256:[0-9a-f]{64}$/);
12
+ /**
13
+ * The `agent-safe.intent/1` binding as it is hashed and sent, as a strict
14
+ * schema: what `CanonicalIntentHasher.bindingOf` produces, and what an
15
+ * implementation in another language must produce byte for byte before
16
+ * canonicalization. Nothing here trims or coerces, because a validator that
17
+ * altered a binding would be validating different bytes than it hashes.
18
+ * `context` is any JSON object that carries the trusted runtime's
19
+ * `idempotency_key`; every other object refuses a key it does not name.
20
+ */
21
+ export const AuthorityIntentBindingSchema = z
22
+ .object({
23
+ protocol_version: z.literal("agent-safe.intent/1"),
24
+ tenant_id: z.string().uuid(),
25
+ intent_id: z.string().uuid(),
26
+ captured_at: z.string().datetime(),
27
+ expires_at: z.string().datetime(),
28
+ actor: z
29
+ .object({
30
+ id: identifier,
31
+ type: identifier,
32
+ runtime: identifier.optional(),
33
+ trust_level: z.string().min(1).max(80).optional(),
34
+ })
35
+ .strict(),
36
+ action: z
37
+ .object({
38
+ type: actionType,
39
+ resource: z.string().min(1).max(500),
40
+ parameters: JsonObjectSchema,
41
+ })
42
+ .strict(),
43
+ context: z.object({ idempotency_key: z.string().min(1).max(180) }).catchall(JsonValueSchema),
44
+ downstream_target: z
45
+ .object({
46
+ system: identifier,
47
+ operation: identifier,
48
+ environment: identifier.optional(),
49
+ endpoint: z.string().min(1).max(500).optional(),
50
+ })
51
+ .strict(),
52
+ expected_effect_digest: sha256Digest.optional(),
53
+ })
54
+ .strict()
55
+ .meta({
56
+ title: "agent-safe.intent/1 binding",
57
+ description: "The execution intent as the authority receives it and as it is hashed: keys sorted by UTF-16 code unit, encoded as JSON without whitespace, and digested with SHA-256. Every property is inside the hash.",
58
+ });
59
+ /**
60
+ * The binding's JSON Schema (draft 2020-12), the document published at
61
+ * `spec/intent/v1/schema.json`. It is derived from the schema above, so the
62
+ * published file and the validator cannot disagree; a test holds the file to
63
+ * this output.
64
+ */
65
+ export function intentBindingJsonSchema() {
66
+ const generated = z.toJSONSchema(AuthorityIntentBindingSchema, {
67
+ target: "draft-2020-12",
68
+ io: "input",
69
+ });
70
+ const { $schema, ...rest } = generated;
71
+ return { $schema, $id: INTENT_SCHEMA_ID, ...rest };
72
+ }
73
+ //# sourceMappingURL=IntentBindingSchema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IntentBindingSchema.js","sourceRoot":"","sources":["../../src/intent/IntentBindingSchema.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,11 @@ export type JsonValue = JsonPrimitive | JsonValue[] | {
6
6
  export type JsonObject = {
7
7
  [key: string]: JsonValue;
8
8
  };
9
+ /**
10
+ * Named in the schema registry so that the published JSON Schema of the
11
+ * intent binding (`intentBindingJsonSchema`) refers to it as `JsonValue`
12
+ * rather than by a generated name; parsing is unaffected.
13
+ */
9
14
  export declare const JsonValueSchema: z.ZodType<JsonValue>;
10
15
  export declare const JsonObjectSchema: z.ZodType<JsonObject>;
11
16
  //# sourceMappingURL=JsonValue.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"JsonValue.d.ts","sourceRoot":"","sources":["../../src/intent/JsonValue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAC7D,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,SAAS,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AACnF,MAAM,MAAM,UAAU,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEtD,eAAO,MAAM,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAShD,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAyC,CAAC"}
1
+ {"version":3,"file":"JsonValue.d.ts","sourceRoot":"","sources":["../../src/intent/JsonValue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAC7D,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,SAAS,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AACnF,MAAM,MAAM,UAAU,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAW+B,CAAC;AAEjF,eAAO,MAAM,gBAAgB,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAyC,CAAC"}
@@ -1,11 +1,18 @@
1
1
  import { z } from "zod";
2
- export const JsonValueSchema = z.lazy(() => z.union([
2
+ /**
3
+ * Named in the schema registry so that the published JSON Schema of the
4
+ * intent binding (`intentBindingJsonSchema`) refers to it as `JsonValue`
5
+ * rather than by a generated name; parsing is unaffected.
6
+ */
7
+ export const JsonValueSchema = z
8
+ .lazy(() => z.union([
3
9
  z.boolean(),
4
10
  z.number().finite(),
5
11
  z.string(),
6
12
  z.null(),
7
13
  z.array(JsonValueSchema),
8
14
  z.record(z.string(), JsonValueSchema),
9
- ]));
15
+ ]))
16
+ .meta({ id: "JsonValue", description: "Any JSON value; numbers are finite." });
10
17
  export const JsonObjectSchema = z.record(z.string(), JsonValueSchema);
11
18
  //# sourceMappingURL=JsonValue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"JsonValue.js","sourceRoot":"","sources":["../../src/intent/JsonValue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,MAAM,CAAC,MAAM,eAAe,GAAyB,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAC/D,CAAC,CAAC,KAAK,CAAC;IACN,CAAC,CAAC,OAAO,EAAE;IACX,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE;IACnB,CAAC,CAAC,MAAM,EAAE;IACV,CAAC,CAAC,IAAI,EAAE;IACR,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC;IACxB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC;CACtC,CAAC,CACH,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAA0B,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC"}
1
+ {"version":3,"file":"JsonValue.js","sourceRoot":"","sources":["../../src/intent/JsonValue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAyB,CAAC;KACnD,IAAI,CAAC,GAAG,EAAE,CACT,CAAC,CAAC,KAAK,CAAC;IACN,CAAC,CAAC,OAAO,EAAE;IACX,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE;IACnB,CAAC,CAAC,MAAM,EAAE;IACV,CAAC,CAAC,IAAI,EAAE;IACR,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC;IACxB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC;CACtC,CAAC,CACH;KACA,IAAI,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC,CAAC;AAEjF,MAAM,CAAC,MAAM,gBAAgB,GAA0B,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC"}
@@ -8,5 +8,5 @@
8
8
  export { FixtureAuthorizationVerifier, FixtureDecisionAuthority, createFixtureAuthorityPair, type FixtureAuthorityPair, type FixtureCommitRecord, type FixtureVerdictResolver, type UnsafeFixtureAuthorityOptions, } from "../decision/FixtureDecisionAuthority.js";
9
9
  export { InMemoryReplayStore } from "../execution/ReplayStore.js";
10
10
  export { LocalPresence, LOCAL_PRESENCE_API_KEY, readBody, type LocalAuthenticator, type LocalCeremonyResponse, type LocalPresenceOptions, type LocalPresenceRequestRecord, type LocalReceipt, type LocalReceiptBinding, type LocalReceiptVerification, type LocalVerificationRecord, type LocalVerificationStatus, } from "./LocalPresence.js";
11
- export { AUTONOMOUS_LIMIT_MINOR, CLAIM_ATTESTATION_TYPE, HUMAN_LIMIT_MINOR, LocalAuthority, LOCAL_AUTHORITY_API_KEY, LOCAL_AUTHORITY_ISSUER, LOCAL_AUTHORITY_JWKS_PATH, hashBinding, stableStringify, type LocalAuthorityOptions, type LocalClaimAttestationClaims, type LocalAuthorityPolicy, type LocalAuthorityRequestRecord, type LocalGrantRecord, type LocalManagedEscalationRecord, type LocalRouteOverride, } from "./LocalAuthority.js";
11
+ export { AUTONOMOUS_LIMIT_MINOR, CLAIM_ATTESTATION_TYPE, EFFECT_RECEIPT_TYPE, HUMAN_LIMIT_MINOR, LocalAuthority, LOCAL_AUTHORITY_API_KEY, LOCAL_AUTHORITY_ISSUER, LOCAL_AUTHORITY_JWKS_PATH, hashBinding, stableStringify, type LocalAuthorityOptions, type LocalClaimAttestationClaims, type LocalEffectReceiptCode, type LocalEffectReceiptRecord, type LocalProviderKey, type LocalAuthorityPolicy, type LocalAuthorityRequestRecord, type LocalGrantRecord, type LocalManagedEscalationRecord, type LocalRouteOverride, } from "./LocalAuthority.js";
12
12
  //# sourceMappingURL=Index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Index.d.ts","sourceRoot":"","sources":["../../src/testing/Index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,0BAA0B,EAC1B,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,6BAA6B,GACnC,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EACL,aAAa,EACb,sBAAsB,EACtB,QAAQ,EACR,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,iBAAiB,EACjB,cAAc,EACd,uBAAuB,EACvB,sBAAsB,EACtB,yBAAyB,EACzB,WAAW,EACX,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,gBAAgB,EACrB,KAAK,4BAA4B,EACjC,KAAK,kBAAkB,GACxB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"Index.d.ts","sourceRoot":"","sources":["../../src/testing/Index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,0BAA0B,EAC1B,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,6BAA6B,GACnC,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EACL,aAAa,EACb,sBAAsB,EACtB,QAAQ,EACR,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,uBAAuB,EACvB,sBAAsB,EACtB,yBAAyB,EACzB,WAAW,EACX,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,gBAAgB,EACrB,KAAK,4BAA4B,EACjC,KAAK,kBAAkB,GACxB,MAAM,qBAAqB,CAAC"}
@@ -8,5 +8,5 @@
8
8
  export { FixtureAuthorizationVerifier, FixtureDecisionAuthority, createFixtureAuthorityPair, } from "../decision/FixtureDecisionAuthority.js";
9
9
  export { InMemoryReplayStore } from "../execution/ReplayStore.js";
10
10
  export { LocalPresence, LOCAL_PRESENCE_API_KEY, readBody, } from "./LocalPresence.js";
11
- export { AUTONOMOUS_LIMIT_MINOR, CLAIM_ATTESTATION_TYPE, HUMAN_LIMIT_MINOR, LocalAuthority, LOCAL_AUTHORITY_API_KEY, LOCAL_AUTHORITY_ISSUER, LOCAL_AUTHORITY_JWKS_PATH, hashBinding, stableStringify, } from "./LocalAuthority.js";
11
+ export { AUTONOMOUS_LIMIT_MINOR, CLAIM_ATTESTATION_TYPE, EFFECT_RECEIPT_TYPE, HUMAN_LIMIT_MINOR, LocalAuthority, LOCAL_AUTHORITY_API_KEY, LOCAL_AUTHORITY_ISSUER, LOCAL_AUTHORITY_JWKS_PATH, hashBinding, stableStringify, } from "./LocalAuthority.js";
12
12
  //# sourceMappingURL=Index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Index.js","sourceRoot":"","sources":["../../src/testing/Index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,0BAA0B,GAK3B,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EACL,aAAa,EACb,sBAAsB,EACtB,QAAQ,GAUT,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,iBAAiB,EACjB,cAAc,EACd,uBAAuB,EACvB,sBAAsB,EACtB,yBAAyB,EACzB,WAAW,EACX,eAAe,GAQhB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"Index.js","sourceRoot":"","sources":["../../src/testing/Index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,0BAA0B,GAK3B,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EACL,aAAa,EACb,sBAAsB,EACtB,QAAQ,GAUT,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,uBAAuB,EACvB,sBAAsB,EACtB,yBAAyB,EACzB,WAAW,EACX,eAAe,GAWhB,MAAM,qBAAqB,CAAC"}
@@ -1,3 +1,21 @@
1
+ /**
2
+ * Loopback stand-in for the Decionis execution-authority routes that
3
+ * `@decionis/agent-safe-pipeline` consumes: enforce-and-bind (including
4
+ * shadow mode and managed escalation), escalation status and cancellation,
5
+ * claim-token with its consume-token alias, and finalize-token.
6
+ *
7
+ * It validates every request against the published OpenAPI contract with its
8
+ * own canonicalizer, so a hash it recomputes is independent evidence that the
9
+ * package binds intents the way Decionis does. Direct Presence evidence is
10
+ * verified against a `LocalPresence`, and a managed escalation is orchestrated
11
+ * through that same `LocalPresence` the way Decionis orchestrates Presence:
12
+ * create a request bound to the exact intent hash, wait for the ceremony,
13
+ * verify the receipt, re-evaluate policy, then expose a normal grant.
14
+ *
15
+ * Grants, claims, escalations, and commit outcomes are synthetic in-memory
16
+ * state. Nothing here is a real policy, tenant, or credential.
17
+ */
18
+ import { type KeyObject } from "node:crypto";
1
19
  import { z } from "zod";
2
20
  import { type LocalPresence } from "./LocalPresence.js";
3
21
  export declare const LOCAL_AUTHORITY_API_KEY = "test-key";
@@ -8,6 +26,8 @@ export declare const LOCAL_AUTHORITY_ISSUER = "synthetic-authority";
8
26
  /** Where the fixture publishes its attestation key, at the path Decionis documents. */
9
27
  export declare const LOCAL_AUTHORITY_JWKS_PATH = "/.well-known/decionis-execution-grant-jwks.json";
10
28
  export declare const CLAIM_ATTESTATION_TYPE = "decionis-claim-attestation+jwt";
29
+ /** The protected header `typ` of a verifying provider's effect receipt (VP-3). */
30
+ export declare const EFFECT_RECEIPT_TYPE = "decionis-effect-receipt+jwt";
11
31
  /** `ExecutionAuthorityRequest`: the binding plus hash, mode, evidence, and managed constraints. */
12
32
  declare const AuthorityRequestSchema: z.ZodObject<{
13
33
  intent_hash: z.ZodString;
@@ -119,6 +139,28 @@ export interface LocalGrantRecord {
119
139
  correlationId: string | null;
120
140
  consumedBy: string | null;
121
141
  finalized: "COMMITTED" | "FAILED" | "INDETERMINATE" | null;
142
+ /** The provider's effect receipt as this authority read it at finalization, when one came. */
143
+ receipt: LocalEffectReceiptRecord | null;
144
+ }
145
+ /** The verdict codes the hosted authority records for a receipt, and this double with it. */
146
+ export type LocalEffectReceiptCode = "EFFECT_RECEIPT_VERIFIED" | "EFFECT_RECEIPT_MALFORMED" | "EFFECT_RECEIPT_KEY_UNKNOWN" | "EFFECT_RECEIPT_SIGNATURE_INVALID" | "EFFECT_RECEIPT_BINDING_MISMATCH";
147
+ export interface LocalEffectReceiptRecord {
148
+ readonly token: string;
149
+ readonly verified: boolean;
150
+ readonly verification_code: LocalEffectReceiptCode;
151
+ readonly provider_key_id: string | null;
152
+ readonly issuer: string | null;
153
+ readonly effect_status: "EFFECTED" | "REFUSED" | "INDETERMINATE" | null;
154
+ readonly effect_digest: string | null;
155
+ readonly effect_reference: string | null;
156
+ readonly effected_at: string | null;
157
+ }
158
+ /** A verifying provider's registered receipt key: the public half, its `kid`, the `iss` it signs as. */
159
+ export interface LocalProviderKey {
160
+ readonly kid: string;
161
+ readonly issuer: string;
162
+ readonly publicKey: KeyObject;
163
+ readonly label: string | null;
122
164
  }
123
165
  export interface LocalManagedEscalationRecord {
124
166
  readonly escalationId: string;
@@ -200,6 +242,8 @@ export declare class LocalAuthority {
200
242
  readonly grants: Map<string, LocalGrantRecord>;
201
243
  /** Managed escalations keyed by their opaque identifier. */
202
244
  readonly escalations: Map<string, LocalManagedEscalationRecord>;
245
+ /** The verifying providers' receipt keys, by `kid`, as `/v1/execution/provider-keys` registers them. */
246
+ readonly providerKeys: Map<string, LocalProviderKey>;
203
247
  private readonly apiKey;
204
248
  private readonly presence;
205
249
  private readonly legacyVerifyReceipt;
@@ -237,6 +281,27 @@ export declare class LocalAuthority {
237
281
  * `node:crypto` alone so the fixture stays an independent implementation.
238
282
  */
239
283
  private attest;
284
+ /**
285
+ * Registers the public half of a provider's receipt key, as the hosted
286
+ * `POST /v1/execution/provider-keys` does; a test or a demo provider calls
287
+ * it directly rather than over HTTP.
288
+ */
289
+ registerProviderKey(input: {
290
+ readonly kid: string;
291
+ readonly issuer: string;
292
+ readonly publicJwk: JsonWebKey;
293
+ readonly label?: string;
294
+ }): void;
295
+ /**
296
+ * Reads a receipt the way the hosted authority does: the protected header
297
+ * names an Ed25519 key the organisation registered; the signature verifies
298
+ * under it; the issuer is the one registered with the key and the audience
299
+ * is this authority; and the claims describe this grant and this claim,
300
+ * through `sub`, the decision and dossier ids and the digest of the claim
301
+ * token being finalized. Anything else is recorded with its code, and the
302
+ * finalization proceeds regardless.
303
+ */
304
+ private readEffectReceipt;
240
305
  get baseUrl(): string;
241
306
  start(): Promise<void>;
242
307
  stop(): Promise<void>;
@@ -249,6 +314,8 @@ export declare class LocalAuthority {
249
314
  /** Statuses returned, in order, for the next managed escalation instead of orchestration. */
250
315
  scriptNextManagedLifecycle(statuses: readonly LifecycleEntry[]): void;
251
316
  private handle;
317
+ /** `POST /v1/execution/provider-keys`: the public half only, or a 400 naming why. */
318
+ private registerProviderKeyRoute;
252
319
  private enforceAndBind;
253
320
  /** Keeps a minted decision for its dossier route, with the envelope when links are on. */
254
321
  private remember;
@@ -1 +1 @@
1
- {"version":3,"file":"LocalAuthority.d.ts","sourceRoot":"","sources":["../../src/testing/LocalAuthority.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LocalAuthority.d.ts","sourceRoot":"","sources":["../../src/testing/LocalAuthority.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAElE,eAAO,MAAM,uBAAuB,aAAa,CAAC;AAClD,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAC7C,eAAO,MAAM,iBAAiB,SAAU,CAAC;AAQzC,0FAA0F;AAC1F,eAAO,MAAM,sBAAsB,wBAAwB,CAAC;AAC5D,uFAAuF;AACvF,eAAO,MAAM,yBAAyB,oDAAoD,CAAC;AAC3F,eAAO,MAAM,sBAAsB,mCAAmC,CAAC;AACvE,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,gCAAgC,CAAC;AA4HjE,mGAAmG;AACnG,QAAA,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAM1B,CAAC;AA+CH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE3E,KAAK,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;AAC9C,KAAK,aAAa,GACd,kBAAkB,GAClB,oBAAoB,GACpB,mBAAmB,GACnB,mBAAmB,GACnB,eAAe,GACf,aAAa,GACb,SAAS,GACT,UAAU,GACV,SAAS,GACT,WAAW,GACX,QAAQ,CAAC;AACb,KAAK,cAAc,GAAG,aAAa,GAAG;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AACxF,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAOxC,yFAAyF;AACzF,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,EAAE,qBAAqB,KAAK,OAAO,CAAC;AAE/E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;IAChD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAE9E,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1D,IAAI,EAAE,OAAO,CAAC;IACd,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2EAA2E;IAC3E,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,0FAA0F;IAC1F,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C,6FAA6F;IAC7F,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,WAAW,GAAG,QAAQ,GAAG,eAAe,GAAG,IAAI,CAAC;IAC3D,8FAA8F;IAC9F,OAAO,EAAE,wBAAwB,GAAG,IAAI,CAAC;CAC1C;AAED,6FAA6F;AAC7F,MAAM,MAAM,sBAAsB,GAC9B,yBAAyB,GACzB,0BAA0B,GAC1B,4BAA4B,GAC5B,kCAAkC,GAClC,iCAAiC,CAAC;AAEtC,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,iBAAiB,EAAE,sBAAsB,CAAC;IACnD,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,UAAU,GAAG,SAAS,GAAG,eAAe,GAAG,IAAI,CAAC;IACxE,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAMD,wGAAwG;AACxG,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAC;IACxC,mFAAmF;IACnF,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,gGAAgG;IAChG,SAAS,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,aAAa,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,eAAe,EAAE,QAAQ,CAAC;CACpC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC;IAClC,yDAAyD;IACzD,QAAQ,CAAC,aAAa,CAAC,EAAE,CACvB,QAAQ,EAAE;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,EAC3E,UAAU,EAAE,MAAM,KACf,OAAO,CAAC;IACb,QAAQ,CAAC,MAAM,CAAC,EAAE,oBAAoB,CAAC;IACvC,gFAAgF;IAChF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC;;;;;;;OAOG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CACtC;AAED,qEAAqE;AACrE,eAAO,MAAM,kCAAkC,yCAAyC,CAAC;AAEzF,+FAA+F;AAC/F,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC;QAC1C,QAAQ,CAAC,0CAA0C,EAAE,MAAM,CAAC;QAC5D,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;QACzC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;QACjC,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC;KAC3C,CAAC;IACF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAStD;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAEpD;AAyBD,qBAAa,cAAc;IACzB,oFAAoF;IACpF,SAAgB,QAAQ,EAAE,2BAA2B,EAAE,CAAM;IAC7D,8CAA8C;IAC9C,SAAgB,MAAM,gCAAuC;IAC7D,4DAA4D;IAC5D,SAAgB,WAAW,4CAAmD;IAC9E,wGAAwG;IACxG,SAAgB,YAAY,gCAAuC;IACnE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;IACrD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAyC;IAC7E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAC9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAoB;IAC7D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAG7B;IACF,4FAA4F;IAC5F,SAAgB,gBAAgB,4BAA4B;IAC5D;;;;;OAKG;IACI,wBAAwB,UAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAKxB;IACF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAmD;IACnF,OAAO,CAAC,oBAAoB,CAAiC;IAC7D,OAAO,CAAC,SAAS,CAAK;IACtB,kFAAkF;IAClF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAGrB;IACJ,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAU;IAC5C,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,IAAI,CAAK;gBAEE,OAAO,GAAE,qBAA0B;IAiBtD,8FAA8F;IAC9F,IAAW,IAAI,IAAI;QAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAMvE;IAED;;;;OAIG;IACH,OAAO,CAAC,MAAM;IA+Cd;;;;OAIG;IACI,mBAAmB,CAAC,KAAK,EAAE;QAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;QAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;KACzB,GAAG,IAAI;IASR;;;;;;;;OAQG;IACH,OAAO,CAAC,iBAAiB;IA2FzB,IAAW,OAAO,IAAI,MAAM,CAE3B;IAEY,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAWtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQlC;;;;OAIG;IACI,UAAU,CAAC,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,kBAAkB,GAAG,IAAI;IAIjF,6FAA6F;IACtF,0BAA0B,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAAG,IAAI;YAI9D,MAAM;IA4FpB,qFAAqF;IACrF,OAAO,CAAC,wBAAwB;IAwBhC,OAAO,CAAC,cAAc;IA2BtB,0FAA0F;IAC1F,OAAO,CAAC,QAAQ;IAkBhB;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IA0CrB,wFAAwF;IACxF,OAAO,CAAC,QAAQ;IA6BhB,OAAO,CAAC,cAAc;IAgBtB,OAAO,CAAC,YAAY;IA+BpB,OAAO,CAAC,aAAa;IAgDrB,qGAAqG;IACrG,OAAO,CAAC,WAAW;IAgDnB,iGAAiG;IACjG,OAAO,CAAC,eAAe;IAiDvB,OAAO,CAAC,aAAa;IA8CrB,OAAO,CAAC,QAAQ;IA8BhB,qFAAqF;IACrF,OAAO,CAAC,OAAO;IAqDf,OAAO,CAAC,aAAa;IAmBrB,OAAO,CAAC,KAAK;IA+Fb,OAAO,CAAC,QAAQ;IAkHhB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAIzB,+EAA+E;IAC/E,OAAO,CAAC,aAAa;YAiBP,OAAO;IAmCrB,OAAO,CAAC,IAAI;IAYZ,OAAO,CAAC,MAAM,CAAC,UAAU;CAU1B"}
@@ -15,7 +15,7 @@
15
15
  * Grants, claims, escalations, and commit outcomes are synthetic in-memory
16
16
  * state. Nothing here is a real policy, tenant, or credential.
17
17
  */
18
- import { createHash, generateKeyPairSync, randomBytes, randomUUID, sign, } from "node:crypto";
18
+ import { createHash, createPublicKey, generateKeyPairSync, randomBytes, randomUUID, sign, verify, } from "node:crypto";
19
19
  import { createServer } from "node:http";
20
20
  import { z } from "zod";
21
21
  import { readBody } from "./LocalPresence.js";
@@ -33,6 +33,9 @@ export const LOCAL_AUTHORITY_ISSUER = "synthetic-authority";
33
33
  /** Where the fixture publishes its attestation key, at the path Decionis documents. */
34
34
  export const LOCAL_AUTHORITY_JWKS_PATH = "/.well-known/decionis-execution-grant-jwks.json";
35
35
  export const CLAIM_ATTESTATION_TYPE = "decionis-claim-attestation+jwt";
36
+ /** The protected header `typ` of a verifying provider's effect receipt (VP-3). */
37
+ export const EFFECT_RECEIPT_TYPE = "decionis-effect-receipt+jwt";
38
+ const COMPACT_JWS = /^[\w-]+\.[\w-]+\.[\w-]+$/;
36
39
  /** The profile the fixture digests parameters under; the only one the pipeline reproduces. */
37
40
  const JCS_PROFILE = "RFC8785/JCS";
38
41
  const boundedId = z.string().trim().min(1).max(200);
@@ -179,8 +182,30 @@ const FinalizeRequestSchema = z.strictObject({
179
182
  // evidence with a 409 and a reason code, not with a 400 request rejection,
180
183
  // so the shape is checked where that answer is produced.
181
184
  effect_evidence: z.unknown().optional(),
185
+ // The provider's receipt: only its shape is a request error. What it says
186
+ // is verified and recorded, never a reason to refuse the finalization.
187
+ effect_receipt: z.string().min(1).max(20_000).regex(COMPACT_JWS).optional(),
188
+ });
189
+ /** `ExecutionProviderKeyRegistrationRequest`: the public half of an Ed25519 key. */
190
+ const ProviderKeyRegistrationSchema = z.strictObject({
191
+ org_id: z.uuid().optional(),
192
+ kid: boundedId,
193
+ issuer: z.string().trim().min(1).max(500),
194
+ algorithm: z.literal("EdDSA"),
195
+ public_jwk: z.strictObject({
196
+ kty: z.literal("OKP"),
197
+ crv: z.literal("Ed25519"),
198
+ x: z.string().regex(/^[\w-]{43}$/),
199
+ kid: z.string().optional(),
200
+ alg: z.literal("EdDSA").optional(),
201
+ use: z.literal("sig").optional(),
202
+ }),
203
+ label: z.string().max(200).optional(),
182
204
  });
183
205
  const BINDING_KEYS = Object.keys(IntentBindingSchema.shape);
206
+ function effectStatus(value) {
207
+ return value === "EFFECTED" || value === "REFUSED" || value === "INDETERMINATE" ? value : null;
208
+ }
184
209
  /** The organization a provisioned synthetic workspace belongs to. */
185
210
  export const LOCAL_AUTHORITY_PROVISIONAL_ORG_ID = "00000000-0000-4000-8000-000000000008";
186
211
  /**
@@ -232,6 +257,8 @@ export class LocalAuthority {
232
257
  grants = new Map();
233
258
  /** Managed escalations keyed by their opaque identifier. */
234
259
  escalations = new Map();
260
+ /** The verifying providers' receipt keys, by `kid`, as `/v1/execution/provider-keys` registers them. */
261
+ providerKeys = new Map();
235
262
  apiKey;
236
263
  presence;
237
264
  legacyVerifyReceipt;
@@ -330,6 +357,103 @@ export class LocalAuthority {
330
357
  const signature = sign(null, Buffer.from(`${header}.${payload}`, "ascii"), this.attestationKey.privateKey);
331
358
  return `${header}.${payload}.${signature.toString("base64url")}`;
332
359
  }
360
+ /**
361
+ * Registers the public half of a provider's receipt key, as the hosted
362
+ * `POST /v1/execution/provider-keys` does; a test or a demo provider calls
363
+ * it directly rather than over HTTP.
364
+ */
365
+ registerProviderKey(input) {
366
+ this.providerKeys.set(input.kid, {
367
+ kid: input.kid,
368
+ issuer: input.issuer,
369
+ publicKey: createPublicKey({ key: input.publicJwk, format: "jwk" }),
370
+ label: input.label ?? null,
371
+ });
372
+ }
373
+ /**
374
+ * Reads a receipt the way the hosted authority does: the protected header
375
+ * names an Ed25519 key the organisation registered; the signature verifies
376
+ * under it; the issuer is the one registered with the key and the audience
377
+ * is this authority; and the claims describe this grant and this claim,
378
+ * through `sub`, the decision and dossier ids and the digest of the claim
379
+ * token being finalized. Anything else is recorded with its code, and the
380
+ * finalization proceeds regardless.
381
+ */
382
+ readEffectReceipt(token, grant, claimToken) {
383
+ const record = (code, partial = {}) => ({
384
+ token,
385
+ verified: code === "EFFECT_RECEIPT_VERIFIED",
386
+ verification_code: code,
387
+ provider_key_id: null,
388
+ issuer: null,
389
+ effect_status: null,
390
+ effect_digest: null,
391
+ effect_reference: null,
392
+ effected_at: null,
393
+ ...partial,
394
+ });
395
+ const [encodedHeader = "", encodedPayload = "", encodedSignature = ""] = token.split(".");
396
+ let header;
397
+ let payload;
398
+ try {
399
+ header = JSON.parse(Buffer.from(encodedHeader, "base64url").toString("utf8"));
400
+ payload = JSON.parse(Buffer.from(encodedPayload, "base64url").toString("utf8"));
401
+ }
402
+ catch {
403
+ return record("EFFECT_RECEIPT_MALFORMED");
404
+ }
405
+ if (header.alg !== "EdDSA" ||
406
+ header.typ !== EFFECT_RECEIPT_TYPE ||
407
+ typeof header.kid !== "string" ||
408
+ header.kid.length === 0) {
409
+ return record("EFFECT_RECEIPT_MALFORMED");
410
+ }
411
+ const key = this.providerKeys.get(header.kid);
412
+ if (key === undefined) {
413
+ return record("EFFECT_RECEIPT_KEY_UNKNOWN", { provider_key_id: header.kid });
414
+ }
415
+ const known = { provider_key_id: key.kid, issuer: key.issuer };
416
+ const signed = Buffer.from(`${encodedHeader}.${encodedPayload}`, "ascii");
417
+ if (!verify(null, signed, key.publicKey, Buffer.from(encodedSignature, "base64url")) ||
418
+ payload.iss !== key.issuer ||
419
+ payload.aud !== LOCAL_AUTHORITY_ISSUER) {
420
+ return record("EFFECT_RECEIPT_SIGNATURE_INVALID", known);
421
+ }
422
+ const effect = typeof payload.effect === "object" && payload.effect !== null
423
+ ? payload.effect
424
+ : null;
425
+ const status = effectStatus(effect?.status);
426
+ const digest = effect?.digest;
427
+ const effectedAt = effect?.effected_at;
428
+ if (typeof payload.sub !== "string" ||
429
+ typeof payload.decision_id !== "string" ||
430
+ typeof payload.dossier_id !== "string" ||
431
+ typeof payload.claim_token_digest !== "string" ||
432
+ typeof payload.jti !== "string" ||
433
+ typeof payload.iat !== "number" ||
434
+ status === null ||
435
+ (digest !== undefined && !sha256Digest.safeParse(digest).success) ||
436
+ typeof effectedAt !== "string" ||
437
+ Number.isNaN(Date.parse(effectedAt))) {
438
+ return record("EFFECT_RECEIPT_MALFORMED", known);
439
+ }
440
+ const described = {
441
+ ...known,
442
+ effect_status: status,
443
+ effect_digest: typeof digest === "string" ? digest : null,
444
+ effect_reference: typeof effect?.reference === "string" ? effect.reference : null,
445
+ effected_at: new Date(effectedAt).toISOString(),
446
+ };
447
+ const claimTokenDigest = `sha256:${createHash("sha256").update(claimToken, "utf8").digest("hex")}`;
448
+ if (payload.sub !== grant.jti ||
449
+ payload.decision_id !== grant.decisionId ||
450
+ payload.dossier_id !== grant.dossierId ||
451
+ payload.claim_token_digest !== claimTokenDigest ||
452
+ (typeof payload.intent_hash === "string" && payload.intent_hash !== grant.intentHash)) {
453
+ return record("EFFECT_RECEIPT_BINDING_MISMATCH", described);
454
+ }
455
+ return record("EFFECT_RECEIPT_VERIFIED", described);
456
+ }
333
457
  get baseUrl() {
334
458
  return `${LOOPBACK_ORIGIN}:${this.port}`;
335
459
  }
@@ -372,6 +496,9 @@ export class LocalAuthority {
372
496
  authorization: headerString(req.headers.authorization),
373
497
  "content-type": headerString(req.headers["content-type"]),
374
498
  "idempotency-key": headerString(req.headers["idempotency-key"]),
499
+ // What the client says about itself, so a test can see the version
500
+ // and the surface a call carried; the double never acts on it.
501
+ "user-agent": headerString(req.headers["user-agent"]),
375
502
  },
376
503
  body: null,
377
504
  recomputedHash: null,
@@ -434,6 +561,8 @@ export class LocalAuthority {
434
561
  return this.send(res, record, 415, { error: "UNSUPPORTED_MEDIA_TYPE" });
435
562
  }
436
563
  switch (url.pathname) {
564
+ case "/v1/execution/provider-keys":
565
+ return this.send(res, record, ...this.registerProviderKeyRoute(record));
437
566
  case "/v1/authority/enforce-and-bind":
438
567
  return this.respond(res, record, "enforce", this.enforceAndBind(record));
439
568
  case "/v1/execution/claim-token":
@@ -445,6 +574,31 @@ export class LocalAuthority {
445
574
  return this.send(res, record, 404, { error: "NOT_FOUND" });
446
575
  }
447
576
  }
577
+ /** `POST /v1/execution/provider-keys`: the public half only, or a 400 naming why. */
578
+ registerProviderKeyRoute(record) {
579
+ const parsed = ProviderKeyRegistrationSchema.safeParse(record.body);
580
+ if (!parsed.success)
581
+ return [400, { error: "INVALID_BODY" }];
582
+ const body = parsed.data;
583
+ this.registerProviderKey({
584
+ kid: body.kid,
585
+ issuer: body.issuer,
586
+ publicJwk: { kty: body.public_jwk.kty, crv: body.public_jwk.crv, x: body.public_jwk.x },
587
+ ...(body.label === undefined ? {} : { label: body.label }),
588
+ });
589
+ return [
590
+ 201,
591
+ {
592
+ service: "decionis",
593
+ kid: body.kid,
594
+ issuer: body.issuer,
595
+ algorithm: "EdDSA",
596
+ label: body.label ?? null,
597
+ created_at: new Date(this.clock()).toISOString(),
598
+ revoked_at: null,
599
+ },
600
+ ];
601
+ }
448
602
  enforceAndBind(record) {
449
603
  const parsed = AuthorityRequestSchema.safeParse(record.body);
450
604
  if (!parsed.success)
@@ -620,6 +774,7 @@ export class LocalAuthority {
620
774
  receiptDossierId: approval?.receiptDossierId ?? null,
621
775
  claimed: false,
622
776
  claimToken: null,
777
+ receipt: null,
623
778
  correlationId: null,
624
779
  consumedBy: null,
625
780
  finalized: null,
@@ -973,9 +1128,37 @@ export class LocalAuthority {
973
1128
  }
974
1129
  if (grant.finalized !== null)
975
1130
  return rejected("NONCE_REPLAY_DETECTED");
1131
+ // The receipt is read first and recorded whatever it says; when the grant
1132
+ // named the effect it expected and the executor supplied no observation of
1133
+ // its own, a verified receipt with a digest stands in as SIGNED_RECEIPT
1134
+ // evidence by the provider's key, exactly as the hosted route reads it.
1135
+ const receipt = parsed.data.effect_receipt === undefined
1136
+ ? null
1137
+ : this.readEffectReceipt(parsed.data.effect_receipt, grant, parsed.data.claim_token);
1138
+ let supplied = parsed.data.effect_evidence;
1139
+ if (supplied === undefined &&
1140
+ receipt?.verified === true &&
1141
+ grant.expectedEffectDigest !== null &&
1142
+ receipt.effect_digest !== null) {
1143
+ supplied = {
1144
+ version: "1.0",
1145
+ status: receipt.effect_status === "EFFECTED" &&
1146
+ receipt.effect_digest === grant.expectedEffectDigest &&
1147
+ parsed.data.outcome === "COMMITTED"
1148
+ ? "CONFIRMED"
1149
+ : "UNCONFIRMED",
1150
+ observation_method: "SIGNED_RECEIPT",
1151
+ observer: { id: receipt.provider_key_id, version: EFFECT_RECEIPT_TYPE },
1152
+ expected_effect_digest: grant.expectedEffectDigest,
1153
+ observed_effect_digest: receipt.effect_digest,
1154
+ observed_at: receipt.effected_at,
1155
+ evidence_digest: `sha256:${createHash("sha256").update(receipt.token, "utf8").digest("hex")}`,
1156
+ evidence_reference: receipt.effect_reference,
1157
+ execution_correlation_id: parsed.data.commit_correlation_id,
1158
+ };
1159
+ }
976
1160
  // The hosted authority refuses the whole finalization for evidence it
977
1161
  // cannot bind, in exactly this order, before the commit transition.
978
- const supplied = parsed.data.effect_evidence;
979
1162
  let evidence;
980
1163
  if (supplied !== undefined) {
981
1164
  const parsedEvidence = EffectEvidenceSchema.safeParse(supplied);
@@ -995,7 +1178,13 @@ export class LocalAuthority {
995
1178
  return rejected("EFFECT_EVIDENCE_OUTCOME_MISMATCH");
996
1179
  }
997
1180
  if (evidence.status === "CONFIRMED") {
998
- const trusted = this.effectObserverIds();
1181
+ // A verified receipt's key is an observer for this finalization only.
1182
+ const trusted = [
1183
+ ...this.effectObserverIds(),
1184
+ ...(receipt?.verified === true && receipt.provider_key_id !== null
1185
+ ? [receipt.provider_key_id]
1186
+ : []),
1187
+ ];
999
1188
  if (trusted.length === 0) {
1000
1189
  return rejected("EFFECT_OBSERVER_PROVENANCE_UNAVAILABLE");
1001
1190
  }
@@ -1005,6 +1194,7 @@ export class LocalAuthority {
1005
1194
  }
1006
1195
  }
1007
1196
  grant.finalized = parsed.data.outcome;
1197
+ grant.receipt = receipt;
1008
1198
  // Byte-for-byte the hosted success body: the commit's own decision-chain
1009
1199
  // evidence is queued, not yet recorded, so a successful finalization still
1010
1200
  // carries `COMMIT_EVIDENCE_PENDING`.
@@ -1018,6 +1208,16 @@ export class LocalAuthority {
1018
1208
  evidence_recorded: false,
1019
1209
  effect_evidence_recorded: evidence !== undefined,
1020
1210
  effect_confirmation: evidence?.status === "CONFIRMED" ? "CONFIRMED" : "UNCONFIRMED",
1211
+ ...(receipt === null
1212
+ ? {}
1213
+ : {
1214
+ effect_receipt: {
1215
+ verified: receipt.verified,
1216
+ verification_code: receipt.verification_code,
1217
+ provider_key_id: receipt.provider_key_id,
1218
+ recorded: true,
1219
+ },
1220
+ }),
1021
1221
  reason_codes: ["COMMIT_EVIDENCE_PENDING"],
1022
1222
  },
1023
1223
  };