@suveren/gateway 0.2.3 → 0.2.4
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/node_modules/@hap/core/dist/index.d.mts +36 -1
- package/node_modules/@hap/core/dist/index.d.ts +36 -1
- package/node_modules/@hap/core/dist/index.js +28 -1
- package/node_modules/@hap/core/dist/index.mjs +27 -1
- package/node_modules/@hap/core/package.json +12 -11
- package/node_modules/@hap/core/src/attestation.ts +4 -1
- package/node_modules/@hap/core/src/canonicalize.ts +65 -0
- package/node_modules/@hap/core/src/index.ts +1 -0
- package/package.json +2 -2
|
@@ -401,6 +401,41 @@ type GatekeeperResult = {
|
|
|
401
401
|
errors: GatekeeperError[];
|
|
402
402
|
};
|
|
403
403
|
|
|
404
|
+
/**
|
|
405
|
+
* JCS — JSON Canonicalization (RFC 8785) for signing.
|
|
406
|
+
*
|
|
407
|
+
* Both attestation and receipt signatures are computed over a canonical byte
|
|
408
|
+
* serialization of the payload, so that two independent implementations (an AS,
|
|
409
|
+
* a gateway, an external verifier — in any language) agree on exactly which
|
|
410
|
+
* bytes were signed regardless of how they happened to construct the object.
|
|
411
|
+
*
|
|
412
|
+
* `JSON.stringify` is NOT sufficient: it preserves key *insertion* order, so the
|
|
413
|
+
* same logical payload built with keys in a different order serializes to
|
|
414
|
+
* different bytes and the signature fails to verify. Canonicalization removes
|
|
415
|
+
* that dependency by sorting keys.
|
|
416
|
+
*
|
|
417
|
+
* This is RFC 8785-compatible and matches the v0.5 spec's normative signing
|
|
418
|
+
* canonicalization (core.md "Signing Canonicalization"):
|
|
419
|
+
* 1. UTF-8.
|
|
420
|
+
* 2. Object keys sorted (RFC 8785 sorts by UTF-16 code units — exactly what
|
|
421
|
+
* JavaScript's default String comparison does, so `Object.keys().sort()` is
|
|
422
|
+
* correct here).
|
|
423
|
+
* 3. No insignificant whitespace.
|
|
424
|
+
* 4. Numbers in the shortest round-trippable form — the ECMAScript
|
|
425
|
+
* Number-to-String algorithm, which `JSON.stringify(number)` produces and
|
|
426
|
+
* RFC 8785 adopts verbatim.
|
|
427
|
+
* 5. Strings escaped per RFC 8259; non-ASCII passed through as UTF-8 (this is
|
|
428
|
+
* `JSON.stringify`'s behaviour for strings).
|
|
429
|
+
* 6. Array order preserved.
|
|
430
|
+
*
|
|
431
|
+
* Because it relies only on `JSON.stringify` for leaves plus `Object.keys`,
|
|
432
|
+
* `Array`, and `String` sort — all of which are environment-independent in
|
|
433
|
+
* JavaScript — Node and the browser produce byte-identical output. The
|
|
434
|
+
* conformance test pins a (payload → canonical bytes) vector that any other
|
|
435
|
+
* implementation can check against.
|
|
436
|
+
*/
|
|
437
|
+
declare function canonicalize(value: unknown): string;
|
|
438
|
+
|
|
404
439
|
/**
|
|
405
440
|
* Frame Canonicalization for Agent Profiles
|
|
406
441
|
*
|
|
@@ -598,4 +633,4 @@ declare function listProfiles(): string[];
|
|
|
598
633
|
declare function getAllProfiles(): AgentProfile[];
|
|
599
634
|
declare function clearProfiles(): void;
|
|
600
635
|
|
|
601
|
-
export { type AgentBoundsParams, type AgentContextParams, type AgentFrameParams, type AgentProfile, type Attestation, type AttestationHeader, type AttestationPayload, type BoundType, 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 ProfileBoundsField, type ProfileContextField, type ProfileFrameField, type ProfileToolGating, type ProfileToolGatingEntry, type ResolvedDomain, attestationId, canonicalBounds, canonicalContext, canonicalFrame, checkAttestationExpiry, clearProfiles, computeBoundsHash, computeContextHash, computeFrameHash, decodeAttestationBlob, encodeAttestationBlob, frameHash, getAllProfiles, getProfile, isV4Attestation, listProfiles, registerProfile, validateBoundsParams, validateContextParams, validateFrameParams, verify, verifyAttestation, verifyAttestationSignature, verifyAttestationV4, verifyBoundsHash, verifyContextHash, verifyFrameHash };
|
|
636
|
+
export { type AgentBoundsParams, type AgentContextParams, type AgentFrameParams, type AgentProfile, type Attestation, type AttestationHeader, type AttestationPayload, type BoundType, 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 ProfileBoundsField, type ProfileContextField, type ProfileFrameField, type ProfileToolGating, type ProfileToolGatingEntry, type ResolvedDomain, attestationId, canonicalBounds, canonicalContext, canonicalFrame, canonicalize, checkAttestationExpiry, clearProfiles, computeBoundsHash, computeContextHash, computeFrameHash, decodeAttestationBlob, encodeAttestationBlob, frameHash, getAllProfiles, getProfile, isV4Attestation, listProfiles, registerProfile, validateBoundsParams, validateContextParams, validateFrameParams, verify, verifyAttestation, verifyAttestationSignature, verifyAttestationV4, verifyBoundsHash, verifyContextHash, verifyFrameHash };
|
|
@@ -401,6 +401,41 @@ type GatekeeperResult = {
|
|
|
401
401
|
errors: GatekeeperError[];
|
|
402
402
|
};
|
|
403
403
|
|
|
404
|
+
/**
|
|
405
|
+
* JCS — JSON Canonicalization (RFC 8785) for signing.
|
|
406
|
+
*
|
|
407
|
+
* Both attestation and receipt signatures are computed over a canonical byte
|
|
408
|
+
* serialization of the payload, so that two independent implementations (an AS,
|
|
409
|
+
* a gateway, an external verifier — in any language) agree on exactly which
|
|
410
|
+
* bytes were signed regardless of how they happened to construct the object.
|
|
411
|
+
*
|
|
412
|
+
* `JSON.stringify` is NOT sufficient: it preserves key *insertion* order, so the
|
|
413
|
+
* same logical payload built with keys in a different order serializes to
|
|
414
|
+
* different bytes and the signature fails to verify. Canonicalization removes
|
|
415
|
+
* that dependency by sorting keys.
|
|
416
|
+
*
|
|
417
|
+
* This is RFC 8785-compatible and matches the v0.5 spec's normative signing
|
|
418
|
+
* canonicalization (core.md "Signing Canonicalization"):
|
|
419
|
+
* 1. UTF-8.
|
|
420
|
+
* 2. Object keys sorted (RFC 8785 sorts by UTF-16 code units — exactly what
|
|
421
|
+
* JavaScript's default String comparison does, so `Object.keys().sort()` is
|
|
422
|
+
* correct here).
|
|
423
|
+
* 3. No insignificant whitespace.
|
|
424
|
+
* 4. Numbers in the shortest round-trippable form — the ECMAScript
|
|
425
|
+
* Number-to-String algorithm, which `JSON.stringify(number)` produces and
|
|
426
|
+
* RFC 8785 adopts verbatim.
|
|
427
|
+
* 5. Strings escaped per RFC 8259; non-ASCII passed through as UTF-8 (this is
|
|
428
|
+
* `JSON.stringify`'s behaviour for strings).
|
|
429
|
+
* 6. Array order preserved.
|
|
430
|
+
*
|
|
431
|
+
* Because it relies only on `JSON.stringify` for leaves plus `Object.keys`,
|
|
432
|
+
* `Array`, and `String` sort — all of which are environment-independent in
|
|
433
|
+
* JavaScript — Node and the browser produce byte-identical output. The
|
|
434
|
+
* conformance test pins a (payload → canonical bytes) vector that any other
|
|
435
|
+
* implementation can check against.
|
|
436
|
+
*/
|
|
437
|
+
declare function canonicalize(value: unknown): string;
|
|
438
|
+
|
|
404
439
|
/**
|
|
405
440
|
* Frame Canonicalization for Agent Profiles
|
|
406
441
|
*
|
|
@@ -598,4 +633,4 @@ declare function listProfiles(): string[];
|
|
|
598
633
|
declare function getAllProfiles(): AgentProfile[];
|
|
599
634
|
declare function clearProfiles(): void;
|
|
600
635
|
|
|
601
|
-
export { type AgentBoundsParams, type AgentContextParams, type AgentFrameParams, type AgentProfile, type Attestation, type AttestationHeader, type AttestationPayload, type BoundType, 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 ProfileBoundsField, type ProfileContextField, type ProfileFrameField, type ProfileToolGating, type ProfileToolGatingEntry, type ResolvedDomain, attestationId, canonicalBounds, canonicalContext, canonicalFrame, checkAttestationExpiry, clearProfiles, computeBoundsHash, computeContextHash, computeFrameHash, decodeAttestationBlob, encodeAttestationBlob, frameHash, getAllProfiles, getProfile, isV4Attestation, listProfiles, registerProfile, validateBoundsParams, validateContextParams, validateFrameParams, verify, verifyAttestation, verifyAttestationSignature, verifyAttestationV4, verifyBoundsHash, verifyContextHash, verifyFrameHash };
|
|
636
|
+
export { type AgentBoundsParams, type AgentContextParams, type AgentFrameParams, type AgentProfile, type Attestation, type AttestationHeader, type AttestationPayload, type BoundType, 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 ProfileBoundsField, type ProfileContextField, type ProfileFrameField, type ProfileToolGating, type ProfileToolGatingEntry, type ResolvedDomain, attestationId, canonicalBounds, canonicalContext, canonicalFrame, canonicalize, checkAttestationExpiry, clearProfiles, computeBoundsHash, computeContextHash, computeFrameHash, decodeAttestationBlob, encodeAttestationBlob, frameHash, getAllProfiles, getProfile, isV4Attestation, listProfiles, registerProfile, validateBoundsParams, validateContextParams, validateFrameParams, verify, verifyAttestation, verifyAttestationSignature, verifyAttestationV4, verifyBoundsHash, verifyContextHash, verifyFrameHash };
|
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
canonicalBounds: () => canonicalBounds,
|
|
35
35
|
canonicalContext: () => canonicalContext,
|
|
36
36
|
canonicalFrame: () => canonicalFrame,
|
|
37
|
+
canonicalize: () => canonicalize,
|
|
37
38
|
checkAttestationExpiry: () => checkAttestationExpiry,
|
|
38
39
|
clearProfiles: () => clearProfiles,
|
|
39
40
|
computeBoundsHash: () => computeBoundsHash,
|
|
@@ -60,6 +61,31 @@ __export(index_exports, {
|
|
|
60
61
|
});
|
|
61
62
|
module.exports = __toCommonJS(index_exports);
|
|
62
63
|
|
|
64
|
+
// src/canonicalize.ts
|
|
65
|
+
function canonicalize(value) {
|
|
66
|
+
if (value === void 0) {
|
|
67
|
+
throw new Error("canonicalize: undefined is not serializable");
|
|
68
|
+
}
|
|
69
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
70
|
+
throw new Error(`canonicalize: ${value} is not a valid JSON number`);
|
|
71
|
+
}
|
|
72
|
+
if (value === null || typeof value !== "object") {
|
|
73
|
+
return JSON.stringify(value);
|
|
74
|
+
}
|
|
75
|
+
if (Array.isArray(value)) {
|
|
76
|
+
const items = value.map((el) => el === void 0 ? "null" : canonicalize(el));
|
|
77
|
+
return "[" + items.join(",") + "]";
|
|
78
|
+
}
|
|
79
|
+
const obj = value;
|
|
80
|
+
const parts = [];
|
|
81
|
+
for (const key of Object.keys(obj).sort()) {
|
|
82
|
+
const v = obj[key];
|
|
83
|
+
if (v === void 0) continue;
|
|
84
|
+
parts.push(JSON.stringify(key) + ":" + canonicalize(v));
|
|
85
|
+
}
|
|
86
|
+
return "{" + parts.join(",") + "}";
|
|
87
|
+
}
|
|
88
|
+
|
|
63
89
|
// src/frame.ts
|
|
64
90
|
var import_crypto = require("crypto");
|
|
65
91
|
function validateFrameParams(params, profile) {
|
|
@@ -215,7 +241,7 @@ function attestationId(blob) {
|
|
|
215
241
|
}
|
|
216
242
|
async function verifyAttestationSignature(attestation, publicKeyHex) {
|
|
217
243
|
try {
|
|
218
|
-
const payloadJson =
|
|
244
|
+
const payloadJson = canonicalize(attestation.payload);
|
|
219
245
|
const payloadBytes = new TextEncoder().encode(payloadJson);
|
|
220
246
|
const signatureBytes = Buffer.from(attestation.signature, "base64");
|
|
221
247
|
const publicKeyBytes = Buffer.from(publicKeyHex, "hex");
|
|
@@ -719,6 +745,7 @@ function resolveCumulativeFields(request, profile, executionLog, now) {
|
|
|
719
745
|
canonicalBounds,
|
|
720
746
|
canonicalContext,
|
|
721
747
|
canonicalFrame,
|
|
748
|
+
canonicalize,
|
|
722
749
|
checkAttestationExpiry,
|
|
723
750
|
clearProfiles,
|
|
724
751
|
computeBoundsHash,
|
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
// src/canonicalize.ts
|
|
2
|
+
function canonicalize(value) {
|
|
3
|
+
if (value === void 0) {
|
|
4
|
+
throw new Error("canonicalize: undefined is not serializable");
|
|
5
|
+
}
|
|
6
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
7
|
+
throw new Error(`canonicalize: ${value} is not a valid JSON number`);
|
|
8
|
+
}
|
|
9
|
+
if (value === null || typeof value !== "object") {
|
|
10
|
+
return JSON.stringify(value);
|
|
11
|
+
}
|
|
12
|
+
if (Array.isArray(value)) {
|
|
13
|
+
const items = value.map((el) => el === void 0 ? "null" : canonicalize(el));
|
|
14
|
+
return "[" + items.join(",") + "]";
|
|
15
|
+
}
|
|
16
|
+
const obj = value;
|
|
17
|
+
const parts = [];
|
|
18
|
+
for (const key of Object.keys(obj).sort()) {
|
|
19
|
+
const v = obj[key];
|
|
20
|
+
if (v === void 0) continue;
|
|
21
|
+
parts.push(JSON.stringify(key) + ":" + canonicalize(v));
|
|
22
|
+
}
|
|
23
|
+
return "{" + parts.join(",") + "}";
|
|
24
|
+
}
|
|
25
|
+
|
|
1
26
|
// src/frame.ts
|
|
2
27
|
import { createHash } from "crypto";
|
|
3
28
|
function validateFrameParams(params, profile) {
|
|
@@ -153,7 +178,7 @@ function attestationId(blob) {
|
|
|
153
178
|
}
|
|
154
179
|
async function verifyAttestationSignature(attestation, publicKeyHex) {
|
|
155
180
|
try {
|
|
156
|
-
const payloadJson =
|
|
181
|
+
const payloadJson = canonicalize(attestation.payload);
|
|
157
182
|
const payloadBytes = new TextEncoder().encode(payloadJson);
|
|
158
183
|
const signatureBytes = Buffer.from(attestation.signature, "base64");
|
|
159
184
|
const publicKeyBytes = Buffer.from(publicKeyHex, "hex");
|
|
@@ -656,6 +681,7 @@ export {
|
|
|
656
681
|
canonicalBounds,
|
|
657
682
|
canonicalContext,
|
|
658
683
|
canonicalFrame,
|
|
684
|
+
canonicalize,
|
|
659
685
|
checkAttestationExpiry,
|
|
660
686
|
clearProfiles,
|
|
661
687
|
computeBoundsHash,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanagencyp/hap-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Core types, cryptographic primitives, and verification logic for the Human Agency Protocol",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -21,22 +21,23 @@
|
|
|
21
21
|
"dist",
|
|
22
22
|
"src"
|
|
23
23
|
],
|
|
24
|
-
"dependencies": {
|
|
25
|
-
"@noble/ed25519": "^2.1.0"
|
|
26
|
-
},
|
|
27
|
-
"devDependencies": {
|
|
28
|
-
"@types/node": "^20.10.0",
|
|
29
|
-
"tsup": "^8.0.0",
|
|
30
|
-
"typescript": "^5.3.0",
|
|
31
|
-
"vitest": "^1.0.0"
|
|
32
|
-
},
|
|
33
24
|
"scripts": {
|
|
34
25
|
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
35
26
|
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
36
27
|
"test": "vitest run",
|
|
37
28
|
"test:watch": "vitest",
|
|
29
|
+
"prepublishOnly": "pnpm build",
|
|
38
30
|
"release:patch": "pnpm version patch && git push --follow-tags",
|
|
39
31
|
"release:minor": "pnpm version minor && git push --follow-tags",
|
|
40
32
|
"release:major": "pnpm version major && git push --follow-tags"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@noble/ed25519": "^2.1.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^20.10.0",
|
|
39
|
+
"tsup": "^8.0.0",
|
|
40
|
+
"typescript": "^5.3.0",
|
|
41
|
+
"vitest": "^1.0.0"
|
|
41
42
|
}
|
|
42
|
-
}
|
|
43
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { createHash } from 'crypto';
|
|
8
8
|
import * as ed from '@noble/ed25519';
|
|
9
9
|
import type { Attestation, AttestationPayload } from './types';
|
|
10
|
+
import { canonicalize } from './canonicalize';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Decodes a base64url-encoded attestation blob.
|
|
@@ -50,7 +51,9 @@ export async function verifyAttestationSignature(
|
|
|
50
51
|
publicKeyHex: string
|
|
51
52
|
): Promise<void> {
|
|
52
53
|
try {
|
|
53
|
-
|
|
54
|
+
// JCS-canonical bytes (RFC 8785) — must match the canonicalization the
|
|
55
|
+
// signer used. The AS signs the same canonical form (see keys.ts).
|
|
56
|
+
const payloadJson = canonicalize(attestation.payload);
|
|
54
57
|
const payloadBytes = new TextEncoder().encode(payloadJson);
|
|
55
58
|
const signatureBytes = Buffer.from(attestation.signature, 'base64');
|
|
56
59
|
const publicKeyBytes = Buffer.from(publicKeyHex, 'hex');
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JCS — JSON Canonicalization (RFC 8785) for signing.
|
|
3
|
+
*
|
|
4
|
+
* Both attestation and receipt signatures are computed over a canonical byte
|
|
5
|
+
* serialization of the payload, so that two independent implementations (an AS,
|
|
6
|
+
* a gateway, an external verifier — in any language) agree on exactly which
|
|
7
|
+
* bytes were signed regardless of how they happened to construct the object.
|
|
8
|
+
*
|
|
9
|
+
* `JSON.stringify` is NOT sufficient: it preserves key *insertion* order, so the
|
|
10
|
+
* same logical payload built with keys in a different order serializes to
|
|
11
|
+
* different bytes and the signature fails to verify. Canonicalization removes
|
|
12
|
+
* that dependency by sorting keys.
|
|
13
|
+
*
|
|
14
|
+
* This is RFC 8785-compatible and matches the v0.5 spec's normative signing
|
|
15
|
+
* canonicalization (core.md "Signing Canonicalization"):
|
|
16
|
+
* 1. UTF-8.
|
|
17
|
+
* 2. Object keys sorted (RFC 8785 sorts by UTF-16 code units — exactly what
|
|
18
|
+
* JavaScript's default String comparison does, so `Object.keys().sort()` is
|
|
19
|
+
* correct here).
|
|
20
|
+
* 3. No insignificant whitespace.
|
|
21
|
+
* 4. Numbers in the shortest round-trippable form — the ECMAScript
|
|
22
|
+
* Number-to-String algorithm, which `JSON.stringify(number)` produces and
|
|
23
|
+
* RFC 8785 adopts verbatim.
|
|
24
|
+
* 5. Strings escaped per RFC 8259; non-ASCII passed through as UTF-8 (this is
|
|
25
|
+
* `JSON.stringify`'s behaviour for strings).
|
|
26
|
+
* 6. Array order preserved.
|
|
27
|
+
*
|
|
28
|
+
* Because it relies only on `JSON.stringify` for leaves plus `Object.keys`,
|
|
29
|
+
* `Array`, and `String` sort — all of which are environment-independent in
|
|
30
|
+
* JavaScript — Node and the browser produce byte-identical output. The
|
|
31
|
+
* conformance test pins a (payload → canonical bytes) vector that any other
|
|
32
|
+
* implementation can check against.
|
|
33
|
+
*/
|
|
34
|
+
export function canonicalize(value: unknown): string {
|
|
35
|
+
if (value === undefined) {
|
|
36
|
+
throw new Error('canonicalize: undefined is not serializable');
|
|
37
|
+
}
|
|
38
|
+
if (typeof value === 'number' && !Number.isFinite(value)) {
|
|
39
|
+
throw new Error(`canonicalize: ${value} is not a valid JSON number`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Primitives: string, finite number, boolean, null — JSON.stringify is already
|
|
43
|
+
// canonical (RFC 8259 string escaping, ECMAScript number formatting).
|
|
44
|
+
if (value === null || typeof value !== 'object') {
|
|
45
|
+
return JSON.stringify(value);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (Array.isArray(value)) {
|
|
49
|
+
// Array order is preserved; undefined elements become null (matching
|
|
50
|
+
// JSON.stringify), which keeps array length stable across serializers.
|
|
51
|
+
const items = value.map((el) => (el === undefined ? 'null' : canonicalize(el)));
|
|
52
|
+
return '[' + items.join(',') + ']';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Plain object: sort keys, omit undefined-valued properties (as JSON.stringify
|
|
56
|
+
// does), recurse on values.
|
|
57
|
+
const obj = value as Record<string, unknown>;
|
|
58
|
+
const parts: string[] = [];
|
|
59
|
+
for (const key of Object.keys(obj).sort()) {
|
|
60
|
+
const v = obj[key];
|
|
61
|
+
if (v === undefined) continue;
|
|
62
|
+
parts.push(JSON.stringify(key) + ':' + canonicalize(v));
|
|
63
|
+
}
|
|
64
|
+
return '{' + parts.join(',') + '}';
|
|
65
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suveren/gateway",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Suveren gateway — local agent gateway built in compliance with the Human Agency Protocol (HAP). Runs the UI, control plane, and MCP server in one Node process.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@hpke/core": "^1.9.0",
|
|
27
27
|
"express": "^4.18.0",
|
|
28
28
|
"http-proxy-middleware": "^3.0.0",
|
|
29
|
-
"@hap/core": "npm:@humanagencyp/hap-core@^0.
|
|
29
|
+
"@hap/core": "npm:@humanagencyp/hap-core@^0.5.0",
|
|
30
30
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
31
31
|
"zod": "^3.22.0"
|
|
32
32
|
},
|