@cotal-ai/core 0.11.6 → 0.13.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/dist/acls.d.ts +31 -7
- package/dist/acls.d.ts.map +1 -1
- package/dist/acls.js +105 -17
- package/dist/acls.js.map +1 -1
- package/dist/agent-file.js +1 -1
- package/dist/agent-file.js.map +1 -1
- package/dist/auth-provider.d.ts +90 -10
- package/dist/auth-provider.d.ts.map +1 -1
- package/dist/auth-provider.js.map +1 -1
- package/dist/backup-config.d.ts +55 -0
- package/dist/backup-config.d.ts.map +1 -0
- package/dist/backup-config.js +243 -0
- package/dist/backup-config.js.map +1 -0
- package/dist/backup.d.ts +86 -0
- package/dist/backup.d.ts.map +1 -0
- package/dist/backup.js +497 -0
- package/dist/backup.js.map +1 -0
- package/dist/broker-floor.d.ts +34 -0
- package/dist/broker-floor.d.ts.map +1 -0
- package/dist/broker-floor.js +39 -0
- package/dist/broker-floor.js.map +1 -0
- package/dist/canonical.d.ts +24 -0
- package/dist/canonical.d.ts.map +1 -0
- package/dist/canonical.js +159 -0
- package/dist/canonical.js.map +1 -0
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +8 -1
- package/dist/channels.js.map +1 -1
- package/dist/connector.d.ts +4 -0
- package/dist/connector.d.ts.map +1 -1
- package/dist/endpoint-action.d.ts +424 -0
- package/dist/endpoint-action.d.ts.map +1 -0
- package/dist/endpoint-action.js +910 -0
- package/dist/endpoint-action.js.map +1 -0
- package/dist/endpoint-binding.d.ts +344 -0
- package/dist/endpoint-binding.d.ts.map +1 -0
- package/dist/endpoint-binding.js +753 -0
- package/dist/endpoint-binding.js.map +1 -0
- package/dist/endpoint-checkpoint.d.ts +292 -0
- package/dist/endpoint-checkpoint.d.ts.map +1 -0
- package/dist/endpoint-checkpoint.js +818 -0
- package/dist/endpoint-checkpoint.js.map +1 -0
- package/dist/endpoint-cluster.d.ts +95 -0
- package/dist/endpoint-cluster.d.ts.map +1 -0
- package/dist/endpoint-cluster.js +194 -0
- package/dist/endpoint-cluster.js.map +1 -0
- package/dist/endpoint-contract-store.d.ts +90 -0
- package/dist/endpoint-contract-store.d.ts.map +1 -0
- package/dist/endpoint-contract-store.js +348 -0
- package/dist/endpoint-contract-store.js.map +1 -0
- package/dist/endpoint-envelope.d.ts +165 -0
- package/dist/endpoint-envelope.d.ts.map +1 -0
- package/dist/endpoint-envelope.js +346 -0
- package/dist/endpoint-envelope.js.map +1 -0
- package/dist/endpoint-grants.d.ts +123 -0
- package/dist/endpoint-grants.d.ts.map +1 -0
- package/dist/endpoint-grants.js +215 -0
- package/dist/endpoint-grants.js.map +1 -0
- package/dist/endpoint-guard.d.ts +222 -0
- package/dist/endpoint-guard.d.ts.map +1 -0
- package/dist/endpoint-guard.js +468 -0
- package/dist/endpoint-guard.js.map +1 -0
- package/dist/endpoint-handle.d.ts +176 -0
- package/dist/endpoint-handle.d.ts.map +1 -0
- package/dist/endpoint-handle.js +608 -0
- package/dist/endpoint-handle.js.map +1 -0
- package/dist/endpoint-journal.d.ts +225 -0
- package/dist/endpoint-journal.d.ts.map +1 -0
- package/dist/endpoint-journal.js +431 -0
- package/dist/endpoint-journal.js.map +1 -0
- package/dist/endpoint-receipt.d.ts +225 -0
- package/dist/endpoint-receipt.d.ts.map +1 -0
- package/dist/endpoint-receipt.js +417 -0
- package/dist/endpoint-receipt.js.map +1 -0
- package/dist/endpoint-records.d.ts +243 -0
- package/dist/endpoint-records.d.ts.map +1 -0
- package/dist/endpoint-records.js +591 -0
- package/dist/endpoint-records.js.map +1 -0
- package/dist/endpoint-serve.d.ts +176 -0
- package/dist/endpoint-serve.d.ts.map +1 -0
- package/dist/endpoint-serve.js +446 -0
- package/dist/endpoint-serve.js.map +1 -0
- package/dist/endpoint-service.d.ts +518 -0
- package/dist/endpoint-service.d.ts.map +1 -0
- package/dist/endpoint-service.js +973 -0
- package/dist/endpoint-service.js.map +1 -0
- package/dist/endpoint-session.d.ts +398 -0
- package/dist/endpoint-session.d.ts.map +1 -0
- package/dist/endpoint-session.js +834 -0
- package/dist/endpoint-session.js.map +1 -0
- package/dist/endpoint-signing.d.ts +105 -0
- package/dist/endpoint-signing.d.ts.map +1 -0
- package/dist/endpoint-signing.js +190 -0
- package/dist/endpoint-signing.js.map +1 -0
- package/dist/endpoint-subjects.d.ts +222 -0
- package/dist/endpoint-subjects.d.ts.map +1 -0
- package/dist/endpoint-subjects.js +381 -0
- package/dist/endpoint-subjects.js.map +1 -0
- package/dist/endpoint-supervisor.d.ts +19 -0
- package/dist/endpoint-supervisor.d.ts.map +1 -0
- package/dist/endpoint-supervisor.js +12 -0
- package/dist/endpoint-supervisor.js.map +1 -0
- package/dist/endpoint-traits.d.ts +225 -0
- package/dist/endpoint-traits.d.ts.map +1 -0
- package/dist/endpoint-traits.js +469 -0
- package/dist/endpoint-traits.js.map +1 -0
- package/dist/endpoint-verbs.d.ts +277 -0
- package/dist/endpoint-verbs.d.ts.map +1 -0
- package/dist/endpoint-verbs.js +576 -0
- package/dist/endpoint-verbs.js.map +1 -0
- package/dist/endpoint-virtual.d.ts +308 -0
- package/dist/endpoint-virtual.d.ts.map +1 -0
- package/dist/endpoint-virtual.js +440 -0
- package/dist/endpoint-virtual.js.map +1 -0
- package/dist/endpoint-work.d.ts +304 -0
- package/dist/endpoint-work.d.ts.map +1 -0
- package/dist/endpoint-work.js +728 -0
- package/dist/endpoint-work.js.map +1 -0
- package/dist/endpoint.d.ts +66 -22
- package/dist/endpoint.d.ts.map +1 -1
- package/dist/endpoint.js +289 -89
- package/dist/endpoint.js.map +1 -1
- package/dist/evict.d.ts +95 -32
- package/dist/evict.d.ts.map +1 -1
- package/dist/evict.js +193 -3
- package/dist/evict.js.map +1 -1
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -1
- package/dist/lease.d.ts +4 -0
- package/dist/lease.d.ts.map +1 -1
- package/dist/lease.js.map +1 -1
- package/dist/members.d.ts +4 -4
- package/dist/members.d.ts.map +1 -1
- package/dist/members.js +12 -12
- package/dist/members.js.map +1 -1
- package/dist/provision.d.ts +78 -20
- package/dist/provision.d.ts.map +1 -1
- package/dist/provision.js +214 -37
- package/dist/provision.js.map +1 -1
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/safe-pattern.d.ts +33 -0
- package/dist/safe-pattern.d.ts.map +1 -0
- package/dist/safe-pattern.js +486 -0
- package/dist/safe-pattern.js.map +1 -0
- package/dist/schema-profile.d.ts +93 -0
- package/dist/schema-profile.d.ts.map +1 -0
- package/dist/schema-profile.js +284 -0
- package/dist/schema-profile.js.map +1 -0
- package/dist/secret-store.d.ts +46 -0
- package/dist/secret-store.d.ts.map +1 -0
- package/dist/secret-store.js +2 -0
- package/dist/secret-store.js.map +1 -0
- package/dist/space-auth.d.ts +9 -0
- package/dist/space-auth.d.ts.map +1 -0
- package/dist/space-auth.js +126 -0
- package/dist/space-auth.js.map +1 -0
- package/dist/streams.d.ts +14 -10
- package/dist/streams.d.ts.map +1 -1
- package/dist/streams.js +47 -76
- package/dist/streams.js.map +1 -1
- package/dist/subjects.d.ts +100 -40
- package/dist/subjects.d.ts.map +1 -1
- package/dist/subjects.js +136 -60
- package/dist/subjects.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -2
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The normative JSON Schema 2020-12 execution profile for contract schemas (SPEC §13.7, D27).
|
|
3
|
+
*
|
|
4
|
+
* A contract schema is a CLOSED resource bundle: fully self-contained (local `#/…` refs) or
|
|
5
|
+
* referencing other contract-store artifacts by digest (`cotal:sha256:<hex>`) only. Ambient
|
|
6
|
+
* HTTP/file/URI resolution never happens; every external reference must be supplied by the
|
|
7
|
+
* caller as an already-fetched, digest-verified bundle member. Registration-time violations are
|
|
8
|
+
* `contract-invalid` (distinct from invocation-time `bad-request`), and all bounds are enforced
|
|
9
|
+
* BEFORE compilation so a hostile descriptor cannot turn registration into a DoS.
|
|
10
|
+
*/
|
|
11
|
+
import { type ValidateFunction } from "ajv/dist/2020.js";
|
|
12
|
+
/** Registration-time bounds (SPEC §13.7/§13.8). Fixed by the profile, not caller-tunable, and
|
|
13
|
+
* RUNTIME-frozen (the afa715b class): a post-import `maxDepth = MAX_SAFE_INTEGER` would remove
|
|
14
|
+
* the pre-compilation recursion/DoS ceiling, so mutation throws instead. */
|
|
15
|
+
export declare const SCHEMA_PROFILE: Readonly<{
|
|
16
|
+
/** One schema document's canonical form, bytes. */
|
|
17
|
+
readonly maxDocumentBytes: number;
|
|
18
|
+
/** The complete resolved closure (root + digest-referenced members), bytes. */
|
|
19
|
+
readonly maxClosureBytes: number;
|
|
20
|
+
/** Structural nesting depth of any document. */
|
|
21
|
+
readonly maxDepth: 32;
|
|
22
|
+
/** Digest-reference chain depth (root → member → member …). */
|
|
23
|
+
readonly maxRefChain: 32;
|
|
24
|
+
/** Compile budget per closure, ms. */
|
|
25
|
+
readonly compileBudgetMs: 100;
|
|
26
|
+
/** Bounded pattern complexity: max characters of any `pattern` / `patternProperties` regex. */
|
|
27
|
+
readonly maxPatternChars: 256;
|
|
28
|
+
/** Per-value validation budget at the serving boundary, ms (§13.8 reference; post-hoc,
|
|
29
|
+
* fail-loud as `bad-request`). */
|
|
30
|
+
readonly validateBudgetMs: 10;
|
|
31
|
+
/** Compiled-schema cache entries (the SPEC's reference 256-entry LRU). */
|
|
32
|
+
readonly compiledCacheEntries: 256;
|
|
33
|
+
}>;
|
|
34
|
+
/** The canonical void schema (§13.7): the one artifact a side with no payload declares, so both
|
|
35
|
+
* `op` digests exist for every command. Validation against it means the payload is absent or
|
|
36
|
+
* `null`. */
|
|
37
|
+
export declare const VOID_SCHEMA: Readonly<{
|
|
38
|
+
readonly type: "null";
|
|
39
|
+
}>;
|
|
40
|
+
/** Artifact digest of the void schema document — one fixed value by construction. */
|
|
41
|
+
export declare const VOID_SCHEMA_ARTIFACT_DIGEST: string;
|
|
42
|
+
/** The void schema's CLOSURE digest (the §13.7 manifest of a self-contained document) — the
|
|
43
|
+
* value `op.inputDigest`/`op.outputDigest` carry for a payload-free side. */
|
|
44
|
+
export declare const VOID_SCHEMA_DIGEST: string;
|
|
45
|
+
/** Thrown for every profile violation at registration/ingest time; maps to the wire error
|
|
46
|
+
* code `contract-invalid` (never `bad-request`, which is for invocation-time args). */
|
|
47
|
+
export declare class ContractInvalidError extends Error {
|
|
48
|
+
readonly code: "contract-invalid";
|
|
49
|
+
constructor(message: string);
|
|
50
|
+
}
|
|
51
|
+
/** A closed schema bundle: the root document plus every digest-referenced member, already
|
|
52
|
+
* fetched and digest-verified (see `verifyArtifact`). Keyed by `sha256:<hex>`. */
|
|
53
|
+
export interface SchemaBundle {
|
|
54
|
+
root: unknown;
|
|
55
|
+
/** Digest → verified member document. Every `cotal:` ref in the closure must resolve here. */
|
|
56
|
+
members?: Record<string, unknown>;
|
|
57
|
+
}
|
|
58
|
+
/** A registered contract schema: the compiled validator plus the bundle's CLOSURE digest —
|
|
59
|
+
* the artifact digest of the §13.7 manifest `{ v: 1, root, members[] }` (members = every
|
|
60
|
+
* artifact transitively REACHABLE from the root, sorted and deduplicated). The closure digest
|
|
61
|
+
* is the contract identity `op.inputDigest`/`op.outputDigest` pin.
|
|
62
|
+
*
|
|
63
|
+
* The interface is structural for CONSUMERS, but authority seams never accept structure: a
|
|
64
|
+
* compiled contract is FROZEN and brand-registered by this compiler ({@link compileContract}),
|
|
65
|
+
* and {@link assertCompiledContract} refuses any object the profile compiler did not produce —
|
|
66
|
+
* so a hand-built `{validate, closureDigest}` pair (an arbitrary validator wearing a
|
|
67
|
+
* registered digest) can never enter a serve table (§13.7: the digest and its enforcing
|
|
68
|
+
* validator are one value). */
|
|
69
|
+
export interface CompiledContract {
|
|
70
|
+
validate: ValidateFunction;
|
|
71
|
+
closureDigest: string;
|
|
72
|
+
}
|
|
73
|
+
/** True iff `c` is a frozen contract this profile compiler produced (never structural). */
|
|
74
|
+
export declare function isCompiledContract(c: unknown): c is CompiledContract;
|
|
75
|
+
/** Refuse anything that is not a compiler-produced contract: authority seams (the serve table,
|
|
76
|
+
* the grant mint) call this so a fabricated validator/digest pair fails loud, never serves. */
|
|
77
|
+
export declare function assertCompiledContract(c: unknown, what: string): CompiledContract;
|
|
78
|
+
/** Validate the whole closure against the profile and compile it with a real 2020-12
|
|
79
|
+
* validator. No network, no filesystem: `loadSchema` is never installed, and every
|
|
80
|
+
* `cotal:` reference must be present in `bundle.members`. */
|
|
81
|
+
export declare function compileContract(bundle: SchemaBundle): CompiledContract;
|
|
82
|
+
/** {@link compileContract} without the closure identity, kept for validation-only callers. */
|
|
83
|
+
export declare function compileContractSchema(bundle: SchemaBundle): ValidateFunction;
|
|
84
|
+
/** A bounded compiled-schema LRU (SPEC §13.7's reference 256-entry cache), keyed by CLOSURE
|
|
85
|
+
* digest. The profile walk (incl. member digest verification) runs on EVERY call — only the
|
|
86
|
+
* ajv compilation is skipped on a hit. Sound across callers: two bundles with one closure
|
|
87
|
+
* digest are byte-identical closures, so a hit can never serve another caller's
|
|
88
|
+
* mis-assembled bundle. */
|
|
89
|
+
export declare function createCompiledContractCache(capacity?: number): {
|
|
90
|
+
compile: (bundle: SchemaBundle) => CompiledContract;
|
|
91
|
+
size: () => number;
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=schema-profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-profile.d.ts","sourceRoot":"","sources":["../src/schema-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAW,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAIlE;;6EAE6E;AAC7E,eAAO,MAAM,cAAc;IACzB,mDAAmD;;IAEnD,+EAA+E;;IAE/E,gDAAgD;;IAEhD,+DAA+D;;IAE/D,sCAAsC;;IAEtC,+FAA+F;;IAE/F;uCACmC;;IAEnC,0EAA0E;;EAEjE,CAAC;AAEZ;;cAEc;AACd,eAAO,MAAM,WAAW;;EAA2C,CAAC;AACpE,qFAAqF;AACrF,eAAO,MAAM,2BAA2B,QAA8B,CAAC;AACvE;8EAC8E;AAC9E,eAAO,MAAM,kBAAkB,QAA2E,CAAC;AAK3G;wFACwF;AACxF,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,EAAG,kBAAkB,CAAU;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAqED;mFACmF;AACnF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAqBD;;;;;;;;;;gCAUgC;AAChC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;CACvB;AAaD,2FAA2F;AAC3F,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,gBAAgB,CAEpE;AAED;gGACgG;AAChG,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAIjF;AA4ED;;8DAE8D;AAC9D,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB,CAGtE;AAED,8FAA8F;AAC9F,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB,CAE5E;AAED;;;;4BAI4B;AAC5B,wBAAgB,2BAA2B,CAAC,QAAQ,GAAE,MAA4C,GAAG;IACnG,OAAO,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,gBAAgB,CAAC;IACpD,IAAI,EAAE,MAAM,MAAM,CAAC;CACpB,CAmBA"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The normative JSON Schema 2020-12 execution profile for contract schemas (SPEC §13.7, D27).
|
|
3
|
+
*
|
|
4
|
+
* A contract schema is a CLOSED resource bundle: fully self-contained (local `#/…` refs) or
|
|
5
|
+
* referencing other contract-store artifacts by digest (`cotal:sha256:<hex>`) only. Ambient
|
|
6
|
+
* HTTP/file/URI resolution never happens; every external reference must be supplied by the
|
|
7
|
+
* caller as an already-fetched, digest-verified bundle member. Registration-time violations are
|
|
8
|
+
* `contract-invalid` (distinct from invocation-time `bad-request`), and all bounds are enforced
|
|
9
|
+
* BEFORE compilation so a hostile descriptor cannot turn registration into a DoS.
|
|
10
|
+
*/
|
|
11
|
+
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
12
|
+
import { canonicalJson, contractDigest, isContractDigest } from "./canonical.js";
|
|
13
|
+
import { assertSafePattern } from "./safe-pattern.js";
|
|
14
|
+
/** Registration-time bounds (SPEC §13.7/§13.8). Fixed by the profile, not caller-tunable, and
|
|
15
|
+
* RUNTIME-frozen (the afa715b class): a post-import `maxDepth = MAX_SAFE_INTEGER` would remove
|
|
16
|
+
* the pre-compilation recursion/DoS ceiling, so mutation throws instead. */
|
|
17
|
+
export const SCHEMA_PROFILE = Object.freeze({
|
|
18
|
+
/** One schema document's canonical form, bytes. */
|
|
19
|
+
maxDocumentBytes: 256 * 1024,
|
|
20
|
+
/** The complete resolved closure (root + digest-referenced members), bytes. */
|
|
21
|
+
maxClosureBytes: 1024 * 1024,
|
|
22
|
+
/** Structural nesting depth of any document. */
|
|
23
|
+
maxDepth: 32,
|
|
24
|
+
/** Digest-reference chain depth (root → member → member …). */
|
|
25
|
+
maxRefChain: 32,
|
|
26
|
+
/** Compile budget per closure, ms. */
|
|
27
|
+
compileBudgetMs: 100,
|
|
28
|
+
/** Bounded pattern complexity: max characters of any `pattern` / `patternProperties` regex. */
|
|
29
|
+
maxPatternChars: 256,
|
|
30
|
+
/** Per-value validation budget at the serving boundary, ms (§13.8 reference; post-hoc,
|
|
31
|
+
* fail-loud as `bad-request`). */
|
|
32
|
+
validateBudgetMs: 10,
|
|
33
|
+
/** Compiled-schema cache entries (the SPEC's reference 256-entry LRU). */
|
|
34
|
+
compiledCacheEntries: 256,
|
|
35
|
+
});
|
|
36
|
+
/** The canonical void schema (§13.7): the one artifact a side with no payload declares, so both
|
|
37
|
+
* `op` digests exist for every command. Validation against it means the payload is absent or
|
|
38
|
+
* `null`. */
|
|
39
|
+
export const VOID_SCHEMA = Object.freeze({ type: "null" });
|
|
40
|
+
/** Artifact digest of the void schema document — one fixed value by construction. */
|
|
41
|
+
export const VOID_SCHEMA_ARTIFACT_DIGEST = contractDigest(VOID_SCHEMA);
|
|
42
|
+
/** The void schema's CLOSURE digest (the §13.7 manifest of a self-contained document) — the
|
|
43
|
+
* value `op.inputDigest`/`op.outputDigest` carry for a payload-free side. */
|
|
44
|
+
export const VOID_SCHEMA_DIGEST = contractDigest({ v: 1, root: VOID_SCHEMA_ARTIFACT_DIGEST, members: [] });
|
|
45
|
+
/** The digest-pinned contract-store reference scheme: `cotal:sha256:<hex>[#/json/pointer]`. */
|
|
46
|
+
const STORE_REF = /^cotal:(sha256:[0-9a-f]{64})(#.*)?$/;
|
|
47
|
+
/** Thrown for every profile violation at registration/ingest time; maps to the wire error
|
|
48
|
+
* code `contract-invalid` (never `bad-request`, which is for invocation-time args). */
|
|
49
|
+
export class ContractInvalidError extends Error {
|
|
50
|
+
code = "contract-invalid";
|
|
51
|
+
constructor(message) {
|
|
52
|
+
super(message);
|
|
53
|
+
this.name = "ContractInvalidError";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Canonicalize within the profile boundary: an I-JSON violation (lone surrogate, undefined,
|
|
57
|
+
* non-finite number) in a schema document is a PROFILE violation and must surface as
|
|
58
|
+
* `contract-invalid`, never as a raw canonicalizer error (§13.7). */
|
|
59
|
+
function canonicalOrInvalid(v, label) {
|
|
60
|
+
try {
|
|
61
|
+
return canonicalJson(v);
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
throw new ContractInvalidError(`${label}: ${e.message}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function digestOrInvalid(v, label) {
|
|
68
|
+
try {
|
|
69
|
+
return contractDigest(v);
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
throw new ContractInvalidError(`${label}: ${e.message}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function structuralDepth(v, depth = 0) {
|
|
76
|
+
if (depth > SCHEMA_PROFILE.maxDepth)
|
|
77
|
+
return depth; // short-circuit, caller throws
|
|
78
|
+
if (v === null || typeof v !== "object")
|
|
79
|
+
return depth;
|
|
80
|
+
let max = depth;
|
|
81
|
+
const children = Array.isArray(v) ? v : Object.values(v);
|
|
82
|
+
for (const c of children) {
|
|
83
|
+
const d = structuralDepth(c, depth + 1);
|
|
84
|
+
if (d > max)
|
|
85
|
+
max = d;
|
|
86
|
+
if (max > SCHEMA_PROFILE.maxDepth)
|
|
87
|
+
return max;
|
|
88
|
+
}
|
|
89
|
+
return max;
|
|
90
|
+
}
|
|
91
|
+
/** The bounded-pattern-complexity gate (§13.7/§13.8 "bounded pattern complexity", "bounded
|
|
92
|
+
* regex"): the {@link assertSafePattern} SAFE SUBSET — a length bound plus an admission
|
|
93
|
+
* analysis that refuses nested repetition, repeated nullable or ambiguous-alternation bodies,
|
|
94
|
+
* overlapping variable repetitions in sequence, backreferences, lookarounds, and anything it
|
|
95
|
+
* cannot parse. Conservative by construction: uncertainty refuses. A refused pattern is
|
|
96
|
+
* rewritten by its author at registration time (`contract-invalid`), never probed at
|
|
97
|
+
* validation time — the post-hoc validate budget can only classify a stall, not prevent it. */
|
|
98
|
+
function assertBoundedPattern(p, label, where) {
|
|
99
|
+
try {
|
|
100
|
+
assertSafePattern(p, SCHEMA_PROFILE.maxPatternChars);
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
throw new ContractInvalidError(`${label}: ${where} refused: ${e.message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** Collect every `$ref` string in a schema document and gate its regex patterns (structural
|
|
107
|
+
* walk; `$ref` in 2020-12 is always a string-valued keyword wherever it appears; `pattern`
|
|
108
|
+
* values and `patternProperties` keys are the profile's bounded-pattern-complexity surface). */
|
|
109
|
+
function collectRefs(v, label, out = []) {
|
|
110
|
+
if (v === null || typeof v !== "object")
|
|
111
|
+
return out;
|
|
112
|
+
if (Array.isArray(v)) {
|
|
113
|
+
for (const c of v)
|
|
114
|
+
collectRefs(c, label, out);
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
for (const [k, c] of Object.entries(v)) {
|
|
118
|
+
if ((k === "$ref" || k === "$dynamicRef") && typeof c === "string")
|
|
119
|
+
out.push(c);
|
|
120
|
+
if (k === "pattern" && typeof c === "string")
|
|
121
|
+
assertBoundedPattern(c, label, "a pattern");
|
|
122
|
+
if (k === "patternProperties" && c !== null && typeof c === "object" && !Array.isArray(c)) {
|
|
123
|
+
for (const p of Object.keys(c))
|
|
124
|
+
assertBoundedPattern(p, label, "a patternProperties key");
|
|
125
|
+
}
|
|
126
|
+
collectRefs(c, label, out);
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
/** Enforce the D27 profile on one document: size, depth, and reference closure. Returns the
|
|
131
|
+
* digest-refs the document makes (for closure walking). */
|
|
132
|
+
function assertDocumentProfile(doc, label) {
|
|
133
|
+
const canonical = canonicalOrInvalid(doc, label); // also enforces I-JSON, as contract-invalid
|
|
134
|
+
const bytes = Buffer.byteLength(canonical, "utf8");
|
|
135
|
+
if (bytes > SCHEMA_PROFILE.maxDocumentBytes)
|
|
136
|
+
throw new ContractInvalidError(`${label}: document is ${bytes} bytes (profile max ${SCHEMA_PROFILE.maxDocumentBytes})`);
|
|
137
|
+
if (structuralDepth(doc) > SCHEMA_PROFILE.maxDepth)
|
|
138
|
+
throw new ContractInvalidError(`${label}: nesting exceeds profile depth ${SCHEMA_PROFILE.maxDepth}`);
|
|
139
|
+
const storeRefs = [];
|
|
140
|
+
for (const ref of collectRefs(doc, label)) {
|
|
141
|
+
if (ref.startsWith("#"))
|
|
142
|
+
continue; // local pointer/anchor — resolved within the document
|
|
143
|
+
const m = STORE_REF.exec(ref);
|
|
144
|
+
if (!m)
|
|
145
|
+
throw new ContractInvalidError(`${label}: external $ref ${JSON.stringify(ref)}; only local '#…' or digest-pinned 'cotal:sha256:<hex>' references are permitted (no ambient resolution)`);
|
|
146
|
+
storeRefs.push(m[1]);
|
|
147
|
+
}
|
|
148
|
+
return storeRefs;
|
|
149
|
+
}
|
|
150
|
+
/** Provenance brand: exactly the frozen objects this compiler returned. A WeakSet (not a
|
|
151
|
+
* field) so the brand is unforgeable and unserializable; the freeze makes field swaps on a
|
|
152
|
+
* branded object impossible, so brand membership proves BOTH fields came out of one compile. */
|
|
153
|
+
const COMPILED = new WeakSet();
|
|
154
|
+
function makeCompiled(validate, closureDigest) {
|
|
155
|
+
const compiled = Object.freeze({ validate, closureDigest });
|
|
156
|
+
COMPILED.add(compiled);
|
|
157
|
+
return compiled;
|
|
158
|
+
}
|
|
159
|
+
/** True iff `c` is a frozen contract this profile compiler produced (never structural). */
|
|
160
|
+
export function isCompiledContract(c) {
|
|
161
|
+
return typeof c === "object" && c !== null && COMPILED.has(c);
|
|
162
|
+
}
|
|
163
|
+
/** Refuse anything that is not a compiler-produced contract: authority seams (the serve table,
|
|
164
|
+
* the grant mint) call this so a fabricated validator/digest pair fails loud, never serves. */
|
|
165
|
+
export function assertCompiledContract(c, what) {
|
|
166
|
+
if (!isCompiledContract(c))
|
|
167
|
+
throw new ContractInvalidError(`${what} is not a profile-compiled contract (schema-profile compileContract); a structural {validate, closureDigest} pair carries no compile provenance and is refused (SPEC 13.7)`);
|
|
168
|
+
return c;
|
|
169
|
+
}
|
|
170
|
+
/** Enforce the profile on the whole closure WITHOUT compiling: bounds every document, walks the
|
|
171
|
+
* digest-reference graph breadth-first (chain depth + total size), VERIFIES every reachable
|
|
172
|
+
* member against its digest key (fail-loud `contract-invalid`, so a mis-assembled bundle can
|
|
173
|
+
* neither register nor poison the compiled cache), and returns the closure identity — the
|
|
174
|
+
* §13.7 manifest digest. */
|
|
175
|
+
function assertClosureProfile(bundle) {
|
|
176
|
+
const members = bundle.members ?? {};
|
|
177
|
+
for (const d of Object.keys(members)) {
|
|
178
|
+
if (!isContractDigest(d))
|
|
179
|
+
throw new ContractInvalidError(`bundle member key ${JSON.stringify(d)} is not a sha256 digest`);
|
|
180
|
+
}
|
|
181
|
+
const seen = new Set();
|
|
182
|
+
let closureBytes = 0;
|
|
183
|
+
let frontier = assertDocumentProfile(bundle.root, "root schema");
|
|
184
|
+
closureBytes += Buffer.byteLength(canonicalOrInvalid(bundle.root, "root schema"), "utf8");
|
|
185
|
+
for (let chain = 0; frontier.length > 0; chain++) {
|
|
186
|
+
if (chain >= SCHEMA_PROFILE.maxRefChain)
|
|
187
|
+
throw new ContractInvalidError(`reference chain exceeds profile depth ${SCHEMA_PROFILE.maxRefChain}`);
|
|
188
|
+
const next = [];
|
|
189
|
+
for (const digest of frontier) {
|
|
190
|
+
if (seen.has(digest))
|
|
191
|
+
continue;
|
|
192
|
+
seen.add(digest);
|
|
193
|
+
const member = members[digest];
|
|
194
|
+
if (member === undefined)
|
|
195
|
+
throw new ContractInvalidError(`unresolved digest reference ${digest}: not present in the bundle (schemas are closed; nothing is fetched)`);
|
|
196
|
+
if (digestOrInvalid(member, `member ${digest}`) !== digest)
|
|
197
|
+
throw new ContractInvalidError(`bundle member under ${digest} does not hash to its key (content is ${digestOrInvalid(member, `member ${digest}`)})`);
|
|
198
|
+
closureBytes += Buffer.byteLength(canonicalOrInvalid(member, `member ${digest}`), "utf8");
|
|
199
|
+
if (closureBytes > SCHEMA_PROFILE.maxClosureBytes)
|
|
200
|
+
throw new ContractInvalidError(`closure is ${closureBytes} bytes (profile max ${SCHEMA_PROFILE.maxClosureBytes})`);
|
|
201
|
+
next.push(...assertDocumentProfile(member, `member ${digest}`));
|
|
202
|
+
}
|
|
203
|
+
frontier = next;
|
|
204
|
+
}
|
|
205
|
+
// A closed bundle lists EXACTLY the closure: a member unreachable from the root escaped every
|
|
206
|
+
// bound and digest check above and never enters the closure identity, so accepting it would
|
|
207
|
+
// make registration history-dependent (cold compile vs cache hit) and hand unverified,
|
|
208
|
+
// unbounded input to the compiler. Refused deterministically, before hit and miss alike.
|
|
209
|
+
const extras = Object.keys(members).filter((d) => !seen.has(d));
|
|
210
|
+
if (extras.length > 0)
|
|
211
|
+
throw new ContractInvalidError(`bundle carries ${extras.length} member(s) unreachable from the root (${extras.slice(0, 3).join(", ")}${extras.length > 3 ? ", …" : ""})`);
|
|
212
|
+
return contractDigest({ v: 1, root: digestOrInvalid(bundle.root, "root schema"), members: [...seen].sort() });
|
|
213
|
+
}
|
|
214
|
+
function compileWithinBudget(bundle) {
|
|
215
|
+
const started = Date.now();
|
|
216
|
+
// Compile with deterministic local resolution only. Members register under their cotal: URI
|
|
217
|
+
// so in-document `$ref: "cotal:sha256:…"` resolves from the bundle, never the network.
|
|
218
|
+
const ajv = new Ajv2020({
|
|
219
|
+
strict: false, // the wire accepts full 2020-12, not ajv's strict-mode dialect subset
|
|
220
|
+
allErrors: false,
|
|
221
|
+
validateFormats: false,
|
|
222
|
+
loadSchema: undefined,
|
|
223
|
+
// PINNED, never inherited from Ajv's default: the safe-pattern analyzer models `/u`
|
|
224
|
+
// semantics exactly (astral atoms, surrogate refusals), so the engine MUST compile
|
|
225
|
+
// `pattern` with the `u` flag. If this ever flipped, patterns would be proven under one
|
|
226
|
+
// grammar and executed under another (an admitting under-approximation).
|
|
227
|
+
unicodeRegExp: true,
|
|
228
|
+
});
|
|
229
|
+
let validate;
|
|
230
|
+
try {
|
|
231
|
+
// Every member is reachable and digest-verified (assertClosureProfile refuses extras), so
|
|
232
|
+
// exactly the closure registers. A registration failure (e.g. a `$id` collision between
|
|
233
|
+
// members) is a profile rejection, same as a compile failure.
|
|
234
|
+
for (const [digest, member] of Object.entries(bundle.members ?? {}))
|
|
235
|
+
ajv.addSchema(member, `cotal:${digest}`);
|
|
236
|
+
validate = ajv.compile(bundle.root);
|
|
237
|
+
}
|
|
238
|
+
catch (e) {
|
|
239
|
+
throw new ContractInvalidError(`schema does not compile under the 2020-12 profile: ${e.message}`);
|
|
240
|
+
}
|
|
241
|
+
const elapsed = Date.now() - started;
|
|
242
|
+
if (elapsed > SCHEMA_PROFILE.compileBudgetMs)
|
|
243
|
+
throw new ContractInvalidError(`compile took ${elapsed}ms (profile budget ${SCHEMA_PROFILE.compileBudgetMs}ms)`);
|
|
244
|
+
return validate;
|
|
245
|
+
}
|
|
246
|
+
/** Validate the whole closure against the profile and compile it with a real 2020-12
|
|
247
|
+
* validator. No network, no filesystem: `loadSchema` is never installed, and every
|
|
248
|
+
* `cotal:` reference must be present in `bundle.members`. */
|
|
249
|
+
export function compileContract(bundle) {
|
|
250
|
+
const closureDigest = assertClosureProfile(bundle);
|
|
251
|
+
return makeCompiled(compileWithinBudget(bundle), closureDigest);
|
|
252
|
+
}
|
|
253
|
+
/** {@link compileContract} without the closure identity, kept for validation-only callers. */
|
|
254
|
+
export function compileContractSchema(bundle) {
|
|
255
|
+
return compileContract(bundle).validate;
|
|
256
|
+
}
|
|
257
|
+
/** A bounded compiled-schema LRU (SPEC §13.7's reference 256-entry cache), keyed by CLOSURE
|
|
258
|
+
* digest. The profile walk (incl. member digest verification) runs on EVERY call — only the
|
|
259
|
+
* ajv compilation is skipped on a hit. Sound across callers: two bundles with one closure
|
|
260
|
+
* digest are byte-identical closures, so a hit can never serve another caller's
|
|
261
|
+
* mis-assembled bundle. */
|
|
262
|
+
export function createCompiledContractCache(capacity = SCHEMA_PROFILE.compiledCacheEntries) {
|
|
263
|
+
if (!Number.isSafeInteger(capacity) || capacity < 1)
|
|
264
|
+
throw new Error(`cache capacity ${capacity} is not a positive integer`);
|
|
265
|
+
const lru = new Map();
|
|
266
|
+
return {
|
|
267
|
+
compile(bundle) {
|
|
268
|
+
const closureDigest = assertClosureProfile(bundle);
|
|
269
|
+
const hit = lru.get(closureDigest);
|
|
270
|
+
if (hit) {
|
|
271
|
+
lru.delete(closureDigest); // refresh recency
|
|
272
|
+
lru.set(closureDigest, hit);
|
|
273
|
+
return hit;
|
|
274
|
+
}
|
|
275
|
+
const compiled = makeCompiled(compileWithinBudget(bundle), closureDigest);
|
|
276
|
+
lru.set(closureDigest, compiled);
|
|
277
|
+
if (lru.size > capacity)
|
|
278
|
+
lru.delete(lru.keys().next().value);
|
|
279
|
+
return compiled;
|
|
280
|
+
},
|
|
281
|
+
size: () => lru.size,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=schema-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-profile.js","sourceRoot":"","sources":["../src/schema-profile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The abstract seam for persisting secret material.
|
|
3
|
+
*
|
|
4
|
+
* Core owns ONLY this tiny value-store contract. The default filesystem adapter (`FsSecretStore`)
|
|
5
|
+
* lives in `@cotal-ai/workspace` because on-disk layout is a workstation concern, not the wire
|
|
6
|
+
* protocol. A closed hosted composition injects its own KMS/Vault-backed implementation at its
|
|
7
|
+
* composition root; nothing here knows it is being hosted.
|
|
8
|
+
*
|
|
9
|
+
* SCOPE — what routes through a `SecretStore`. Only DURABLE HOSTED SECRET BLOBS: the full persisted
|
|
10
|
+
* space trust bundle (operator + account seeds and the account signing seed; only the system-account
|
|
11
|
+
* seed is stripped before disk), the auth service's callout / issuer / owner-secret / service-key
|
|
12
|
+
* material, agent standing creds, and any daemon standing credential the hosted composition persists
|
|
13
|
+
* (renewable ones — delivery / membership-rw — are read via `get` on each refresh, never a one-shot
|
|
14
|
+
* snapshot; rotation-renewed observer / evictor creds are read at start or per use).
|
|
15
|
+
* Machine-local, transient, or non-secret artifacts are NOT `SecretStore` material and remain the
|
|
16
|
+
* local filesystem owner's responsibility: the mesh registry,
|
|
17
|
+
* the transient MCP config, launcher scripts, personas, auth-service discovery (pid/port), the IdP
|
|
18
|
+
* pin, `membership.json`, the login cache, and the actor ledger (authorization DB state, not an
|
|
19
|
+
* exportable blob). Every
|
|
20
|
+
* durable secret that routes through here must be READ through `get` too, or a hosted `put` is a
|
|
21
|
+
* silent no-op while a raw `readFileSync` stays authoritative.
|
|
22
|
+
*
|
|
23
|
+
* KEYS are OPAQUE logical strings built by the owning package. Core assigns them no taxonomy: a
|
|
24
|
+
* KMS key-id / policy / HSM-slot mapping is the closed adapter's concern. Typing the key could not
|
|
25
|
+
* enforce "signing-seed never leaves the HSM" anyway, because {@link SecretStore.get} necessarily
|
|
26
|
+
* exports the value; non-exportable signing, if ever needed, is a separate capability seam
|
|
27
|
+
* (sign / mint), not a richer key here. v1 accepts that signing material DOES enter process memory
|
|
28
|
+
* (e.g. `mintCreds`).
|
|
29
|
+
*/
|
|
30
|
+
export interface SecretStore {
|
|
31
|
+
/** The stored value for `key`, or `undefined` if absent. */
|
|
32
|
+
get(key: string): Promise<string | undefined>;
|
|
33
|
+
/** Store `value` under `key`, replacing any prior value AS A WHOLE: a concurrent `get` observes
|
|
34
|
+
* either the complete old value or the complete new one, never a partial or torn intermediate
|
|
35
|
+
* (FS adapter → atomic temp+rename; a managed backend → a conditional / whole-object put). This
|
|
36
|
+
* atomicity is part of the contract because standing daemon/agent creds are re-read live during
|
|
37
|
+
* renewal. Private perms / hardening beyond the atomicity guarantee remain the adapter's concern. */
|
|
38
|
+
put(key: string, value: string): Promise<void>;
|
|
39
|
+
/** Remove `key`. Idempotent. The ONLY portable contract is: after a successful `delete`,
|
|
40
|
+
* `get(key)` returns absent. This is NOT cryptographic shred, revocation, or credential kill —
|
|
41
|
+
* a backend may retain recoverable versions, and any already-issued NATS cred or signing seed
|
|
42
|
+
* stays valid until its credential lifetime ends or broker/key rotation invalidates it (those
|
|
43
|
+
* are mint / renewal / eviction concerns, not `SecretStore`). */
|
|
44
|
+
delete(key: string): Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=secret-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-store.d.ts","sourceRoot":"","sources":["../src/secret-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAE9C;;;;0GAIsG;IACtG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/C;;;;sEAIkE;IAClE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-store.js","sourceRoot":"","sources":["../src/secret-store.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SpaceAuth } from "./provision.js";
|
|
2
|
+
/**
|
|
3
|
+
* Validate an existing full space trust bundle before a restore or other state mutation.
|
|
4
|
+
*
|
|
5
|
+
* This is read-only: it never generates, rotates, or signs key material, and returns the same
|
|
6
|
+
* validated object. Persisted bundles may omit `sys.signingSeed`; when present, it is validated too.
|
|
7
|
+
*/
|
|
8
|
+
export declare function validateSpaceAuth(auth: unknown, expectedSpace?: string): SpaceAuth;
|
|
9
|
+
//# sourceMappingURL=space-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"space-auth.d.ts","sourceRoot":"","sources":["../src/space-auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AA4DhD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CA2DlF"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { decode, Types } from "@nats-io/jwt";
|
|
2
|
+
import { fromPublic, fromSeed } from "@nats-io/nkeys";
|
|
3
|
+
import { token } from "./subjects.js";
|
|
4
|
+
function record(value, field) {
|
|
5
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
6
|
+
throw new Error(`validateSpaceAuth: ${field} must be an object`);
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
function string(value, field) {
|
|
10
|
+
if (typeof value !== "string" || value.length === 0)
|
|
11
|
+
throw new Error(`validateSpaceAuth: ${field} must be a non-empty string`);
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
function publicKey(value, field, prefix) {
|
|
15
|
+
let pair;
|
|
16
|
+
try {
|
|
17
|
+
pair = fromPublic(value);
|
|
18
|
+
const pub = pair.getPublicKey();
|
|
19
|
+
if (!pub.startsWith(prefix))
|
|
20
|
+
throw new Error();
|
|
21
|
+
return pub;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw new Error(`validateSpaceAuth: ${field} is not a valid ${prefix === "A" ? "account" : "operator"} public nkey`);
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
pair?.clear();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function seedPublicKey(value, field, prefix) {
|
|
31
|
+
let pair;
|
|
32
|
+
try {
|
|
33
|
+
pair = fromSeed(new TextEncoder().encode(value));
|
|
34
|
+
const pub = pair.getPublicKey();
|
|
35
|
+
if (!pub.startsWith(prefix))
|
|
36
|
+
throw new Error();
|
|
37
|
+
return pub;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
throw new Error(`validateSpaceAuth: ${field} is not a valid ${prefix === "A" ? "account" : "operator"} seed`);
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
pair?.clear();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function claims(jwt, field) {
|
|
47
|
+
try {
|
|
48
|
+
const decoded = decode(jwt);
|
|
49
|
+
const now = Math.floor(Date.now() / 1000);
|
|
50
|
+
if (decoded.aud !== "NATS")
|
|
51
|
+
throw new Error();
|
|
52
|
+
if (!Number.isSafeInteger(decoded.iat) || decoded.iat < 0)
|
|
53
|
+
throw new Error();
|
|
54
|
+
if (typeof decoded.jti !== "string" || decoded.jti.length === 0)
|
|
55
|
+
throw new Error();
|
|
56
|
+
if (decoded.exp !== undefined && (!Number.isSafeInteger(decoded.exp) || decoded.exp <= now))
|
|
57
|
+
throw new Error();
|
|
58
|
+
if (decoded.nbf !== undefined && (!Number.isSafeInteger(decoded.nbf) || decoded.nbf > now))
|
|
59
|
+
throw new Error();
|
|
60
|
+
return decoded;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
throw new Error(`validateSpaceAuth: ${field} is not a currently valid signed NATS JWT`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Validate an existing full space trust bundle before a restore or other state mutation.
|
|
68
|
+
*
|
|
69
|
+
* This is read-only: it never generates, rotates, or signs key material, and returns the same
|
|
70
|
+
* validated object. Persisted bundles may omit `sys.signingSeed`; when present, it is validated too.
|
|
71
|
+
*/
|
|
72
|
+
export function validateSpaceAuth(auth, expectedSpace) {
|
|
73
|
+
const root = record(auth, "auth");
|
|
74
|
+
const operator = record(root.operator, "operator");
|
|
75
|
+
const account = record(root.account, "account");
|
|
76
|
+
const sys = record(root.sys, "sys");
|
|
77
|
+
const space = string(root.space, "space");
|
|
78
|
+
if (!space.trim())
|
|
79
|
+
throw new Error("validateSpaceAuth: space must not be blank");
|
|
80
|
+
if (expectedSpace !== undefined && space !== expectedSpace)
|
|
81
|
+
throw new Error(`validateSpaceAuth: space "${space}" does not match expected space "${expectedSpace}"`);
|
|
82
|
+
const operatorSeed = string(operator.seed, "operator.seed");
|
|
83
|
+
const operatorJwt = string(operator.jwt, "operator.jwt");
|
|
84
|
+
const accountPub = publicKey(string(account.pub, "account.pub"), "account.pub", "A");
|
|
85
|
+
const accountSeed = string(account.seed, "account.seed");
|
|
86
|
+
const accountJwt = string(account.jwt, "account.jwt");
|
|
87
|
+
const signingSeed = string(account.signingSeed, "account.signingSeed");
|
|
88
|
+
const signingPub = publicKey(string(account.signingPub, "account.signingPub"), "account.signingPub", "A");
|
|
89
|
+
const sysPub = publicKey(string(sys.pub, "sys.pub"), "sys.pub", "A");
|
|
90
|
+
const sysJwt = string(sys.jwt, "sys.jwt");
|
|
91
|
+
if (accountPub === sysPub || signingPub === sysPub)
|
|
92
|
+
throw new Error("validateSpaceAuth: data account identities and system account must use distinct nkeys");
|
|
93
|
+
const operatorPub = seedPublicKey(operatorSeed, "operator.seed", "O");
|
|
94
|
+
if (seedPublicKey(accountSeed, "account.seed", "A") !== accountPub)
|
|
95
|
+
throw new Error("validateSpaceAuth: account.seed does not match account.pub");
|
|
96
|
+
if (seedPublicKey(signingSeed, "account.signingSeed", "A") !== signingPub)
|
|
97
|
+
throw new Error("validateSpaceAuth: account.signingSeed does not match account.signingPub");
|
|
98
|
+
if (sys.signingSeed !== undefined) {
|
|
99
|
+
const sysSigningSeed = string(sys.signingSeed, "sys.signingSeed");
|
|
100
|
+
if (seedPublicKey(sysSigningSeed, "sys.signingSeed", "A") !== sysPub)
|
|
101
|
+
throw new Error("validateSpaceAuth: sys.signingSeed does not match sys.pub");
|
|
102
|
+
}
|
|
103
|
+
const operatorClaims = claims(operatorJwt, "operator.jwt");
|
|
104
|
+
const accountClaims = claims(accountJwt, "account.jwt");
|
|
105
|
+
const sysClaims = claims(sysJwt, "sys.jwt");
|
|
106
|
+
publicKey(operatorClaims.sub, "operator JWT subject", "O");
|
|
107
|
+
if (operatorClaims.nats?.type !== Types.Operator || operatorClaims.sub !== operatorPub || operatorClaims.iss !== operatorPub)
|
|
108
|
+
throw new Error("validateSpaceAuth: operator JWT subject, issuer, or type does not match operator.seed");
|
|
109
|
+
if (operatorClaims.name !== `cotal-${token(space)}`)
|
|
110
|
+
throw new Error("validateSpaceAuth: operator JWT name does not match space");
|
|
111
|
+
if (operatorClaims.nats.system_account !== sysPub)
|
|
112
|
+
throw new Error("validateSpaceAuth: operator JWT system account does not match sys.pub");
|
|
113
|
+
if (accountClaims.nats?.type !== Types.Account || accountClaims.sub !== accountPub || accountClaims.iss !== operatorPub)
|
|
114
|
+
throw new Error("validateSpaceAuth: account JWT subject, issuer, or type does not match the operator/account keys");
|
|
115
|
+
if (accountClaims.name !== token(space))
|
|
116
|
+
throw new Error("validateSpaceAuth: account JWT name does not match space");
|
|
117
|
+
const signingKeys = accountClaims.nats.signing_keys;
|
|
118
|
+
if (!Array.isArray(signingKeys) || !signingKeys.includes(signingPub))
|
|
119
|
+
throw new Error("validateSpaceAuth: active account signing key is not trusted as an unscoped signer by account.jwt");
|
|
120
|
+
if (sysClaims.nats?.type !== Types.Account || sysClaims.sub !== sysPub || sysClaims.iss !== operatorPub)
|
|
121
|
+
throw new Error("validateSpaceAuth: system account JWT subject, issuer, or type does not match the operator/system keys");
|
|
122
|
+
if (sysClaims.name !== "SYS")
|
|
123
|
+
throw new Error("validateSpaceAuth: system account JWT name must be SYS");
|
|
124
|
+
return auth;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=space-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"space-auth.js","sourceRoot":"","sources":["../src/space-auth.ts"],"names":[],"mappings":"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"}
|