@suveren/gateway 0.7.6 → 0.8.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.
Files changed (161) hide show
  1. package/content/integrations/mollie.json +103 -28
  2. package/dist/mcp-server/http.mjs +228 -118
  3. package/dist/ui/assets/{index-CsrM6RQr.js → index-BFnSVa9N.js} +22 -22
  4. package/dist/ui/index.html +1 -1
  5. package/node_modules/@hap/core/dist/index.d.mts +29 -6
  6. package/node_modules/@hap/core/dist/index.d.ts +29 -6
  7. package/node_modules/@hap/core/dist/index.js +54 -8
  8. package/node_modules/@hap/core/dist/index.mjs +53 -8
  9. package/node_modules/@hap/core/package.json +2 -1
  10. package/node_modules/@hap/core/src/frame.ts +115 -14
  11. package/node_modules/@hap/core/src/gatekeeper.ts +26 -0
  12. package/node_modules/@hap/core/src/types.ts +7 -1
  13. package/node_modules/@types/node/README.md +1 -1
  14. package/node_modules/@types/node/buffer.buffer.d.ts +5 -0
  15. package/node_modules/@types/node/crypto.d.ts +0 -2
  16. package/node_modules/@types/node/dgram.d.ts +119 -4
  17. package/node_modules/@types/node/ffi.d.ts +1 -1
  18. package/node_modules/@types/node/fs/promises.d.ts +61 -65
  19. package/node_modules/@types/node/fs.d.ts +86 -76
  20. package/node_modules/@types/node/http.d.ts +1 -1
  21. package/node_modules/@types/node/http2.d.ts +2 -2
  22. package/node_modules/@types/node/index.d.ts +1 -0
  23. package/node_modules/@types/node/inspector.d.ts +2 -1
  24. package/node_modules/@types/node/net.d.ts +108 -16
  25. package/node_modules/@types/node/package.json +2 -2
  26. package/node_modules/@types/node/quic.d.ts +37 -11
  27. package/node_modules/@types/node/stream/iter.d.ts +4 -4
  28. package/node_modules/@types/node/tls.d.ts +24 -0
  29. package/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +5 -0
  30. package/node_modules/@types/node/ts5.6/index.d.ts +1 -0
  31. package/node_modules/@types/node/ts5.7/index.d.ts +1 -0
  32. package/node_modules/@types/node/tty.d.ts +4 -3
  33. package/node_modules/@types/node/vfs.d.ts +210 -0
  34. package/node_modules/@types/node/vm.d.ts +1 -1
  35. package/node_modules/express-rate-limit/dist/index.cjs +6 -3
  36. package/node_modules/express-rate-limit/dist/index.d.cts +11 -0
  37. package/node_modules/express-rate-limit/dist/index.d.mts +11 -0
  38. package/node_modules/express-rate-limit/dist/index.d.ts +11 -0
  39. package/node_modules/express-rate-limit/dist/index.mjs +6 -3
  40. package/node_modules/express-rate-limit/{license.md → license} +7 -6
  41. package/node_modules/express-rate-limit/package.json +5 -5
  42. package/node_modules/express-rate-limit/readme.md +1 -0
  43. package/node_modules/fast-uri/index.js +20 -5
  44. package/node_modules/fast-uri/lib/utils.js +8 -1
  45. package/node_modules/fast-uri/package.json +1 -1
  46. package/node_modules/fast-uri/test/component-safe-serialization.test.js +58 -0
  47. package/node_modules/fast-uri/test/ipv6-validation.test.js +34 -0
  48. package/node_modules/ip-address/README.md +92 -82
  49. package/node_modules/ip-address/dist/common.d.ts +20 -0
  50. package/node_modules/ip-address/dist/common.js +36 -0
  51. package/node_modules/ip-address/dist/common.js.map +1 -1
  52. package/node_modules/ip-address/dist/ipv4.d.ts +50 -0
  53. package/node_modules/ip-address/dist/ipv4.js +75 -0
  54. package/node_modules/ip-address/dist/ipv4.js.map +1 -1
  55. package/node_modules/ip-address/dist/ipv6.d.ts +79 -10
  56. package/node_modules/ip-address/dist/ipv6.js +120 -30
  57. package/node_modules/ip-address/dist/ipv6.js.map +1 -1
  58. package/node_modules/ip-address/dist/v4/constants.d.ts +12 -0
  59. package/node_modules/ip-address/dist/v4/constants.js +40 -1
  60. package/node_modules/ip-address/dist/v4/constants.js.map +1 -1
  61. package/node_modules/ip-address/dist/v6/constants.d.ts +14 -0
  62. package/node_modules/ip-address/dist/v6/constants.js +47 -1
  63. package/node_modules/ip-address/dist/v6/constants.js.map +1 -1
  64. package/node_modules/ip-address/package.json +1 -1
  65. package/node_modules/jose/dist/types/jwe/compact/decrypt.d.ts +7 -16
  66. package/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts +6 -17
  67. package/node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts +7 -16
  68. package/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts +11 -34
  69. package/node_modules/jose/dist/types/jwe/general/decrypt.d.ts +7 -16
  70. package/node_modules/jose/dist/types/jwe/general/encrypt.d.ts +15 -35
  71. package/node_modules/jose/dist/types/jwk/embedded.d.ts +3 -8
  72. package/node_modules/jose/dist/types/jwk/thumbprint.d.ts +5 -5
  73. package/node_modules/jose/dist/types/jwks/local.d.ts +6 -10
  74. package/node_modules/jose/dist/types/jwks/remote.d.ts +13 -24
  75. package/node_modules/jose/dist/types/jws/compact/sign.d.ts +5 -10
  76. package/node_modules/jose/dist/types/jws/compact/verify.d.ts +9 -18
  77. package/node_modules/jose/dist/types/jws/flattened/sign.d.ts +7 -16
  78. package/node_modules/jose/dist/types/jws/flattened/verify.d.ts +9 -18
  79. package/node_modules/jose/dist/types/jws/general/sign.d.ts +11 -21
  80. package/node_modules/jose/dist/types/jws/general/verify.d.ts +9 -18
  81. package/node_modules/jose/dist/types/jwt/decrypt.d.ts +10 -20
  82. package/node_modules/jose/dist/types/jwt/encrypt.d.ts +6 -13
  83. package/node_modules/jose/dist/types/jwt/sign.d.ts +5 -6
  84. package/node_modules/jose/dist/types/jwt/unsecured.d.ts +9 -14
  85. package/node_modules/jose/dist/types/jwt/verify.d.ts +9 -19
  86. package/node_modules/jose/dist/types/key/export.d.ts +4 -4
  87. package/node_modules/jose/dist/types/key/generate_key_pair.d.ts +6 -8
  88. package/node_modules/jose/dist/types/key/generate_secret.d.ts +4 -11
  89. package/node_modules/jose/dist/types/key/import.d.ts +10 -17
  90. package/node_modules/jose/dist/types/types.d.ts +44 -84
  91. package/node_modules/jose/dist/types/util/base64url.d.ts +2 -2
  92. package/node_modules/jose/dist/types/util/decode_jwt.d.ts +3 -10
  93. package/node_modules/jose/dist/types/util/decode_protected_header.d.ts +4 -4
  94. package/node_modules/jose/dist/types/util/errors.d.ts +26 -53
  95. package/node_modules/jose/dist/webapi/index.js +70 -32
  96. package/node_modules/jose/dist/webapi/jwe/compact/decrypt.js +7 -8
  97. package/node_modules/jose/dist/webapi/jwe/compact/encrypt.js +42 -48
  98. package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +14 -14
  99. package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +53 -64
  100. package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +38 -49
  101. package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +121 -169
  102. package/node_modules/jose/dist/webapi/jwk/embedded.js +30 -32
  103. package/node_modules/jose/dist/webapi/jwk/thumbprint.js +54 -70
  104. package/node_modules/jose/dist/webapi/jwks/local.js +47 -64
  105. package/node_modules/jose/dist/webapi/jwks/remote.js +88 -136
  106. package/node_modules/jose/dist/webapi/jws/compact/sign.js +21 -21
  107. package/node_modules/jose/dist/webapi/jws/compact/verify.js +7 -8
  108. package/node_modules/jose/dist/webapi/jws/flattened/sign.js +29 -31
  109. package/node_modules/jose/dist/webapi/jws/flattened/verify.js +21 -24
  110. package/node_modules/jose/dist/webapi/jws/general/sign.js +61 -72
  111. package/node_modules/jose/dist/webapi/jws/general/verify.js +45 -69
  112. package/node_modules/jose/dist/webapi/jwt/decrypt.js +13 -20
  113. package/node_modules/jose/dist/webapi/jwt/encrypt.js +57 -70
  114. package/node_modules/jose/dist/webapi/jwt/sign.js +17 -16
  115. package/node_modules/jose/dist/webapi/jwt/unsecured.js +32 -40
  116. package/node_modules/jose/dist/webapi/jwt/verify.js +12 -14
  117. package/node_modules/jose/dist/webapi/key/export.js +32 -37
  118. package/node_modules/jose/dist/webapi/key/generate_key_pair.js +40 -48
  119. package/node_modules/jose/dist/webapi/key/generate_secret.js +35 -40
  120. package/node_modules/jose/dist/webapi/key/import.js +52 -56
  121. package/node_modules/jose/dist/webapi/lib/asn1.js +130 -169
  122. package/node_modules/jose/dist/webapi/lib/base64.js +18 -20
  123. package/node_modules/jose/dist/webapi/lib/buffer_utils.js +34 -38
  124. package/node_modules/jose/dist/webapi/lib/content_encryption.js +79 -115
  125. package/node_modules/jose/dist/webapi/lib/crypto_key.js +24 -25
  126. package/node_modules/jose/dist/webapi/lib/deflate.js +45 -47
  127. package/node_modules/jose/dist/webapi/lib/helpers.js +40 -37
  128. package/node_modules/jose/dist/webapi/lib/invalid_key_input.js +10 -24
  129. package/node_modules/jose/dist/webapi/lib/is_key_like.js +17 -15
  130. package/node_modules/jose/dist/webapi/lib/jwe_algorithms.js +106 -93
  131. package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +207 -230
  132. package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +104 -138
  133. package/node_modules/jose/dist/webapi/lib/jwk_metadata.js +17 -18
  134. package/node_modules/jose/dist/webapi/lib/jwk_to_key.js +9 -16
  135. package/node_modules/jose/dist/webapi/lib/jws_algorithms.js +62 -61
  136. package/node_modules/jose/dist/webapi/lib/jws_sign.js +50 -75
  137. package/node_modules/jose/dist/webapi/lib/jws_verify.js +80 -125
  138. package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +123 -209
  139. package/node_modules/jose/dist/webapi/lib/key.js +84 -136
  140. package/node_modules/jose/dist/webapi/lib/key_algorithm.js +13 -8
  141. package/node_modules/jose/dist/webapi/lib/key_descriptor.js +8 -6
  142. package/node_modules/jose/dist/webapi/lib/key_management.js +200 -231
  143. package/node_modules/jose/dist/webapi/lib/key_options.js +7 -5
  144. package/node_modules/jose/dist/webapi/lib/options.js +55 -68
  145. package/node_modules/jose/dist/webapi/lib/signing.js +18 -22
  146. package/node_modules/jose/dist/webapi/lib/type_checks.js +26 -29
  147. package/node_modules/jose/dist/webapi/util/base64url.js +26 -35
  148. package/node_modules/jose/dist/webapi/util/decode_jwt.js +32 -31
  149. package/node_modules/jose/dist/webapi/util/decode_protected_header.js +18 -23
  150. package/node_modules/jose/dist/webapi/util/errors.js +94 -84
  151. package/node_modules/jose/package.json +17 -81
  152. package/package.json +2 -2
  153. package/profiles/calendar/0.5.profile.json +190 -0
  154. package/profiles/charge/0.5.profile.json +210 -0
  155. package/profiles/customers/0.7.profile.json +157 -0
  156. package/profiles/deploy/0.9.profile.json +176 -0
  157. package/profiles/email/0.6.profile.json +208 -0
  158. package/profiles/index.json +9 -1
  159. package/profiles/publish/0.5.profile.json +186 -0
  160. package/profiles/purchase/0.5.profile.json +231 -0
  161. package/profiles/records/0.5.profile.json +117 -0
@@ -8,7 +8,7 @@
8
8
  because tab chrome does. -->
9
9
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
10
10
  <title>Suveren</title>
11
- <script type="module" crossorigin src="/assets/index-CsrM6RQr.js"></script>
11
+ <script type="module" crossorigin src="/assets/index-BFnSVa9N.js"></script>
12
12
  <link rel="stylesheet" crossorigin href="/assets/index-BYVb_0oG.css">
13
13
  </head>
14
14
  <body>
@@ -656,7 +656,7 @@ interface GatekeeperRequest {
656
656
  * Structured error from Gatekeeper verification.
657
657
  */
658
658
  interface GatekeeperError {
659
- code: 'BOUND_EXCEEDED' | 'CUMULATIVE_LIMIT_EXCEEDED' | 'INVALID_SIGNATURE' | 'TTL_EXPIRED' | 'FRAME_MISMATCH' | 'BOUNDS_MISMATCH' | 'CONTEXT_MISMATCH' | 'DOMAIN_NOT_COVERED' | 'INVALID_PROFILE' | 'MALFORMED_ATTESTATION' | 'INVALID_ACTION_TYPE' | 'ATTESTATION_REVOKED' | 'APPROVAL_REQUIRED' | 'IDEMPOTENCY_MISMATCH' | 'MANDATE_SIGNATURE_REQUIRED' | 'MANDATE_SIGNATURE_INVALID' | 'APPROVAL_SIGNATURE_REQUIRED' | 'APPROVAL_SIGNATURE_INVALID';
659
+ code: 'BOUND_EXCEEDED' | 'CUMULATIVE_LIMIT_EXCEEDED' | 'INVALID_SIGNATURE' | 'TTL_EXPIRED' | 'FRAME_MISMATCH' | 'BOUNDS_MISMATCH' | 'CONTEXT_MISMATCH' | 'DOMAIN_NOT_COVERED' | 'INVALID_PROFILE' | 'MALFORMED_ATTESTATION' | 'INVALID_ACTION_TYPE' | 'ATTESTATION_REVOKED' | 'APPROVAL_REQUIRED' | 'IDEMPOTENCY_MISMATCH' | 'MANDATE_SIGNATURE_REQUIRED' | 'MANDATE_SIGNATURE_INVALID' | 'APPROVAL_SIGNATURE_REQUIRED' | 'APPROVAL_SIGNATURE_INVALID' | 'BOUNDS_INVALID_VALUE' | 'CONTEXT_INVALID_VALUE';
660
660
  field?: string;
661
661
  message: string;
662
662
  bound?: string | number;
@@ -921,8 +921,11 @@ declare function deriveIdentityLine(subject: Subject | undefined | null, { opera
921
921
  * Frame Canonicalization for Agent Profiles
922
922
  *
923
923
  * Agent profiles support mixed-type fields (strings and numbers).
924
- * Canonical form: all values are converted to strings via String(value).
925
- * Keys are ordered according to the profile's keyOrder.
924
+ * Canonical form: `key=value` records joined with LF, keys in the profile's
925
+ * keyOrder. Values are stringified with String(value) the shortest
926
+ * round-trippable form for numbers — then percent-encoded per protocol.md
927
+ * (`=`, `%`, and every byte outside printable ASCII); a value carrying a raw
928
+ * LF/CR is refused, never normalized. See `canonicalRecords` below.
926
929
  *
927
930
  * v0.3: frameSchema
928
931
  * v0.4: boundsSchema + contextSchema (separate hashes)
@@ -966,19 +969,39 @@ declare function validateContextParams(params: AgentContextParams, profile: Agen
966
969
  valid: boolean;
967
970
  errors: string[];
968
971
  };
972
+ /**
973
+ * Thrown when a value cannot be canonicalized at all — currently only for raw
974
+ * LF/CR inside a value. Carries the protocol error code so callers can map it
975
+ * straight onto the wire without string-matching a message.
976
+ *
977
+ * protocol.md → *Bounds & Scope Canonicalization* → Value encoding:
978
+ * "Values MUST NOT contain raw newline (\n) or carriage-return (\r)
979
+ * characters. Implementations MUST reject input containing them; silent
980
+ * stripping or normalization is a violation because it produces a hash that
981
+ * does not faithfully represent the input."
982
+ */
983
+ declare class CanonicalValueError extends Error {
984
+ readonly code: 'BOUNDS_INVALID_VALUE' | 'CONTEXT_INVALID_VALUE';
985
+ readonly field: string;
986
+ constructor(code: 'BOUNDS_INVALID_VALUE' | 'CONTEXT_INVALID_VALUE', field: string, message: string);
987
+ }
969
988
  /**
970
989
  * Builds the canonical bounds string from parameters.
971
- * All values are converted to strings. Keys are ordered per profile's boundsSchema.keyOrder.
990
+ * Keys are ordered per profile's boundsSchema.keyOrder; values are encoded per
991
+ * protocol.md → *Value encoding* (see `canonicalRecords`).
972
992
  *
973
993
  * @throws Error if any field fails validation
994
+ * @throws CanonicalValueError (code BOUNDS_INVALID_VALUE) if a value carries a raw LF/CR
974
995
  */
975
996
  declare function canonicalBounds(params: AgentBoundsParams, profile: AgentProfile): string;
976
997
  /**
977
998
  * Builds the canonical context string from parameters.
978
- * All values are converted to strings. Keys are ordered per profile's contextSchema.keyOrder.
999
+ * Keys are ordered per profile's contextSchema.keyOrder; values are encoded per
1000
+ * protocol.md → *Value encoding* (see `canonicalRecords`).
979
1001
  * For empty context (no contextSchema or no fields), returns "".
980
1002
  *
981
1003
  * @throws Error if any field fails validation
1004
+ * @throws CanonicalValueError (code CONTEXT_INVALID_VALUE) if a value carries a raw LF/CR
982
1005
  */
983
1006
  declare function canonicalContext(params: AgentContextParams, profile: AgentProfile): string;
984
1007
  /**
@@ -1299,4 +1322,4 @@ interface ReceiptPayload {
1299
1322
  */
1300
1323
  declare function verifyReceiptSignature(receipt: ReceiptPayload, publicKeyHex: string): Promise<void>;
1301
1324
 
1302
- export { type AgentBoundsParams, type AgentContextParams, type AgentFrameParams, type AgentProfile, type ApprovalObject, type Attestation, type AttestationHeader, type AttestationPayload, type BoundType, type ContentBinding, ContentBindingError, type ContentBindingErrorCode, type CumulativeFieldDef, type CumulativeWindow, type DeclaredFieldDef, type ExecutionContextFieldDef, type ExecutionLogEntry, type ExecutionLogQuery, type ExecutionMappingTransform, type ExecutionMappingValue, type ExecutionPath, type FieldConstraint, type FieldUnit, type GateQuestion, type GatekeeperError, type GatekeeperRequest, type GatekeeperResult, type MandateBinding, MandateError, type MandateProjection, type OwnerMandate, type ProfileBoundsField, type ProfileContextField, type ProfileFrameField, type ProfileToolGating, type ProfileToolGatingEntry, type ReceiptPayload, type ResolvedDomain, type SigningSurface, type Subject, type SubjectValidation, approvalSigningBytes, attestationId, bindingAppliesTo, buildMandateProjection, canonicalBounds, canonicalContext, canonicalFrame, canonicalize, canonicalizeText, checkAttestationExpiry, clearProfiles, computeBoundsHash, computeContentHash, computeContextHash, computeFieldsContentHash, computeFrameHash, computeIntentDisclosureHash, computeIntentHash, contentCanonicalBytes, decodeAttestationBlob, decodeDidKey, deriveIdentityLine, encodeAttestationBlob, encodeDidKey, frameHash, getAllProfiles, getProfile, intentDisclosureCanonicalBytes, isFieldBinding, isKeyBearingDid, isV4Attestation, listProfiles, mandateSigningBytes, registerProfile, selectBoundFields, signApproval, signMandateProjection, validateBoundsParams, validateContextParams, validateFrameParams, validateSubject, verify, verifyApproval, verifyAttestation, verifyAttestationSignature, verifyAttestationV4, verifyBoundsHash, verifyContextHash, verifyFrameHash, verifyOwnerMandate, verifyOwnerMandates, verifyReceiptSignature };
1325
+ export { type AgentBoundsParams, type AgentContextParams, type AgentFrameParams, type AgentProfile, type ApprovalObject, type Attestation, type AttestationHeader, type AttestationPayload, type BoundType, CanonicalValueError, type ContentBinding, ContentBindingError, type ContentBindingErrorCode, type CumulativeFieldDef, type CumulativeWindow, type DeclaredFieldDef, type ExecutionContextFieldDef, type ExecutionLogEntry, type ExecutionLogQuery, type ExecutionMappingTransform, type ExecutionMappingValue, type ExecutionPath, type FieldConstraint, type FieldUnit, type GateQuestion, type GatekeeperError, type GatekeeperRequest, type GatekeeperResult, type MandateBinding, MandateError, type MandateProjection, type OwnerMandate, type ProfileBoundsField, type ProfileContextField, type ProfileFrameField, type ProfileToolGating, type ProfileToolGatingEntry, type ReceiptPayload, type ResolvedDomain, type SigningSurface, type Subject, type SubjectValidation, approvalSigningBytes, attestationId, bindingAppliesTo, buildMandateProjection, canonicalBounds, canonicalContext, canonicalFrame, canonicalize, canonicalizeText, checkAttestationExpiry, clearProfiles, computeBoundsHash, computeContentHash, computeContextHash, computeFieldsContentHash, computeFrameHash, computeIntentDisclosureHash, computeIntentHash, contentCanonicalBytes, decodeAttestationBlob, decodeDidKey, deriveIdentityLine, encodeAttestationBlob, encodeDidKey, frameHash, getAllProfiles, getProfile, intentDisclosureCanonicalBytes, isFieldBinding, isKeyBearingDid, isV4Attestation, listProfiles, mandateSigningBytes, registerProfile, selectBoundFields, signApproval, signMandateProjection, validateBoundsParams, validateContextParams, validateFrameParams, validateSubject, verify, verifyApproval, verifyAttestation, verifyAttestationSignature, verifyAttestationV4, verifyBoundsHash, verifyContextHash, verifyFrameHash, verifyOwnerMandate, verifyOwnerMandates, verifyReceiptSignature };
@@ -656,7 +656,7 @@ interface GatekeeperRequest {
656
656
  * Structured error from Gatekeeper verification.
657
657
  */
658
658
  interface GatekeeperError {
659
- code: 'BOUND_EXCEEDED' | 'CUMULATIVE_LIMIT_EXCEEDED' | 'INVALID_SIGNATURE' | 'TTL_EXPIRED' | 'FRAME_MISMATCH' | 'BOUNDS_MISMATCH' | 'CONTEXT_MISMATCH' | 'DOMAIN_NOT_COVERED' | 'INVALID_PROFILE' | 'MALFORMED_ATTESTATION' | 'INVALID_ACTION_TYPE' | 'ATTESTATION_REVOKED' | 'APPROVAL_REQUIRED' | 'IDEMPOTENCY_MISMATCH' | 'MANDATE_SIGNATURE_REQUIRED' | 'MANDATE_SIGNATURE_INVALID' | 'APPROVAL_SIGNATURE_REQUIRED' | 'APPROVAL_SIGNATURE_INVALID';
659
+ code: 'BOUND_EXCEEDED' | 'CUMULATIVE_LIMIT_EXCEEDED' | 'INVALID_SIGNATURE' | 'TTL_EXPIRED' | 'FRAME_MISMATCH' | 'BOUNDS_MISMATCH' | 'CONTEXT_MISMATCH' | 'DOMAIN_NOT_COVERED' | 'INVALID_PROFILE' | 'MALFORMED_ATTESTATION' | 'INVALID_ACTION_TYPE' | 'ATTESTATION_REVOKED' | 'APPROVAL_REQUIRED' | 'IDEMPOTENCY_MISMATCH' | 'MANDATE_SIGNATURE_REQUIRED' | 'MANDATE_SIGNATURE_INVALID' | 'APPROVAL_SIGNATURE_REQUIRED' | 'APPROVAL_SIGNATURE_INVALID' | 'BOUNDS_INVALID_VALUE' | 'CONTEXT_INVALID_VALUE';
660
660
  field?: string;
661
661
  message: string;
662
662
  bound?: string | number;
@@ -921,8 +921,11 @@ declare function deriveIdentityLine(subject: Subject | undefined | null, { opera
921
921
  * Frame Canonicalization for Agent Profiles
922
922
  *
923
923
  * Agent profiles support mixed-type fields (strings and numbers).
924
- * Canonical form: all values are converted to strings via String(value).
925
- * Keys are ordered according to the profile's keyOrder.
924
+ * Canonical form: `key=value` records joined with LF, keys in the profile's
925
+ * keyOrder. Values are stringified with String(value) the shortest
926
+ * round-trippable form for numbers — then percent-encoded per protocol.md
927
+ * (`=`, `%`, and every byte outside printable ASCII); a value carrying a raw
928
+ * LF/CR is refused, never normalized. See `canonicalRecords` below.
926
929
  *
927
930
  * v0.3: frameSchema
928
931
  * v0.4: boundsSchema + contextSchema (separate hashes)
@@ -966,19 +969,39 @@ declare function validateContextParams(params: AgentContextParams, profile: Agen
966
969
  valid: boolean;
967
970
  errors: string[];
968
971
  };
972
+ /**
973
+ * Thrown when a value cannot be canonicalized at all — currently only for raw
974
+ * LF/CR inside a value. Carries the protocol error code so callers can map it
975
+ * straight onto the wire without string-matching a message.
976
+ *
977
+ * protocol.md → *Bounds & Scope Canonicalization* → Value encoding:
978
+ * "Values MUST NOT contain raw newline (\n) or carriage-return (\r)
979
+ * characters. Implementations MUST reject input containing them; silent
980
+ * stripping or normalization is a violation because it produces a hash that
981
+ * does not faithfully represent the input."
982
+ */
983
+ declare class CanonicalValueError extends Error {
984
+ readonly code: 'BOUNDS_INVALID_VALUE' | 'CONTEXT_INVALID_VALUE';
985
+ readonly field: string;
986
+ constructor(code: 'BOUNDS_INVALID_VALUE' | 'CONTEXT_INVALID_VALUE', field: string, message: string);
987
+ }
969
988
  /**
970
989
  * Builds the canonical bounds string from parameters.
971
- * All values are converted to strings. Keys are ordered per profile's boundsSchema.keyOrder.
990
+ * Keys are ordered per profile's boundsSchema.keyOrder; values are encoded per
991
+ * protocol.md → *Value encoding* (see `canonicalRecords`).
972
992
  *
973
993
  * @throws Error if any field fails validation
994
+ * @throws CanonicalValueError (code BOUNDS_INVALID_VALUE) if a value carries a raw LF/CR
974
995
  */
975
996
  declare function canonicalBounds(params: AgentBoundsParams, profile: AgentProfile): string;
976
997
  /**
977
998
  * Builds the canonical context string from parameters.
978
- * All values are converted to strings. Keys are ordered per profile's contextSchema.keyOrder.
999
+ * Keys are ordered per profile's contextSchema.keyOrder; values are encoded per
1000
+ * protocol.md → *Value encoding* (see `canonicalRecords`).
979
1001
  * For empty context (no contextSchema or no fields), returns "".
980
1002
  *
981
1003
  * @throws Error if any field fails validation
1004
+ * @throws CanonicalValueError (code CONTEXT_INVALID_VALUE) if a value carries a raw LF/CR
982
1005
  */
983
1006
  declare function canonicalContext(params: AgentContextParams, profile: AgentProfile): string;
984
1007
  /**
@@ -1299,4 +1322,4 @@ interface ReceiptPayload {
1299
1322
  */
1300
1323
  declare function verifyReceiptSignature(receipt: ReceiptPayload, publicKeyHex: string): Promise<void>;
1301
1324
 
1302
- export { type AgentBoundsParams, type AgentContextParams, type AgentFrameParams, type AgentProfile, type ApprovalObject, type Attestation, type AttestationHeader, type AttestationPayload, type BoundType, type ContentBinding, ContentBindingError, type ContentBindingErrorCode, type CumulativeFieldDef, type CumulativeWindow, type DeclaredFieldDef, type ExecutionContextFieldDef, type ExecutionLogEntry, type ExecutionLogQuery, type ExecutionMappingTransform, type ExecutionMappingValue, type ExecutionPath, type FieldConstraint, type FieldUnit, type GateQuestion, type GatekeeperError, type GatekeeperRequest, type GatekeeperResult, type MandateBinding, MandateError, type MandateProjection, type OwnerMandate, type ProfileBoundsField, type ProfileContextField, type ProfileFrameField, type ProfileToolGating, type ProfileToolGatingEntry, type ReceiptPayload, type ResolvedDomain, type SigningSurface, type Subject, type SubjectValidation, approvalSigningBytes, attestationId, bindingAppliesTo, buildMandateProjection, canonicalBounds, canonicalContext, canonicalFrame, canonicalize, canonicalizeText, checkAttestationExpiry, clearProfiles, computeBoundsHash, computeContentHash, computeContextHash, computeFieldsContentHash, computeFrameHash, computeIntentDisclosureHash, computeIntentHash, contentCanonicalBytes, decodeAttestationBlob, decodeDidKey, deriveIdentityLine, encodeAttestationBlob, encodeDidKey, frameHash, getAllProfiles, getProfile, intentDisclosureCanonicalBytes, isFieldBinding, isKeyBearingDid, isV4Attestation, listProfiles, mandateSigningBytes, registerProfile, selectBoundFields, signApproval, signMandateProjection, validateBoundsParams, validateContextParams, validateFrameParams, validateSubject, verify, verifyApproval, verifyAttestation, verifyAttestationSignature, verifyAttestationV4, verifyBoundsHash, verifyContextHash, verifyFrameHash, verifyOwnerMandate, verifyOwnerMandates, verifyReceiptSignature };
1325
+ export { type AgentBoundsParams, type AgentContextParams, type AgentFrameParams, type AgentProfile, type ApprovalObject, type Attestation, type AttestationHeader, type AttestationPayload, type BoundType, CanonicalValueError, type ContentBinding, ContentBindingError, type ContentBindingErrorCode, type CumulativeFieldDef, type CumulativeWindow, type DeclaredFieldDef, type ExecutionContextFieldDef, type ExecutionLogEntry, type ExecutionLogQuery, type ExecutionMappingTransform, type ExecutionMappingValue, type ExecutionPath, type FieldConstraint, type FieldUnit, type GateQuestion, type GatekeeperError, type GatekeeperRequest, type GatekeeperResult, type MandateBinding, MandateError, type MandateProjection, type OwnerMandate, type ProfileBoundsField, type ProfileContextField, type ProfileFrameField, type ProfileToolGating, type ProfileToolGatingEntry, type ReceiptPayload, type ResolvedDomain, type SigningSurface, type Subject, type SubjectValidation, approvalSigningBytes, attestationId, bindingAppliesTo, buildMandateProjection, canonicalBounds, canonicalContext, canonicalFrame, canonicalize, canonicalizeText, checkAttestationExpiry, clearProfiles, computeBoundsHash, computeContentHash, computeContextHash, computeFieldsContentHash, computeFrameHash, computeIntentDisclosureHash, computeIntentHash, contentCanonicalBytes, decodeAttestationBlob, decodeDidKey, deriveIdentityLine, encodeAttestationBlob, encodeDidKey, frameHash, getAllProfiles, getProfile, intentDisclosureCanonicalBytes, isFieldBinding, isKeyBearingDid, isV4Attestation, listProfiles, mandateSigningBytes, registerProfile, selectBoundFields, signApproval, signMandateProjection, validateBoundsParams, validateContextParams, validateFrameParams, validateSubject, verify, verifyApproval, verifyAttestation, verifyAttestationSignature, verifyAttestationV4, verifyBoundsHash, verifyContextHash, verifyFrameHash, verifyOwnerMandate, verifyOwnerMandates, verifyReceiptSignature };
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ CanonicalValueError: () => CanonicalValueError,
33
34
  ContentBindingError: () => ContentBindingError,
34
35
  MandateError: () => MandateError,
35
36
  approvalSigningBytes: () => approvalSigningBytes,
@@ -376,15 +377,51 @@ function validateContextParams(params, profile) {
376
377
  }
377
378
  return { valid: errors.length === 0, errors };
378
379
  }
380
+ var CanonicalValueError = class extends Error {
381
+ code;
382
+ field;
383
+ constructor(code, field, message) {
384
+ super(message);
385
+ this.name = "CanonicalValueError";
386
+ this.code = code;
387
+ this.field = field;
388
+ }
389
+ };
390
+ function percentEncodeCanonicalValue(raw) {
391
+ const bytes = new TextEncoder().encode(raw);
392
+ let out = "";
393
+ for (const b of bytes) {
394
+ if (b === 61 || b === 37 || b < 32 || b > 126) {
395
+ out += "%" + b.toString(16).toUpperCase().padStart(2, "0");
396
+ } else {
397
+ out += String.fromCharCode(b);
398
+ }
399
+ }
400
+ return out;
401
+ }
402
+ function canonicalRecords(params, keyOrder, code) {
403
+ const lines = [];
404
+ for (const key of keyOrder) {
405
+ const value = params[key];
406
+ if (value === void 0 || value === null) continue;
407
+ const raw = String(value);
408
+ if (raw.includes("\n") || raw.includes("\r")) {
409
+ throw new CanonicalValueError(
410
+ code,
411
+ key,
412
+ `Value for "${key}" contains a raw newline or carriage return. Refusing: a hash over stripped or normalized input would not represent what was authorized.`
413
+ );
414
+ }
415
+ lines.push(`${key}=${percentEncodeCanonicalValue(raw)}`);
416
+ }
417
+ return lines.join("\n");
418
+ }
379
419
  function canonicalBounds(params, profile) {
380
420
  const validation = validateBoundsParams(params, profile);
381
421
  if (!validation.valid) {
382
422
  throw new Error(`Invalid bounds parameters: ${validation.errors.join("; ")}`);
383
423
  }
384
- const lines = profile.boundsSchema.keyOrder.map(
385
- (key) => `${key}=${String(params[key])}`
386
- );
387
- return lines.join("\n");
424
+ return canonicalRecords(params, profile.boundsSchema.keyOrder, "BOUNDS_INVALID_VALUE");
388
425
  }
389
426
  function canonicalContext(params, profile) {
390
427
  if (!profile.contextSchema || Object.keys(profile.contextSchema.fields).length === 0) {
@@ -394,10 +431,7 @@ function canonicalContext(params, profile) {
394
431
  if (!validation.valid) {
395
432
  throw new Error(`Invalid context parameters: ${validation.errors.join("; ")}`);
396
433
  }
397
- const lines = profile.contextSchema.keyOrder.map(
398
- (key) => `${key}=${String(params[key])}`
399
- );
400
- return lines.join("\n");
434
+ return canonicalRecords(params, profile.contextSchema.keyOrder, "CONTEXT_INVALID_VALUE");
401
435
  }
402
436
  function computeBoundsHash(params, profile) {
403
437
  const canonical = canonicalBounds(params, profile);
@@ -527,7 +561,17 @@ async function verify(request, publicKeyHex, now = Math.floor(Date.now() / 1e3),
527
561
  return verifyV3(request, profile, publicKeyHex, now, executionLog, errors);
528
562
  }
529
563
  }
564
+ function noAttestationSupplied(request) {
565
+ return !Array.isArray(request.attestations) || request.attestations.length === 0;
566
+ }
567
+ function noAttestationResult() {
568
+ return {
569
+ approved: false,
570
+ errors: [{ code: "MALFORMED_ATTESTATION", message: "No attestation supplied \u2014 nothing to verify" }]
571
+ };
572
+ }
530
573
  async function verifyV3(request, profile, publicKeyHex, now, executionLog, errors) {
574
+ if (noAttestationSupplied(request)) return noAttestationResult();
531
575
  let expectedFrameHash;
532
576
  try {
533
577
  expectedFrameHash = computeFrameHash(request.frame, profile);
@@ -588,6 +632,7 @@ async function verifyV3(request, profile, publicKeyHex, now, executionLog, error
588
632
  return { approved: true };
589
633
  }
590
634
  async function verifyV4(request, profile, publicKeyHex, now, executionLog) {
635
+ if (noAttestationSupplied(request)) return noAttestationResult();
591
636
  const errors = [];
592
637
  const bounds = request.frame;
593
638
  const context = request.context;
@@ -1156,6 +1201,7 @@ async function verifyReceiptSignature(receipt, publicKeyHex) {
1156
1201
  }
1157
1202
  // Annotate the CommonJS export names for ESM import in node:
1158
1203
  0 && (module.exports = {
1204
+ CanonicalValueError,
1159
1205
  ContentBindingError,
1160
1206
  MandateError,
1161
1207
  approvalSigningBytes,
@@ -287,15 +287,51 @@ function validateContextParams(params, profile) {
287
287
  }
288
288
  return { valid: errors.length === 0, errors };
289
289
  }
290
+ var CanonicalValueError = class extends Error {
291
+ code;
292
+ field;
293
+ constructor(code, field, message) {
294
+ super(message);
295
+ this.name = "CanonicalValueError";
296
+ this.code = code;
297
+ this.field = field;
298
+ }
299
+ };
300
+ function percentEncodeCanonicalValue(raw) {
301
+ const bytes = new TextEncoder().encode(raw);
302
+ let out = "";
303
+ for (const b of bytes) {
304
+ if (b === 61 || b === 37 || b < 32 || b > 126) {
305
+ out += "%" + b.toString(16).toUpperCase().padStart(2, "0");
306
+ } else {
307
+ out += String.fromCharCode(b);
308
+ }
309
+ }
310
+ return out;
311
+ }
312
+ function canonicalRecords(params, keyOrder, code) {
313
+ const lines = [];
314
+ for (const key of keyOrder) {
315
+ const value = params[key];
316
+ if (value === void 0 || value === null) continue;
317
+ const raw = String(value);
318
+ if (raw.includes("\n") || raw.includes("\r")) {
319
+ throw new CanonicalValueError(
320
+ code,
321
+ key,
322
+ `Value for "${key}" contains a raw newline or carriage return. Refusing: a hash over stripped or normalized input would not represent what was authorized.`
323
+ );
324
+ }
325
+ lines.push(`${key}=${percentEncodeCanonicalValue(raw)}`);
326
+ }
327
+ return lines.join("\n");
328
+ }
290
329
  function canonicalBounds(params, profile) {
291
330
  const validation = validateBoundsParams(params, profile);
292
331
  if (!validation.valid) {
293
332
  throw new Error(`Invalid bounds parameters: ${validation.errors.join("; ")}`);
294
333
  }
295
- const lines = profile.boundsSchema.keyOrder.map(
296
- (key) => `${key}=${String(params[key])}`
297
- );
298
- return lines.join("\n");
334
+ return canonicalRecords(params, profile.boundsSchema.keyOrder, "BOUNDS_INVALID_VALUE");
299
335
  }
300
336
  function canonicalContext(params, profile) {
301
337
  if (!profile.contextSchema || Object.keys(profile.contextSchema.fields).length === 0) {
@@ -305,10 +341,7 @@ function canonicalContext(params, profile) {
305
341
  if (!validation.valid) {
306
342
  throw new Error(`Invalid context parameters: ${validation.errors.join("; ")}`);
307
343
  }
308
- const lines = profile.contextSchema.keyOrder.map(
309
- (key) => `${key}=${String(params[key])}`
310
- );
311
- return lines.join("\n");
344
+ return canonicalRecords(params, profile.contextSchema.keyOrder, "CONTEXT_INVALID_VALUE");
312
345
  }
313
346
  function computeBoundsHash(params, profile) {
314
347
  const canonical = canonicalBounds(params, profile);
@@ -438,7 +471,17 @@ async function verify(request, publicKeyHex, now = Math.floor(Date.now() / 1e3),
438
471
  return verifyV3(request, profile, publicKeyHex, now, executionLog, errors);
439
472
  }
440
473
  }
474
+ function noAttestationSupplied(request) {
475
+ return !Array.isArray(request.attestations) || request.attestations.length === 0;
476
+ }
477
+ function noAttestationResult() {
478
+ return {
479
+ approved: false,
480
+ errors: [{ code: "MALFORMED_ATTESTATION", message: "No attestation supplied \u2014 nothing to verify" }]
481
+ };
482
+ }
441
483
  async function verifyV3(request, profile, publicKeyHex, now, executionLog, errors) {
484
+ if (noAttestationSupplied(request)) return noAttestationResult();
442
485
  let expectedFrameHash;
443
486
  try {
444
487
  expectedFrameHash = computeFrameHash(request.frame, profile);
@@ -499,6 +542,7 @@ async function verifyV3(request, profile, publicKeyHex, now, executionLog, error
499
542
  return { approved: true };
500
543
  }
501
544
  async function verifyV4(request, profile, publicKeyHex, now, executionLog) {
545
+ if (noAttestationSupplied(request)) return noAttestationResult();
502
546
  const errors = [];
503
547
  const bounds = request.frame;
504
548
  const context = request.context;
@@ -1066,6 +1110,7 @@ async function verifyReceiptSignature(receipt, publicKeyHex) {
1066
1110
  }
1067
1111
  }
1068
1112
  export {
1113
+ CanonicalValueError,
1069
1114
  ContentBindingError,
1070
1115
  MandateError,
1071
1116
  approvalSigningBytes,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humanagencyp/hap-core",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Core types, cryptographic primitives, and verification logic for the Human Agency Protocol",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -24,6 +24,7 @@
24
24
  "scripts": {
25
25
  "build": "tsup src/index.ts --format cjs,esm --dts",
26
26
  "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
27
+ "typecheck": "tsc --noEmit",
27
28
  "test": "vitest run",
28
29
  "test:watch": "vitest",
29
30
  "prepublishOnly": "pnpm build",
@@ -2,8 +2,11 @@
2
2
  * Frame Canonicalization for Agent Profiles
3
3
  *
4
4
  * Agent profiles support mixed-type fields (strings and numbers).
5
- * Canonical form: all values are converted to strings via String(value).
6
- * Keys are ordered according to the profile's keyOrder.
5
+ * Canonical form: `key=value` records joined with LF, keys in the profile's
6
+ * keyOrder. Values are stringified with String(value) the shortest
7
+ * round-trippable form for numbers — then percent-encoded per protocol.md
8
+ * (`=`, `%`, and every byte outside printable ASCII); a value carrying a raw
9
+ * LF/CR is refused, never normalized. See `canonicalRecords` below.
7
10
  *
8
11
  * v0.3: frameSchema
9
12
  * v0.4: boundsSchema + contextSchema (separate hashes)
@@ -176,11 +179,115 @@ export function validateContextParams(
176
179
  return { valid: errors.length === 0, errors };
177
180
  }
178
181
 
182
+ // ─── Value Encoding (normative, v0.5+) ───────────────────────────────────────
183
+
184
+ /**
185
+ * Thrown when a value cannot be canonicalized at all — currently only for raw
186
+ * LF/CR inside a value. Carries the protocol error code so callers can map it
187
+ * straight onto the wire without string-matching a message.
188
+ *
189
+ * protocol.md → *Bounds & Scope Canonicalization* → Value encoding:
190
+ * "Values MUST NOT contain raw newline (\n) or carriage-return (\r)
191
+ * characters. Implementations MUST reject input containing them; silent
192
+ * stripping or normalization is a violation because it produces a hash that
193
+ * does not faithfully represent the input."
194
+ */
195
+ export class CanonicalValueError extends Error {
196
+ readonly code: 'BOUNDS_INVALID_VALUE' | 'CONTEXT_INVALID_VALUE';
197
+ readonly field: string;
198
+
199
+ constructor(code: 'BOUNDS_INVALID_VALUE' | 'CONTEXT_INVALID_VALUE', field: string, message: string) {
200
+ super(message);
201
+ this.name = 'CanonicalValueError';
202
+ this.code = code;
203
+ this.field = field;
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Percent-encode a value per protocol.md → *Value encoding*.
209
+ *
210
+ * Encoded, over the value's UTF-8 bytes, as `%` + two UPPERCASE hex digits:
211
+ * - `=` (0x3D) — otherwise it would be read as the key/value separator
212
+ * - `%` (0x25) — so the encoding is self-inverse
213
+ * - every byte outside printable ASCII 0x20–0x7E
214
+ *
215
+ * LF and CR are deliberately NOT in this list: they are refused upstream, so
216
+ * encoding them is unreachable (v0.7 removed the spec's contradiction here).
217
+ *
218
+ * This runs at canonicalization time only. Stored values keep the human's
219
+ * original bytes.
220
+ */
221
+ function percentEncodeCanonicalValue(raw: string): string {
222
+ const bytes = new TextEncoder().encode(raw);
223
+ let out = '';
224
+ for (const b of bytes) {
225
+ if (b === 0x3d || b === 0x25 || b < 0x20 || b > 0x7e) {
226
+ out += '%' + b.toString(16).toUpperCase().padStart(2, '0');
227
+ } else {
228
+ out += String.fromCharCode(b);
229
+ }
230
+ }
231
+ return out;
232
+ }
233
+
234
+ /**
235
+ * The one place `key=value` records are built for bounds and context.
236
+ *
237
+ * Rules applied here (all normative, protocol.md → *Bounds & Scope
238
+ * Canonicalization*):
239
+ * - keys in the schema's keyOrder, never alphabetical
240
+ * - a value carrying a raw LF/CR is REFUSED (never stripped or encoded)
241
+ * - `=`, `%`, and any byte outside 0x20–0x7E are percent-encoded (UPPERCASE)
242
+ * - numbers use JS `String()`, which is the shortest round-trippable form
243
+ * (`String(20.0)` === "20")
244
+ * - a key with no value is OMITTED entirely — it emits no record
245
+ *
246
+ * On the omission rule: required keys are guaranteed present by the caller's
247
+ * validation (`validateBoundsParams` / `validateContextParams` reject a missing
248
+ * required field), so "explicit inclusion of all required keys" still holds.
249
+ * What remains are *optional* keys the human never set. Rendering those as the
250
+ * literal string "undefined" — the pre-fix behaviour — hashed a JavaScript
251
+ * artifact that no other language would produce and that collides with a real
252
+ * value of "undefined". Omitting them (rather than emitting `key=`) also keeps
253
+ * "the human set no limit" distinct from "the human set an empty value", and
254
+ * matches this package's JSON canonicalization, which drops undefined-valued
255
+ * properties.
256
+ */
257
+ function canonicalRecords(
258
+ params: Record<string, string | number | undefined>,
259
+ keyOrder: string[],
260
+ code: 'BOUNDS_INVALID_VALUE' | 'CONTEXT_INVALID_VALUE',
261
+ ): string {
262
+ const lines: string[] = [];
263
+
264
+ for (const key of keyOrder) {
265
+ const value = params[key];
266
+ if (value === undefined || value === null) continue;
267
+
268
+ const raw = String(value);
269
+ if (raw.includes('\n') || raw.includes('\r')) {
270
+ throw new CanonicalValueError(
271
+ code,
272
+ key,
273
+ `Value for "${key}" contains a raw newline or carriage return. ` +
274
+ 'Refusing: a hash over stripped or normalized input would not represent what was authorized.',
275
+ );
276
+ }
277
+
278
+ lines.push(`${key}=${percentEncodeCanonicalValue(raw)}`);
279
+ }
280
+
281
+ return lines.join('\n');
282
+ }
283
+
179
284
  /**
180
285
  * Builds the canonical bounds string from parameters.
181
- * All values are converted to strings. Keys are ordered per profile's boundsSchema.keyOrder.
286
+ * Keys are ordered per profile's boundsSchema.keyOrder; values are encoded per
287
+ * protocol.md → *Value encoding* (see `canonicalRecords`).
182
288
  *
183
289
  * @throws Error if any field fails validation
290
+ * @throws CanonicalValueError (code BOUNDS_INVALID_VALUE) if a value carries a raw LF/CR
184
291
  */
185
292
  export function canonicalBounds(params: AgentBoundsParams, profile: AgentProfile): string {
186
293
  const validation = validateBoundsParams(params, profile);
@@ -188,19 +295,17 @@ export function canonicalBounds(params: AgentBoundsParams, profile: AgentProfile
188
295
  throw new Error(`Invalid bounds parameters: ${validation.errors.join('; ')}`);
189
296
  }
190
297
 
191
- const lines = profile.boundsSchema!.keyOrder.map(
192
- (key) => `${key}=${String(params[key])}`
193
- );
194
-
195
- return lines.join('\n');
298
+ return canonicalRecords(params, profile.boundsSchema!.keyOrder, 'BOUNDS_INVALID_VALUE');
196
299
  }
197
300
 
198
301
  /**
199
302
  * Builds the canonical context string from parameters.
200
- * All values are converted to strings. Keys are ordered per profile's contextSchema.keyOrder.
303
+ * Keys are ordered per profile's contextSchema.keyOrder; values are encoded per
304
+ * protocol.md → *Value encoding* (see `canonicalRecords`).
201
305
  * For empty context (no contextSchema or no fields), returns "".
202
306
  *
203
307
  * @throws Error if any field fails validation
308
+ * @throws CanonicalValueError (code CONTEXT_INVALID_VALUE) if a value carries a raw LF/CR
204
309
  */
205
310
  export function canonicalContext(params: AgentContextParams, profile: AgentProfile): string {
206
311
  // No contextSchema or no fields → empty context
@@ -213,11 +318,7 @@ export function canonicalContext(params: AgentContextParams, profile: AgentProfi
213
318
  throw new Error(`Invalid context parameters: ${validation.errors.join('; ')}`);
214
319
  }
215
320
 
216
- const lines = profile.contextSchema.keyOrder.map(
217
- (key) => `${key}=${String(params[key])}`
218
- );
219
-
220
- return lines.join('\n');
321
+ return canonicalRecords(params, profile.contextSchema.keyOrder, 'CONTEXT_INVALID_VALUE');
221
322
  }
222
323
 
223
324
  /**
@@ -86,6 +86,28 @@ export async function verify(
86
86
  }
87
87
  }
88
88
 
89
+ /**
90
+ * Fail closed when there is nothing to verify.
91
+ *
92
+ * The attestation loop below is a `for` over `request.attestations`. With an
93
+ * empty (or missing) array it runs zero times, pushes zero errors, and control
94
+ * falls through to the bounds check — which happily approves a call whose
95
+ * signature, bounds_hash, and TTL were never checked. "No errors" is not the
96
+ * same as "verified"; an absent attestation is the one case where silence must
97
+ * read as refusal.
98
+ */
99
+ function noAttestationSupplied(request: GatekeeperRequest): boolean {
100
+ return !Array.isArray(request.attestations) || request.attestations.length === 0;
101
+ }
102
+
103
+ /** Built fresh per call so a caller cannot mutate a shared error array. */
104
+ function noAttestationResult(): GatekeeperResult {
105
+ return {
106
+ approved: false,
107
+ errors: [{ code: 'MALFORMED_ATTESTATION', message: 'No attestation supplied — nothing to verify' }],
108
+ };
109
+ }
110
+
89
111
  // ─── v0.3 Verification ────────────────────────────────────────────────────────
90
112
 
91
113
  async function verifyV3(
@@ -96,6 +118,8 @@ async function verifyV3(
96
118
  executionLog: ExecutionLogQuery | undefined,
97
119
  errors: GatekeeperError[],
98
120
  ): Promise<GatekeeperResult> {
121
+ if (noAttestationSupplied(request)) return noAttestationResult();
122
+
99
123
  let expectedFrameHash: string;
100
124
  try {
101
125
  expectedFrameHash = computeFrameHash(request.frame, profile);
@@ -179,6 +203,8 @@ async function verifyV4(
179
203
  now: number,
180
204
  executionLog: ExecutionLogQuery | undefined,
181
205
  ): Promise<GatekeeperResult> {
206
+ if (noAttestationSupplied(request)) return noAttestationResult();
207
+
182
208
  const errors: GatekeeperError[] = [];
183
209
 
184
210
  // In v0.4 the `frame` param carries bounds; `context` carries context params
@@ -712,7 +712,13 @@ export interface GatekeeperError {
712
712
  | 'MANDATE_SIGNATURE_REQUIRED'
713
713
  | 'MANDATE_SIGNATURE_INVALID'
714
714
  | 'APPROVAL_SIGNATURE_REQUIRED'
715
- | 'APPROVAL_SIGNATURE_INVALID';
715
+ | 'APPROVAL_SIGNATURE_INVALID'
716
+ // Canonicalization refusals (protocol.md → Bounds & Scope Canonicalization
717
+ // → Value encoding). Raised when a value cannot be canonicalized at all —
718
+ // a raw LF/CR, or a profile `constraint.pattern` violation. Distinct from
719
+ // BOUNDS_MISMATCH / CONTEXT_MISMATCH, which mean the hashes disagree.
720
+ | 'BOUNDS_INVALID_VALUE'
721
+ | 'CONTEXT_INVALID_VALUE';
716
722
  field?: string;
717
723
  message: string;
718
724
  bound?: string | number;
@@ -8,7 +8,7 @@ This package contains type definitions for node (https://nodejs.org/).
8
8
  Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node.
9
9
 
10
10
  ### Additional Details
11
- * Last updated: Mon, 24 Aug 2026 19:40:21 GMT
11
+ * Last updated: Tue, 01 Sep 2026 20:06:05 GMT
12
12
  * Dependencies: [undici-types](https://npmjs.com/package/undici-types)
13
13
 
14
14
  # Credits
@@ -463,4 +463,9 @@ declare module "node:buffer" {
463
463
  */
464
464
  type AllowSharedBuffer = Buffer<ArrayBufferLike>;
465
465
  }
466
+ /**
467
+ * @deprecated This is intended for internal use, and will be removed once `@types/node` no longer supports
468
+ * TypeScript versions earlier than 5.7.
469
+ */
470
+ type BufferView<T extends NodeJS.ArrayBufferView> = T extends NodeJS.ArrayBufferView<infer B> ? Buffer<B> : never;
466
471
  }
@@ -3475,7 +3475,6 @@ declare module "node:crypto" {
3475
3475
  * ```
3476
3476
  * @since v24.7.0
3477
3477
  * @param algorithm Variant of Argon2, one of `"argon2d"`, `"argon2i"` or `"argon2id"`.
3478
- * @experimental
3479
3478
  */
3480
3479
  function argon2(
3481
3480
  algorithm: Argon2Algorithm,
@@ -3515,7 +3514,6 @@ declare module "node:crypto" {
3515
3514
  * console.log(derivedKey.toString('hex')); // 'af91dad...9520f15'
3516
3515
  * ```
3517
3516
  * @since v24.7.0
3518
- * @experimental
3519
3517
  */
3520
3518
  function argon2Sync(algorithm: Argon2Algorithm, parameters: Argon2Parameters): NonSharedBuffer;
3521
3519
  /**