@suveren/gateway 0.7.4 → 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.
- package/content/integrations/mollie.json +103 -28
- package/dist/control-plane/index.mjs +248 -58
- package/dist/mcp-server/http.mjs +577 -124
- package/dist/ui/assets/index-BFnSVa9N.js +110 -0
- package/dist/ui/assets/{index-CkWaRck1.css → index-BYVb_0oG.css} +1 -1
- package/dist/ui/index.html +2 -2
- package/node_modules/@hap/core/dist/index.d.mts +285 -13
- package/node_modules/@hap/core/dist/index.d.ts +285 -13
- package/node_modules/@hap/core/dist/index.js +281 -13
- package/node_modules/@hap/core/dist/index.mjs +266 -12
- package/node_modules/@hap/core/package.json +4 -2
- package/node_modules/@hap/core/src/did-key.ts +126 -0
- package/node_modules/@hap/core/src/frame.ts +115 -14
- package/node_modules/@hap/core/src/gatekeeper.ts +26 -0
- package/node_modules/@hap/core/src/identity.ts +21 -5
- package/node_modules/@hap/core/src/index.ts +3 -0
- package/node_modules/@hap/core/src/mandate.ts +207 -0
- package/node_modules/@hap/core/src/receipt.ts +74 -0
- package/node_modules/@hap/core/src/types.ts +87 -5
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/accept.js +54 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/charset.js +10 -34
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/encoding.js +9 -30
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/language.js +15 -36
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/lib/mediaType.js +16 -134
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/LICENSE +22 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/README.md +71 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.d.ts +46 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.js +176 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/dist/index.js.map +1 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/node_modules/content-type/package.json +52 -0
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/package.json +16 -10
- package/node_modules/@types/node/README.md +1 -1
- package/node_modules/@types/node/buffer.buffer.d.ts +10 -5
- package/node_modules/@types/node/crypto.d.ts +2 -4
- package/node_modules/@types/node/dgram.d.ts +119 -4
- package/node_modules/@types/node/ffi.d.ts +1 -1
- package/node_modules/@types/node/fs/promises.d.ts +61 -65
- package/node_modules/@types/node/fs.d.ts +86 -76
- package/node_modules/@types/node/http.d.ts +17 -1
- package/node_modules/@types/node/http2.d.ts +12 -4
- package/node_modules/@types/node/index.d.ts +1 -0
- package/node_modules/@types/node/inspector.d.ts +2 -1
- package/node_modules/@types/node/net.d.ts +108 -16
- package/node_modules/@types/node/package.json +2 -2
- package/node_modules/@types/node/process.d.ts +52 -0
- package/node_modules/@types/node/quic.d.ts +246 -38
- package/node_modules/@types/node/sqlite.d.ts +6 -2
- package/node_modules/@types/node/stream/iter.d.ts +4 -4
- package/node_modules/@types/node/test.d.ts +36 -0
- package/node_modules/@types/node/tls.d.ts +24 -0
- package/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +5 -0
- package/node_modules/@types/node/ts5.6/index.d.ts +1 -0
- package/node_modules/@types/node/ts5.7/index.d.ts +1 -0
- package/node_modules/@types/node/tty.d.ts +4 -3
- package/node_modules/@types/node/vfs.d.ts +210 -0
- package/node_modules/@types/node/vm.d.ts +1 -1
- package/node_modules/express-rate-limit/dist/index.cjs +6 -3
- package/node_modules/express-rate-limit/dist/index.d.cts +11 -0
- package/node_modules/express-rate-limit/dist/index.d.mts +11 -0
- package/node_modules/express-rate-limit/dist/index.d.ts +11 -0
- package/node_modules/express-rate-limit/dist/index.mjs +6 -3
- package/node_modules/express-rate-limit/{license.md → license} +7 -6
- package/node_modules/express-rate-limit/package.json +5 -5
- package/node_modules/express-rate-limit/readme.md +1 -0
- package/node_modules/fast-uri/index.js +215 -47
- package/node_modules/fast-uri/lib/schemes.js +9 -4
- package/node_modules/fast-uri/lib/utils.js +396 -92
- package/node_modules/fast-uri/package.json +1 -1
- package/node_modules/fast-uri/test/component-safe-serialization.test.js +163 -0
- package/node_modules/fast-uri/test/equal.test.js +31 -3
- package/node_modules/fast-uri/test/fixtures/uri-js-parse.json +2 -0
- package/node_modules/fast-uri/test/ipv6-canonical.test.js +34 -0
- package/node_modules/fast-uri/test/ipv6-validation.test.js +124 -0
- package/node_modules/fast-uri/test/malformed-percent.test.js +77 -0
- package/node_modules/fast-uri/test/malformed-urn.test.js +61 -0
- package/node_modules/fast-uri/test/parse.test.js +7 -3
- package/node_modules/fast-uri/test/query-fragment-normalization.test.js +33 -0
- package/node_modules/fast-uri/test/reserved-path-normalization.test.js +109 -0
- package/node_modules/fast-uri/test/scheme-validation.test.js +124 -0
- package/node_modules/fast-uri/test/security-normalization.test.js +101 -0
- package/node_modules/fast-uri/test/security.test.js +75 -3
- package/node_modules/fast-uri/test/urn-full-input.test.js +29 -0
- package/node_modules/fast-uri/test/websocket-query-preservation.test.js +24 -0
- package/node_modules/hono/dist/cjs/client/client.js +25 -11
- package/node_modules/hono/dist/cjs/client/utils.js +4 -1
- package/node_modules/hono/dist/cjs/context.js +4 -0
- package/node_modules/hono/dist/cjs/helper/accepts/accepts.js +36 -2
- package/node_modules/hono/dist/cjs/helper/ssg/ssg.js +1 -1
- package/node_modules/hono/dist/cjs/helper/ssg/utils.js +30 -10
- package/node_modules/hono/dist/cjs/jsx/dom/render.js +2 -0
- package/node_modules/hono/dist/cjs/middleware/cache/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/cors/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/csrf/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/etag/digest.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/etag/index.js +3 -3
- package/node_modules/hono/dist/cjs/middleware/pretty-json/index.js +3 -1
- package/node_modules/hono/dist/cjs/request.js +8 -4
- package/node_modules/hono/dist/cjs/router/linear-router/router.js +7 -2
- package/node_modules/hono/dist/cjs/router/pattern-router/router.js +3 -9
- package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +10 -3
- package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +47 -83
- package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +2 -1
- package/node_modules/hono/dist/cjs/router/trie-router/node.js +46 -69
- package/node_modules/hono/dist/cjs/router/trie-router/router.js +3 -11
- package/node_modules/hono/dist/cjs/router/utils.js +27 -0
- package/node_modules/hono/dist/cjs/utils/body.js +15 -3
- package/node_modules/hono/dist/cjs/utils/cookie.js +1 -1
- package/node_modules/hono/dist/cjs/utils/ipaddr.js +5 -3
- package/node_modules/hono/dist/cjs/utils/stream.js +7 -1
- package/node_modules/hono/dist/cjs/utils/url.js +9 -1
- package/node_modules/hono/dist/client/client.js +25 -11
- package/node_modules/hono/dist/client/utils.js +4 -1
- package/node_modules/hono/dist/context.js +4 -0
- package/node_modules/hono/dist/helper/accepts/accepts.js +36 -2
- package/node_modules/hono/dist/helper/ssg/ssg.js +1 -1
- package/node_modules/hono/dist/helper/ssg/utils.js +30 -10
- package/node_modules/hono/dist/jsx/dom/render.js +2 -0
- package/node_modules/hono/dist/middleware/cache/index.js +1 -1
- package/node_modules/hono/dist/middleware/cors/index.js +1 -1
- package/node_modules/hono/dist/middleware/csrf/index.js +1 -1
- package/node_modules/hono/dist/middleware/etag/digest.js +1 -1
- package/node_modules/hono/dist/middleware/etag/index.js +3 -3
- package/node_modules/hono/dist/middleware/pretty-json/index.js +3 -1
- package/node_modules/hono/dist/request.js +8 -4
- package/node_modules/hono/dist/router/linear-router/router.js +7 -2
- package/node_modules/hono/dist/router/pattern-router/router.js +3 -9
- package/node_modules/hono/dist/router/reg-exp-router/node.js +6 -2
- package/node_modules/hono/dist/router/reg-exp-router/router.js +53 -84
- package/node_modules/hono/dist/router/reg-exp-router/trie.js +2 -1
- package/node_modules/hono/dist/router/trie-router/node.js +46 -69
- package/node_modules/hono/dist/router/trie-router/router.js +3 -11
- package/node_modules/hono/dist/router/utils.js +5 -0
- package/node_modules/hono/dist/types/context.d.ts +4 -0
- package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +3 -0
- package/node_modules/hono/dist/types/router/trie-router/node.d.ts +1 -2
- package/node_modules/hono/dist/types/router/trie-router/router.d.ts +0 -1
- package/node_modules/hono/dist/types/router/utils.d.ts +1 -0
- package/node_modules/hono/dist/types/utils/url.d.ts +4 -0
- package/node_modules/hono/dist/utils/body.js +15 -3
- package/node_modules/hono/dist/utils/cookie.js +1 -1
- package/node_modules/hono/dist/utils/ipaddr.js +5 -3
- package/node_modules/hono/dist/utils/stream.js +7 -1
- package/node_modules/hono/dist/utils/url.js +9 -1
- package/node_modules/hono/package.json +1 -1
- package/node_modules/ip-address/README.md +92 -82
- package/node_modules/ip-address/dist/common.d.ts +20 -0
- package/node_modules/ip-address/dist/common.js +36 -0
- package/node_modules/ip-address/dist/common.js.map +1 -1
- package/node_modules/ip-address/dist/ipv4.d.ts +50 -0
- package/node_modules/ip-address/dist/ipv4.js +75 -0
- package/node_modules/ip-address/dist/ipv4.js.map +1 -1
- package/node_modules/ip-address/dist/ipv6.d.ts +79 -10
- package/node_modules/ip-address/dist/ipv6.js +120 -30
- package/node_modules/ip-address/dist/ipv6.js.map +1 -1
- package/node_modules/ip-address/dist/v4/constants.d.ts +12 -0
- package/node_modules/ip-address/dist/v4/constants.js +40 -1
- package/node_modules/ip-address/dist/v4/constants.js.map +1 -1
- package/node_modules/ip-address/dist/v6/constants.d.ts +14 -0
- package/node_modules/ip-address/dist/v6/constants.js +47 -1
- package/node_modules/ip-address/dist/v6/constants.js.map +1 -1
- package/node_modules/ip-address/package.json +1 -1
- package/node_modules/is-plain-object/README.md +39 -95
- package/node_modules/is-plain-object/dist/is-plain-object.js +0 -2
- package/node_modules/is-plain-object/is-plain-object.d.ts +6 -1
- package/node_modules/is-plain-object/package.json +8 -34
- package/node_modules/jose/dist/types/jwe/compact/decrypt.d.ts +7 -16
- package/node_modules/jose/dist/types/jwe/compact/encrypt.d.ts +10 -20
- package/node_modules/jose/dist/types/jwe/flattened/decrypt.d.ts +7 -16
- package/node_modules/jose/dist/types/jwe/flattened/encrypt.d.ts +16 -38
- package/node_modules/jose/dist/types/jwe/general/decrypt.d.ts +7 -16
- package/node_modules/jose/dist/types/jwe/general/encrypt.d.ts +19 -38
- package/node_modules/jose/dist/types/jwk/embedded.d.ts +3 -8
- package/node_modules/jose/dist/types/jwk/thumbprint.d.ts +5 -5
- package/node_modules/jose/dist/types/jwks/local.d.ts +6 -10
- package/node_modules/jose/dist/types/jwks/remote.d.ts +17 -28
- package/node_modules/jose/dist/types/jws/compact/sign.d.ts +5 -10
- package/node_modules/jose/dist/types/jws/compact/verify.d.ts +9 -18
- package/node_modules/jose/dist/types/jws/flattened/sign.d.ts +7 -16
- package/node_modules/jose/dist/types/jws/flattened/verify.d.ts +9 -18
- package/node_modules/jose/dist/types/jws/general/sign.d.ts +11 -21
- package/node_modules/jose/dist/types/jws/general/verify.d.ts +12 -21
- package/node_modules/jose/dist/types/jwt/decrypt.d.ts +10 -20
- package/node_modules/jose/dist/types/jwt/encrypt.d.ts +17 -29
- package/node_modules/jose/dist/types/jwt/sign.d.ts +12 -19
- package/node_modules/jose/dist/types/jwt/unsecured.d.ts +17 -28
- package/node_modules/jose/dist/types/jwt/verify.d.ts +9 -19
- package/node_modules/jose/dist/types/key/export.d.ts +4 -4
- package/node_modules/jose/dist/types/key/generate_key_pair.d.ts +6 -8
- package/node_modules/jose/dist/types/key/generate_secret.d.ts +4 -11
- package/node_modules/jose/dist/types/key/import.d.ts +10 -17
- package/node_modules/jose/dist/types/types.d.ts +48 -86
- package/node_modules/jose/dist/types/util/base64url.d.ts +2 -2
- package/node_modules/jose/dist/types/util/decode_jwt.d.ts +3 -10
- package/node_modules/jose/dist/types/util/decode_protected_header.d.ts +4 -4
- package/node_modules/jose/dist/types/util/errors.d.ts +26 -53
- package/node_modules/jose/dist/webapi/index.js +70 -32
- package/node_modules/jose/dist/webapi/jwe/compact/decrypt.js +7 -8
- package/node_modules/jose/dist/webapi/jwe/compact/encrypt.js +42 -26
- package/node_modules/jose/dist/webapi/jwe/flattened/decrypt.js +14 -10
- package/node_modules/jose/dist/webapi/jwe/flattened/encrypt.js +53 -71
- package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +38 -49
- package/node_modules/jose/dist/webapi/jwe/general/encrypt.js +121 -158
- package/node_modules/jose/dist/webapi/jwk/embedded.js +30 -18
- package/node_modules/jose/dist/webapi/jwk/thumbprint.js +54 -64
- package/node_modules/jose/dist/webapi/jwks/local.js +49 -74
- package/node_modules/jose/dist/webapi/jwks/remote.js +91 -160
- package/node_modules/jose/dist/webapi/jws/compact/sign.js +21 -20
- package/node_modules/jose/dist/webapi/jws/compact/verify.js +7 -8
- package/node_modules/jose/dist/webapi/jws/flattened/sign.js +29 -30
- package/node_modules/jose/dist/webapi/jws/flattened/verify.js +21 -23
- package/node_modules/jose/dist/webapi/jws/general/sign.js +61 -70
- package/node_modules/jose/dist/webapi/jws/general/verify.js +47 -41
- package/node_modules/jose/dist/webapi/jwt/decrypt.js +13 -22
- package/node_modules/jose/dist/webapi/jwt/encrypt.js +58 -100
- package/node_modules/jose/dist/webapi/jwt/sign.js +18 -50
- package/node_modules/jose/dist/webapi/jwt/unsecured.js +34 -63
- package/node_modules/jose/dist/webapi/jwt/verify.js +12 -14
- package/node_modules/jose/dist/webapi/key/export.js +32 -37
- package/node_modules/jose/dist/webapi/key/generate_key_pair.js +40 -44
- package/node_modules/jose/dist/webapi/key/generate_secret.js +35 -39
- package/node_modules/jose/dist/webapi/key/import.js +52 -52
- package/node_modules/jose/dist/webapi/lib/asn1.js +130 -164
- package/node_modules/jose/dist/webapi/lib/base64.js +18 -20
- package/node_modules/jose/dist/webapi/lib/buffer_utils.js +34 -38
- package/node_modules/jose/dist/webapi/lib/content_encryption.js +79 -123
- package/node_modules/jose/dist/webapi/lib/crypto_key.js +24 -25
- package/node_modules/jose/dist/webapi/lib/deflate.js +46 -40
- package/node_modules/jose/dist/webapi/lib/helpers.js +40 -37
- package/node_modules/jose/dist/webapi/lib/invalid_key_input.js +10 -24
- package/node_modules/jose/dist/webapi/lib/is_key_like.js +17 -15
- package/node_modules/jose/dist/webapi/lib/jwe_algorithms.js +106 -93
- package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +210 -176
- package/node_modules/jose/dist/webapi/lib/jwe_encrypt.js +105 -107
- package/node_modules/jose/dist/webapi/lib/jwk_metadata.js +19 -0
- package/node_modules/jose/dist/webapi/lib/jwk_to_key.js +9 -16
- package/node_modules/jose/dist/webapi/lib/jws_algorithms.js +62 -61
- package/node_modules/jose/dist/webapi/lib/jws_sign.js +58 -72
- package/node_modules/jose/dist/webapi/lib/jws_verify.js +90 -103
- package/node_modules/jose/dist/webapi/lib/jwt_claims_set.js +134 -185
- package/node_modules/jose/dist/webapi/lib/key.js +84 -121
- package/node_modules/jose/dist/webapi/lib/key_algorithm.js +13 -8
- package/node_modules/jose/dist/webapi/lib/key_descriptor.js +8 -6
- package/node_modules/jose/dist/webapi/lib/key_management.js +200 -225
- package/node_modules/jose/dist/webapi/lib/key_options.js +8 -0
- package/node_modules/jose/dist/webapi/lib/options.js +57 -44
- package/node_modules/jose/dist/webapi/lib/signing.js +18 -22
- package/node_modules/jose/dist/webapi/lib/type_checks.js +28 -33
- package/node_modules/jose/dist/webapi/util/base64url.js +26 -35
- package/node_modules/jose/dist/webapi/util/decode_jwt.js +32 -31
- package/node_modules/jose/dist/webapi/util/decode_protected_header.js +18 -23
- package/node_modules/jose/dist/webapi/util/errors.js +94 -84
- package/node_modules/jose/package.json +17 -81
- package/package.json +2 -2
- package/profiles/calendar/0.5.profile.json +190 -0
- package/profiles/charge/0.5.profile.json +210 -0
- package/profiles/customers/0.7.profile.json +157 -0
- package/profiles/deploy/0.9.profile.json +176 -0
- package/profiles/email/0.6.profile.json +208 -0
- package/profiles/index.json +9 -1
- package/profiles/publish/0.5.profile.json +186 -0
- package/profiles/purchase/0.5.profile.json +231 -0
- package/profiles/records/0.5.profile.json +117 -0
- package/dist/ui/assets/index-0Q2eNytX.js +0 -110
- package/node_modules/@modelcontextprotocol/sdk/node_modules/negotiator/HISTORY.md +0 -114
|
@@ -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
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - {@link deriveIdentityLine} — the human-readable footer line for a subject.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type { Subject } from './types';
|
|
11
|
+
import type { OwnerMandate, Subject } from './types';
|
|
12
12
|
|
|
13
13
|
export interface SubjectValidation {
|
|
14
14
|
valid: boolean;
|
|
@@ -19,12 +19,25 @@ export interface SubjectValidation {
|
|
|
19
19
|
* Enforce the Identity-Assurance invariants on a single subject:
|
|
20
20
|
* - `self_declared` ⇒ `low` / `self`, no disclosed name.
|
|
21
21
|
* - `as_vouched` ⇒ `high` / `as`, `verifier` required.
|
|
22
|
-
* - `eudi` ⇒ `high` / `external`, `verifier`
|
|
22
|
+
* - `eudi` ⇒ `high` / `external`, `verifier` required — and, when the
|
|
23
|
+
* carrying attestation's `owner_mandates` are supplied, a corresponding
|
|
24
|
+
* entry with `binding:"eudi"` for this subject's DID.
|
|
23
25
|
* - `disclose.name` only at `assurance:"high"`.
|
|
26
|
+
*
|
|
27
|
+
* v0.6: `Subject.owner_signature` is deprecated and IGNORED here — it signed
|
|
28
|
+
* the identity claim, not the mandate (see the field's deprecation note). The
|
|
29
|
+
* signature-bearing object is the attestation's `owner_mandates` entry, which
|
|
30
|
+
* is payload-level context this per-subject check cannot see on its own; pass
|
|
31
|
+
* `opts.ownerMandates` to enforce the eudi ⇒ mandate-entry rule, omit it to
|
|
32
|
+
* validate subject shape alone (pre-0.6 callers keep their behaviour minus
|
|
33
|
+
* the retired owner_signature requirement).
|
|
24
34
|
*/
|
|
25
|
-
export function validateSubject(
|
|
35
|
+
export function validateSubject(
|
|
36
|
+
subject: Subject,
|
|
37
|
+
opts?: { ownerMandates?: OwnerMandate[] },
|
|
38
|
+
): SubjectValidation {
|
|
26
39
|
const errors: string[] = [];
|
|
27
|
-
const { did, assurance, method, trust_root, verifier, disclose
|
|
40
|
+
const { did, assurance, method, trust_root, verifier, disclose } = subject;
|
|
28
41
|
|
|
29
42
|
if (!did) errors.push('subject.did is required');
|
|
30
43
|
|
|
@@ -39,7 +52,10 @@ export function validateSubject(subject: Subject): SubjectValidation {
|
|
|
39
52
|
} else if (method === 'eudi') {
|
|
40
53
|
if (trust_root !== 'external') errors.push('eudi requires trust_root "external"');
|
|
41
54
|
if (!verifier) errors.push('eudi requires a verifier');
|
|
42
|
-
if (
|
|
55
|
+
if (opts?.ownerMandates !== undefined) {
|
|
56
|
+
const entry = opts.ownerMandates.find(m => m.did === did && m.binding === 'eudi');
|
|
57
|
+
if (!entry) errors.push('eudi requires an owner_mandates entry with binding "eudi" for this DID');
|
|
58
|
+
}
|
|
43
59
|
} else {
|
|
44
60
|
errors.push(`high assurance requires method "as_vouched" or "eudi", got "${method}"`);
|
|
45
61
|
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owner Mandate Signatures (HAP v0.6) — the `HAP-mandate` projection, the
|
|
3
|
+
* `HAP-approval` object, and their verification.
|
|
4
|
+
*
|
|
5
|
+
* The human signs BEFORE the AS does, so they cannot sign the finished
|
|
6
|
+
* attestation (`attestation_id`/`issued_at` do not exist yet). They sign a
|
|
7
|
+
* mandate PROJECTION: a canonical object every field of which is known at
|
|
8
|
+
* approval time and reconstructible from the finished attestation — so a
|
|
9
|
+
* verifier rebuilds it from the attestation it already holds and checks the
|
|
10
|
+
* signature with the key carried in the owner's DID. No side channel, no
|
|
11
|
+
* second fetch, no key directory. See protocol.md → "Owner Mandate Signatures".
|
|
12
|
+
*
|
|
13
|
+
* Verification here requires NO trust in the AS. What it cannot do is tell the
|
|
14
|
+
* verifier WHOSE key signed — confirming the DID belongs to the expected
|
|
15
|
+
* person is the out-of-band step (protocol.md → "Identity DIDs vs signing
|
|
16
|
+
* DIDs"), and it is the honest cost of cold verification.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import * as ed from '@noble/ed25519';
|
|
20
|
+
import type { Attestation, AttestationPayload, OwnerMandate } from './types';
|
|
21
|
+
import { canonicalize } from './canonicalize';
|
|
22
|
+
import { decodeDidKey } from './did-key';
|
|
23
|
+
|
|
24
|
+
/** The object the owner signs — a canonical projection of the mandate. */
|
|
25
|
+
export interface MandateProjection {
|
|
26
|
+
typ: 'HAP-mandate';
|
|
27
|
+
/** Projection/canonicalization version. A verifier MUST pin it. */
|
|
28
|
+
version: '0.6';
|
|
29
|
+
profile_id: string;
|
|
30
|
+
/** The signing owner's own DID. MUST be a member of `resolved_owners`. */
|
|
31
|
+
owner_did: string;
|
|
32
|
+
bounds_hash: string;
|
|
33
|
+
context_hash: string;
|
|
34
|
+
execution_context_hash: string;
|
|
35
|
+
gate_content_hashes: Record<string, string>;
|
|
36
|
+
/** Included iff the attestation carries one — binds ciphertext AND the
|
|
37
|
+
* frozen approver set, the swap this mechanism exists to stop. */
|
|
38
|
+
intent_disclosure_hash?: string;
|
|
39
|
+
commitment_mode: string;
|
|
40
|
+
/** The replay defence: the human signs how long the authority lives. */
|
|
41
|
+
expires_at: number;
|
|
42
|
+
nonce: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Per-action approval, signed by the owner in `review` mode. Signing a
|
|
46
|
+
* `reject` matters as much as a `commit`: a rejection the AS can discard is a
|
|
47
|
+
* rejection that never happened. */
|
|
48
|
+
export interface ApprovalObject {
|
|
49
|
+
typ: 'HAP-approval';
|
|
50
|
+
version: '0.6';
|
|
51
|
+
proposal_id: string;
|
|
52
|
+
attestation_id: string;
|
|
53
|
+
decision: 'commit' | 'reject';
|
|
54
|
+
/** What was approved: the receipt's contentHash where the profile binds
|
|
55
|
+
* content; otherwise sha256 over the JCS of the proposal's argument set. */
|
|
56
|
+
content_hash: string;
|
|
57
|
+
decided_at: number;
|
|
58
|
+
nonce: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export class MandateError extends Error {
|
|
62
|
+
constructor(
|
|
63
|
+
public code:
|
|
64
|
+
| 'MANDATE_SIGNATURE_INVALID'
|
|
65
|
+
| 'APPROVAL_SIGNATURE_INVALID'
|
|
66
|
+
| 'NOT_KEY_BEARING'
|
|
67
|
+
| 'OWNER_NOT_RESOLVED'
|
|
68
|
+
| 'MALFORMED_ATTESTATION',
|
|
69
|
+
message: string,
|
|
70
|
+
) {
|
|
71
|
+
super(`${code}: ${message}`);
|
|
72
|
+
this.name = 'MandateError';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Rebuild the projection a given `owner_mandates` entry signed, from the
|
|
78
|
+
* attestation's own signed fields. Field absence is defined, not incidental:
|
|
79
|
+
* `intent_disclosure_hash` is included iff the attestation carries one.
|
|
80
|
+
*/
|
|
81
|
+
export function buildMandateProjection(
|
|
82
|
+
payload: AttestationPayload,
|
|
83
|
+
entry: Pick<OwnerMandate, 'did' | 'nonce'>,
|
|
84
|
+
): MandateProjection {
|
|
85
|
+
const { profile_id, bounds_hash, context_hash, execution_context_hash, gate_content_hashes, commitment_mode, expires_at } = payload;
|
|
86
|
+
if (!bounds_hash || !context_hash || !commitment_mode) {
|
|
87
|
+
throw new MandateError('MALFORMED_ATTESTATION', 'mandate projection requires bounds_hash, context_hash and commitment_mode');
|
|
88
|
+
}
|
|
89
|
+
const projection: MandateProjection = {
|
|
90
|
+
typ: 'HAP-mandate',
|
|
91
|
+
version: '0.6',
|
|
92
|
+
profile_id,
|
|
93
|
+
owner_did: entry.did,
|
|
94
|
+
bounds_hash,
|
|
95
|
+
context_hash,
|
|
96
|
+
execution_context_hash,
|
|
97
|
+
gate_content_hashes,
|
|
98
|
+
commitment_mode,
|
|
99
|
+
expires_at,
|
|
100
|
+
nonce: entry.nonce,
|
|
101
|
+
};
|
|
102
|
+
if (payload.intent_disclosure_hash !== undefined) {
|
|
103
|
+
projection.intent_disclosure_hash = payload.intent_disclosure_hash;
|
|
104
|
+
}
|
|
105
|
+
return projection;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The exact bytes an owner signs: RFC 8785 (JCS) canonical UTF-8. */
|
|
109
|
+
export function mandateSigningBytes(projection: MandateProjection): Uint8Array {
|
|
110
|
+
return new TextEncoder().encode(canonicalize(projection));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The exact bytes an owner signs for a per-action approval. */
|
|
114
|
+
export function approvalSigningBytes(approval: ApprovalObject): Uint8Array {
|
|
115
|
+
return new TextEncoder().encode(canonicalize(approval));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function base64urlToBytes(s: string): Uint8Array {
|
|
119
|
+
const base64 = s.replace(/-/g, '+').replace(/_/g, '/');
|
|
120
|
+
const padding = base64.length % 4 === 0 ? '' : '='.repeat(4 - (base64.length % 4));
|
|
121
|
+
return new Uint8Array(Buffer.from(base64 + padding, 'base64'));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function bytesToBase64url(bytes: Uint8Array): string {
|
|
125
|
+
return Buffer.from(bytes).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Sign a mandate projection with a raw Ed25519 private key — the `raw`
|
|
130
|
+
* binding: tests and CI only, no custody claim. WebAuthn (`webauthn`) and
|
|
131
|
+
* wallet (`eudi`) bindings sign the same bytes through their own custody;
|
|
132
|
+
* they are implemented by the platforms that hold those keys, not here.
|
|
133
|
+
*/
|
|
134
|
+
export async function signMandateProjection(
|
|
135
|
+
projection: MandateProjection,
|
|
136
|
+
privateKey: Uint8Array,
|
|
137
|
+
): Promise<string> {
|
|
138
|
+
const sig = await ed.signAsync(mandateSigningBytes(projection), privateKey);
|
|
139
|
+
return bytesToBase64url(sig);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Sign an approval object with a raw Ed25519 private key (`raw` binding). */
|
|
143
|
+
export async function signApproval(approval: ApprovalObject, privateKey: Uint8Array): Promise<string> {
|
|
144
|
+
const sig = await ed.signAsync(approvalSigningBytes(approval), privateKey);
|
|
145
|
+
return bytesToBase64url(sig);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Verify ONE `owner_mandates` entry against the attestation that carries it.
|
|
150
|
+
*
|
|
151
|
+
* Steps (protocol.md → "Verification procedure", steps 4 of 6): membership in
|
|
152
|
+
* `resolved_owners`, key-bearing DID, projection reconstruction, Ed25519 over
|
|
153
|
+
* JCS bytes. The DID is authoritative over `alg` — a disagreement is
|
|
154
|
+
* MANDATE_SIGNATURE_INVALID, never a fallback to the claimed algorithm.
|
|
155
|
+
*
|
|
156
|
+
* What this deliberately does NOT verify: that the DID belongs to the person
|
|
157
|
+
* the verifier expects (out-of-band, step 5) and the AS's own signature over
|
|
158
|
+
* the attestation (verifyAttestationSignature, step 1).
|
|
159
|
+
*/
|
|
160
|
+
export async function verifyOwnerMandate(attestation: Attestation, entry: OwnerMandate): Promise<void> {
|
|
161
|
+
const owners = attestation.payload.resolved_owners ?? [];
|
|
162
|
+
if (!owners.includes(entry.did)) {
|
|
163
|
+
throw new MandateError('OWNER_NOT_RESOLVED', `signing DID ${entry.did} is not in resolved_owners`);
|
|
164
|
+
}
|
|
165
|
+
if (entry.alg !== 'EdDSA') {
|
|
166
|
+
// The only key type a did:key carries in this protocol version is Ed25519;
|
|
167
|
+
// an entry claiming otherwise disagrees with its own DID.
|
|
168
|
+
throw new MandateError('MANDATE_SIGNATURE_INVALID', `alg ${entry.alg} disagrees with the DID's key type (DID is authoritative)`);
|
|
169
|
+
}
|
|
170
|
+
let publicKey: Uint8Array;
|
|
171
|
+
try {
|
|
172
|
+
publicKey = decodeDidKey(entry.did);
|
|
173
|
+
} catch (err) {
|
|
174
|
+
throw new MandateError('NOT_KEY_BEARING', (err as Error).message);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const projection = buildMandateProjection(attestation.payload, entry);
|
|
178
|
+
const ok = await ed.verifyAsync(base64urlToBytes(entry.signature), mandateSigningBytes(projection), publicKey);
|
|
179
|
+
if (!ok) {
|
|
180
|
+
throw new MandateError('MANDATE_SIGNATURE_INVALID', `owner mandate signature by ${entry.did} does not verify`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Verify every `owner_mandates` entry an attestation carries. Resolves to the
|
|
185
|
+
* verified entries; an attestation with none resolves to `[]` (the v0.5
|
|
186
|
+
* posture — nothing to check, and nothing claimed). */
|
|
187
|
+
export async function verifyOwnerMandates(attestation: Attestation): Promise<OwnerMandate[]> {
|
|
188
|
+
const entries = attestation.payload.owner_mandates ?? [];
|
|
189
|
+
for (const entry of entries) {
|
|
190
|
+
await verifyOwnerMandate(attestation, entry);
|
|
191
|
+
}
|
|
192
|
+
return entries;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Verify an approval signature against a signer's key-bearing DID. */
|
|
196
|
+
export async function verifyApproval(approval: ApprovalObject, signature: string, signerDid: string): Promise<void> {
|
|
197
|
+
let publicKey: Uint8Array;
|
|
198
|
+
try {
|
|
199
|
+
publicKey = decodeDidKey(signerDid);
|
|
200
|
+
} catch (err) {
|
|
201
|
+
throw new MandateError('NOT_KEY_BEARING', (err as Error).message);
|
|
202
|
+
}
|
|
203
|
+
const ok = await ed.verifyAsync(base64urlToBytes(signature), approvalSigningBytes(approval), publicKey);
|
|
204
|
+
if (!ok) {
|
|
205
|
+
throw new MandateError('APPROVAL_SIGNATURE_INVALID', `approval signature by ${signerDid} does not verify`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execution receipts (HAP v0.5/v0.6) — the wire type and holder-side
|
|
3
|
+
* verification. Until v0.6 this library had no receipt surface at all; every
|
|
4
|
+
* verifier hand-assembled the strip→canonicalize→verify dance. This module is
|
|
5
|
+
* that dance, once.
|
|
6
|
+
*
|
|
7
|
+
* Receipt payloads are camelCase on the wire (unlike the snake_case
|
|
8
|
+
* attestation payload) — a shipped inconsistency v0.6 chose to document
|
|
9
|
+
* rather than break (changelog.md → "Renames and terminology").
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as ed from '@noble/ed25519';
|
|
13
|
+
import type { ContentBinding, Subject } from './types';
|
|
14
|
+
import { canonicalize } from './canonicalize';
|
|
15
|
+
|
|
16
|
+
/** Signed execution receipt — protocol.md → "Receipt Payload Schema". */
|
|
17
|
+
export interface ReceiptPayload {
|
|
18
|
+
id: string;
|
|
19
|
+
groupId?: string | null;
|
|
20
|
+
userId?: string;
|
|
21
|
+
boundsHash: string;
|
|
22
|
+
profileId: string;
|
|
23
|
+
/** Downstream tool name. Audit metadata only — never a dispatch key. */
|
|
24
|
+
action: string;
|
|
25
|
+
/** Semantic category — drives cumulative bucketing and bounds dispatch. */
|
|
26
|
+
actionType: string;
|
|
27
|
+
executionContext: Record<string, unknown>;
|
|
28
|
+
cumulativeState?: Record<string, unknown>;
|
|
29
|
+
limits?: Record<string, unknown>;
|
|
30
|
+
timestamp: number;
|
|
31
|
+
/** v0.6 — hash of the bound content, computed by the Gatekeeper, copied
|
|
32
|
+
* verbatim by the AS. The AS never sees the content. */
|
|
33
|
+
contentHash?: string;
|
|
34
|
+
/** v0.6 — the binding declaration echoed into the signed receipt, so a
|
|
35
|
+
* verifier knows exactly what `contentHash` covers. */
|
|
36
|
+
contentBinding?: Pick<ContentBinding, 'version' | 'kind' | 'fields' | 'required_fields' | 'appliesTo'>;
|
|
37
|
+
/** v0.6 — disclosed subset of the attestation's identity overlay. */
|
|
38
|
+
subjects?: Subject[];
|
|
39
|
+
/** v0.6 — review path: the proposal this receipt executed. */
|
|
40
|
+
proposalId?: string;
|
|
41
|
+
/** v0.6 — review path: the owner's HAP-approval signature (or its sha256)
|
|
42
|
+
* for the executed proposal. */
|
|
43
|
+
approvalSignature?: string;
|
|
44
|
+
/** Ed25519 over the JCS-canonical payload (this field excluded), base64url. */
|
|
45
|
+
signature: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Verify a receipt's AS signature: strip `signature`, JCS-canonicalize the
|
|
50
|
+
* rest, Ed25519-verify against the AS public key (hex). This is holder-side
|
|
51
|
+
* verification — it needs the COMPLETE receipt; a redacted public view cannot
|
|
52
|
+
* be re-verified this way (protocol.md → "Receipt Verification").
|
|
53
|
+
*
|
|
54
|
+
* Content is checked separately: where `contentHash` is present, recompute it
|
|
55
|
+
* from the held artifact via the content-binding module and compare.
|
|
56
|
+
*
|
|
57
|
+
* @throws Error prefixed `INVALID_SIGNATURE:` when verification fails.
|
|
58
|
+
*/
|
|
59
|
+
export async function verifyReceiptSignature(receipt: ReceiptPayload, publicKeyHex: string): Promise<void> {
|
|
60
|
+
const { signature, ...unsigned } = receipt;
|
|
61
|
+
if (!signature) {
|
|
62
|
+
throw new Error('INVALID_SIGNATURE: receipt carries no signature');
|
|
63
|
+
}
|
|
64
|
+
const bytes = new TextEncoder().encode(canonicalize(unsigned));
|
|
65
|
+
const base64 = signature.replace(/-/g, '+').replace(/_/g, '/');
|
|
66
|
+
const padding = base64.length % 4 === 0 ? '' : '='.repeat(4 - (base64.length % 4));
|
|
67
|
+
const sigBytes = new Uint8Array(Buffer.from(base64 + padding, 'base64'));
|
|
68
|
+
const publicKeyBytes = new Uint8Array(Buffer.from(publicKeyHex, 'hex'));
|
|
69
|
+
|
|
70
|
+
const ok = await ed.verifyAsync(sigBytes, bytes, publicKeyBytes).catch(() => false);
|
|
71
|
+
if (!ok) {
|
|
72
|
+
throw new Error('INVALID_SIGNATURE: receipt signature verification failed');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -19,7 +19,7 @@ export interface ResolvedDomain {
|
|
|
19
19
|
|
|
20
20
|
export interface AttestationPayload {
|
|
21
21
|
attestation_id: string;
|
|
22
|
-
version: '0.3' | '0.4' | '0.5';
|
|
22
|
+
version: '0.3' | '0.4' | '0.5' | '0.6';
|
|
23
23
|
profile_id: string;
|
|
24
24
|
/** v0.3 (deprecated) — hash of the authorization frame */
|
|
25
25
|
frame_hash?: string;
|
|
@@ -67,12 +67,55 @@ export interface AttestationPayload {
|
|
|
67
67
|
*
|
|
68
68
|
* Cryptographically bound in the signed payload so a compromised SP cannot
|
|
69
69
|
* silently flip 'review' → 'automatic'. Absent on v0.3 attestations.
|
|
70
|
+
* `review_above_cap` (v0.5): automatic below the signed caps, proposal above.
|
|
70
71
|
*/
|
|
71
|
-
commitment_mode?: 'automatic' | 'review';
|
|
72
|
+
commitment_mode?: 'automatic' | 'review' | 'review_above_cap';
|
|
73
|
+
/**
|
|
74
|
+
* v0.6 (Owner Mandate Signatures) — the Decision Owner's own signature(s)
|
|
75
|
+
* over the mandate projection ({@link MandateProjection}), one entry per
|
|
76
|
+
* co-signing owner. Carried INSIDE the AS-signed payload, so the AS attests
|
|
77
|
+
* to having received it and cannot strip it without invalidating its own
|
|
78
|
+
* signature. Optional and additive: an attestation without it behaves as
|
|
79
|
+
* before (the v0.5 posture — the AS asserts, nobody co-signs).
|
|
80
|
+
*/
|
|
81
|
+
owner_mandates?: OwnerMandate[];
|
|
72
82
|
issued_at: number;
|
|
73
83
|
expires_at: number;
|
|
74
84
|
}
|
|
75
85
|
|
|
86
|
+
/** Mandate-assurance axis — what custody signed the mandate. Independent of
|
|
87
|
+
* identity assurance ({@link Subject.method}). */
|
|
88
|
+
export type MandateBinding = 'raw' | 'webauthn' | 'eudi';
|
|
89
|
+
|
|
90
|
+
/** Which surface showed the owner what they signed. A DECLARATION, not a proof
|
|
91
|
+
* — no verifier can check it; see protocol.md → Owner Mandate Signatures. */
|
|
92
|
+
export type SigningSurface = 'gatekeeper_local' | 'as_web' | 'wallet_display';
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* v0.6 — one owner's signature over the mandate projection.
|
|
96
|
+
*
|
|
97
|
+
* There is deliberately NO `public_key` field, and it MUST NOT be added: the
|
|
98
|
+
* verification key is carried in the DID itself (key-bearing `did:key`), so a
|
|
99
|
+
* non-key-bearing DID fails STRUCTURALLY instead of validating against a key
|
|
100
|
+
* the AS could have substituted. `alg` is kept only as curve-confusion
|
|
101
|
+
* hygiene; if it disagrees with the DID's key type, the DID is authoritative.
|
|
102
|
+
*/
|
|
103
|
+
export interface OwnerMandate {
|
|
104
|
+
/** The signing owner's key-bearing DID. MUST be a member of `resolved_owners`. */
|
|
105
|
+
did: string;
|
|
106
|
+
/** Signature algorithm hint. The DID's multicodec wins on any disagreement. */
|
|
107
|
+
alg: 'EdDSA' | 'ES256';
|
|
108
|
+
/** base64url (no padding) signature over the JCS bytes of the mandate projection. */
|
|
109
|
+
signature: string;
|
|
110
|
+
/** When the owner signed (unix seconds). */
|
|
111
|
+
signed_at: number;
|
|
112
|
+
/** Defence-in-depth against duplicate issuance by an HONEST AS only —
|
|
113
|
+
* AS-side nonce enforcement is no defence against the AS itself. */
|
|
114
|
+
nonce: string;
|
|
115
|
+
binding: MandateBinding;
|
|
116
|
+
signing_surface?: SigningSurface;
|
|
117
|
+
}
|
|
118
|
+
|
|
76
119
|
/**
|
|
77
120
|
* v0.6 Identity Assurance — a signed overlay binding a Decision Owner's verified
|
|
78
121
|
* real-world identity to the attestation, gated by HOW the identity was verified.
|
|
@@ -98,8 +141,11 @@ export interface Subject {
|
|
|
98
141
|
/** When the underlying verification was performed (unix seconds). */
|
|
99
142
|
verified_at?: number;
|
|
100
143
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
144
|
+
* @deprecated v0.6 — replaced by {@link AttestationPayload.owner_mandates}.
|
|
145
|
+
* This field signed the wrong object (the identity claim, not what was
|
|
146
|
+
* committed to) and was welded to one method. Implementations MUST NOT emit
|
|
147
|
+
* it; verifiers MAY ignore it on artifacts that carry it. Kept readable for
|
|
148
|
+
* audit of pre-0.6 artifacts only.
|
|
103
149
|
*/
|
|
104
150
|
owner_signature?: string | null;
|
|
105
151
|
}
|
|
@@ -340,6 +386,15 @@ export interface ProfileContextField {
|
|
|
340
386
|
description?: string;
|
|
341
387
|
displayName?: string;
|
|
342
388
|
format?: 'email' | 'domain' | 'url' | 'currency';
|
|
389
|
+
/**
|
|
390
|
+
* v0.6 — what this field NAMES on the read path. `counterparty` = the other
|
|
391
|
+
* party to a communication (matched against an item's participants);
|
|
392
|
+
* `resource` = the container an item belongs to (a direct attribute match —
|
|
393
|
+
* and a resource scope enforced on writes MUST also bind reads). Absent →
|
|
394
|
+
* implementations MAY infer `counterparty` from `format: email|domain`
|
|
395
|
+
* (transitional; the explicit declaration is normative).
|
|
396
|
+
*/
|
|
397
|
+
scopeKind?: 'counterparty' | 'resource';
|
|
343
398
|
constraint?: FieldConstraint;
|
|
344
399
|
enum?: string[];
|
|
345
400
|
}
|
|
@@ -636,7 +691,34 @@ export interface GatekeeperRequest {
|
|
|
636
691
|
* Structured error from Gatekeeper verification.
|
|
637
692
|
*/
|
|
638
693
|
export interface GatekeeperError {
|
|
639
|
-
code:
|
|
694
|
+
code:
|
|
695
|
+
| 'BOUND_EXCEEDED'
|
|
696
|
+
| 'CUMULATIVE_LIMIT_EXCEEDED'
|
|
697
|
+
| 'INVALID_SIGNATURE'
|
|
698
|
+
| 'TTL_EXPIRED'
|
|
699
|
+
| 'FRAME_MISMATCH'
|
|
700
|
+
| 'BOUNDS_MISMATCH'
|
|
701
|
+
| 'CONTEXT_MISMATCH'
|
|
702
|
+
| 'DOMAIN_NOT_COVERED'
|
|
703
|
+
| 'INVALID_PROFILE'
|
|
704
|
+
| 'MALFORMED_ATTESTATION'
|
|
705
|
+
// v0.5/v0.6 receipt-path codes a Gatekeeper must recognize as definitive
|
|
706
|
+
// rejections (protocol.md → Pre-flight Receipt Request):
|
|
707
|
+
| 'INVALID_ACTION_TYPE'
|
|
708
|
+
| 'ATTESTATION_REVOKED'
|
|
709
|
+
| 'APPROVAL_REQUIRED'
|
|
710
|
+
| 'IDEMPOTENCY_MISMATCH'
|
|
711
|
+
// v0.6 owner-mandate codes (protocol.md → Owner Mandate Signatures):
|
|
712
|
+
| 'MANDATE_SIGNATURE_REQUIRED'
|
|
713
|
+
| 'MANDATE_SIGNATURE_INVALID'
|
|
714
|
+
| 'APPROVAL_SIGNATURE_REQUIRED'
|
|
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';
|
|
640
722
|
field?: string;
|
|
641
723
|
message: string;
|
|
642
724
|
bound?: string | number;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* negotiator
|
|
3
|
+
* Copyright(c) 2026 Blake Embrey
|
|
4
|
+
* MIT Licensed
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
var contentType = require('content-type');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Module exports.
|
|
13
|
+
* @private
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
module.exports = parseAccept;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Parse an Accept-style header.
|
|
20
|
+
* @private
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
function parseAccept(header) {
|
|
24
|
+
var values = [];
|
|
25
|
+
var index = 0;
|
|
26
|
+
|
|
27
|
+
while (index < header.length) {
|
|
28
|
+
var start = skipOptionalWhitespace(header, index);
|
|
29
|
+
var parsed = contentType.parse(header, { comma: true, start: start });
|
|
30
|
+
|
|
31
|
+
// `content-type` normalizes the type, but accept methods return original casing.
|
|
32
|
+
parsed.type = header.slice(start, start + parsed.type.length);
|
|
33
|
+
values.push(parsed);
|
|
34
|
+
|
|
35
|
+
index = parsed.index + 1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return values;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Skip optional whitespace.
|
|
43
|
+
* @private
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
function skipOptionalWhitespace(header, index) {
|
|
47
|
+
var cursor = index;
|
|
48
|
+
|
|
49
|
+
while (header.charCodeAt(cursor) === 0x20 || header.charCodeAt(cursor) === 0x09) {
|
|
50
|
+
cursor++;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return cursor;
|
|
54
|
+
}
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
|
+
var parseAccept = require('./accept');
|
|
12
|
+
|
|
11
13
|
/**
|
|
12
14
|
* Module exports.
|
|
13
15
|
* @public
|
|
@@ -21,55 +23,29 @@ module.exports.preferredCharsets = preferredCharsets;
|
|
|
21
23
|
* @private
|
|
22
24
|
*/
|
|
23
25
|
|
|
24
|
-
var simpleCharsetRegExp = /^\s*([^\s;]+)\s*(?:;(.*))?$/;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Parse the Accept-Charset header.
|
|
28
|
-
* @private
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
26
|
function parseAcceptCharset(accept) {
|
|
32
|
-
var accepts = accept
|
|
27
|
+
var accepts = parseAccept(accept);
|
|
33
28
|
|
|
34
29
|
for (var i = 0, j = 0; i < accepts.length; i++) {
|
|
35
|
-
var charset =
|
|
36
|
-
|
|
37
|
-
if (charset) {
|
|
38
|
-
accepts[j++] = charset;
|
|
39
|
-
}
|
|
30
|
+
var charset = formatCharset(accepts[i], i);
|
|
31
|
+
if (charset) accepts[j++] = charset;
|
|
40
32
|
}
|
|
41
33
|
|
|
42
|
-
// trim accepts
|
|
43
34
|
accepts.length = j;
|
|
44
|
-
|
|
45
35
|
return accepts;
|
|
46
36
|
}
|
|
47
37
|
|
|
48
38
|
/**
|
|
49
|
-
*
|
|
39
|
+
* Format a parsed charset for negotiation.
|
|
50
40
|
* @private
|
|
51
41
|
*/
|
|
52
42
|
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
if (!match) return null;
|
|
56
|
-
|
|
57
|
-
var charset = match[1];
|
|
58
|
-
var q = 1;
|
|
59
|
-
if (match[2]) {
|
|
60
|
-
var params = match[2].split(';')
|
|
61
|
-
for (var j = 0; j < params.length; j++) {
|
|
62
|
-
var p = params[j].trim().split('=');
|
|
63
|
-
if (p[0] === 'q') {
|
|
64
|
-
q = parseFloat(p[1]);
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
43
|
+
function formatCharset(parsed, i) {
|
|
44
|
+
if (!parsed.type) return null;
|
|
69
45
|
|
|
70
46
|
return {
|
|
71
|
-
charset:
|
|
72
|
-
q: q,
|
|
47
|
+
charset: parsed.type,
|
|
48
|
+
q: parsed.parameters.q ? parseFloat(parsed.parameters.q) : 1,
|
|
73
49
|
i: i
|
|
74
50
|
};
|
|
75
51
|
}
|