@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,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CONTRACT STORE (SPEC §13.7): content-addressed, public, immutable, permanent. One
|
|
3
|
+
* artifact per digest-keyed subject `cotal.<space>.epc.<digest-hex>`, published as a SINGLE
|
|
4
|
+
* message (bounded at 256 KiB; the §13.12 operator floor asserts `max_payload` covers it) —
|
|
5
|
+
* deliberately NOT a chunked object store, whose chunk replay needs a consumer with a
|
|
6
|
+
* body-selected delivery target (§13.9 forbids that shape). A closure is fetched
|
|
7
|
+
* artifact-by-artifact through its digest references, never as one blob.
|
|
8
|
+
*
|
|
9
|
+
* CONTENT IDENTITY (§13.7 "Content addressing"): an artifact is its STRICT RFC 8785 CANONICAL
|
|
10
|
+
* JSON — the digest is computed over the canonical bytes, publication REFUSES bytes that are
|
|
11
|
+
* not already the canonical serialization of their own parse (a noncanonical encoding of an
|
|
12
|
+
* equivalent value never gets its own identity; non-JSON bytes are never artifacts), and
|
|
13
|
+
* verify-on-read re-proves BOTH the digest and the canonical form (a garbled store never
|
|
14
|
+
* serves). TWO DIGESTS, NEVER CONFLATED: an ARTIFACT digest identifies one document's
|
|
15
|
+
* canonical bytes; a CLOSURE digest identifies a whole resolved bundle and is the artifact
|
|
16
|
+
* digest of that bundle's MANIFEST `{ v: 1, root, members }` — `members` being every artifact
|
|
17
|
+
* transitively reachable THROUGH BY-DIGEST REFERENCES from `root` (the root itself is named by
|
|
18
|
+
* its own field and appears in `members` only if a reference re-reaches it), sorted
|
|
19
|
+
* lexicographically and deduplicated. The manifest is itself an ordinary artifact on its own
|
|
20
|
+
* digest subject. Closure verification fetches the manifest BY the closure digest, walks the
|
|
21
|
+
* references from `root`, and PROVES the walked set equals `members` EXACTLY — a manifest that
|
|
22
|
+
* under- or over-names its closure never verifies.
|
|
23
|
+
*
|
|
24
|
+
* Reads are the subject-scoped last-by-subject Direct Get on the exact digest subject — no
|
|
25
|
+
* consumer, no replay machinery, nothing body-selected. Publication is mediated and
|
|
26
|
+
* create-only (`Nats-Expected-Last-Subject-Sequence: 0`): a digest subject is written at most
|
|
27
|
+
* once, and the digest is computed HERE from the canonical bytes (never a caller claim). The
|
|
28
|
+
* store seams run on a space-bonded BRANDED context (§13.4): JS + JSM derive from ONE
|
|
29
|
+
* connection, so a lost publish CAS can never validate its "recorded artifact" through a
|
|
30
|
+
* different broker. Every walk is BOUNDED by the frozen §13.7 registration bounds (closure
|
|
31
|
+
* <= 1 MiB, ref chain <= 32, document <= 256 KiB, plus the artifact-count ceiling) and ONE
|
|
32
|
+
* total monotonic time budget — the ceilings are non-raiseable (a caller may narrow, never
|
|
33
|
+
* widen) and a truncated closure never verifies as complete.
|
|
34
|
+
*/
|
|
35
|
+
import { jetstream, jetstreamManager } from "@nats-io/jetstream";
|
|
36
|
+
import { headers as natsHeaders } from "@nats-io/transport-node";
|
|
37
|
+
import { createHash } from "node:crypto";
|
|
38
|
+
import { canonicalJson } from "./canonical.js";
|
|
39
|
+
import { EpEnvelopeError } from "./endpoint-envelope.js";
|
|
40
|
+
import { epcSubject } from "./endpoint-subjects.js";
|
|
41
|
+
import { epcStreamName } from "./endpoint-binding.js";
|
|
42
|
+
/** The §13.7 artifact bound: a document above it cannot ride one message and is refused. */
|
|
43
|
+
export const CONTRACT_ARTIFACT_MAX_BYTES = 256 * 1024;
|
|
44
|
+
/** The §13.7 closure byte bound: the sum of every fetched artifact's canonical bytes. */
|
|
45
|
+
export const CONTRACT_CLOSURE_MAX_BYTES = 1024 * 1024;
|
|
46
|
+
/** The §13.7 reference-chain bound: the walk's depth from the root. */
|
|
47
|
+
export const CONTRACT_CLOSURE_MAX_REF_DEPTH = 32;
|
|
48
|
+
/** The artifact-count ceiling: a walk that would exceed it fails loud, never truncates. */
|
|
49
|
+
export const CONTRACT_CLOSURE_MAX_ARTIFACTS = 64;
|
|
50
|
+
const HEX64 = /^[0-9a-f]{64}$/;
|
|
51
|
+
/** The resources, unreachable from the context token itself. A structural look-alike never
|
|
52
|
+
* appears in this map (so never authorizes); and `ctx.js`/`ctx.jsm` do not exist to reassign. */
|
|
53
|
+
const STORE_RESOURCES = new WeakMap();
|
|
54
|
+
/** Bond the resources to one space by CONSTRUCTION (frozen token + WeakMap-private resources, the
|
|
55
|
+
* shared context discipline): a hand-assembled structural look-alike is rejected at every seam,
|
|
56
|
+
* and the broker cannot be rebound out from under a live operation. */
|
|
57
|
+
export async function contractStoreContext(nc, space) {
|
|
58
|
+
if (nc === null || typeof nc !== "object" || typeof nc.close !== "function")
|
|
59
|
+
throw new EpEnvelopeError("failed-precondition", "a contract-store context is constructed from ONE binding-layer connection; separate resources are never accepted (SPEC 13.4)");
|
|
60
|
+
if (typeof space !== "string" || space.length === 0)
|
|
61
|
+
throw new EpEnvelopeError("failed-precondition", "a contract-store context needs a space");
|
|
62
|
+
const js = jetstream(nc);
|
|
63
|
+
const jsm = await jetstreamManager(nc);
|
|
64
|
+
const ctx = Object.freeze({ space });
|
|
65
|
+
STORE_RESOURCES.set(ctx, { js, jsm });
|
|
66
|
+
return ctx;
|
|
67
|
+
}
|
|
68
|
+
/** The brand check AND the resource fetch in one: a context not minted by contractStoreContext()
|
|
69
|
+
* is absent from the map and never authorizes. */
|
|
70
|
+
function resources(ctx) {
|
|
71
|
+
const r = ctx === null || typeof ctx !== "object" ? undefined : STORE_RESOURCES.get(ctx);
|
|
72
|
+
if (r === undefined)
|
|
73
|
+
throw new EpEnvelopeError("failed-precondition", `the contract-store context was not constructed by contractStoreContext(); a hand-assembled resource bundle never authorizes - the space bond is constructed, not asserted (SPEC 13.4)`);
|
|
74
|
+
return r;
|
|
75
|
+
}
|
|
76
|
+
/** The artifact's subject token: SHA-256 hex over the artifact's CANONICAL bytes (§13.7: the
|
|
77
|
+
* `sha256:` prefix is not a subject token). The bytes handed here MUST already be canonical —
|
|
78
|
+
* publication and verify-on-read enforce that ({@link assertCanonicalArtifactBytes}). */
|
|
79
|
+
export function contractArtifactDigestHex(bytes) {
|
|
80
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
81
|
+
}
|
|
82
|
+
/** Normalize a digest reference (`<hex>` or `sha256:<hex>`) to the bare subject token; a
|
|
83
|
+
* malformed reference fails loud (a garbled ref never fetches an unintended subject). */
|
|
84
|
+
export function contractRefToHex(ref) {
|
|
85
|
+
const hex = ref.startsWith("sha256:") ? ref.slice("sha256:".length) : ref;
|
|
86
|
+
if (!HEX64.test(hex))
|
|
87
|
+
throw new EpEnvelopeError("contract-invalid", `contract reference ${JSON.stringify(ref)} is not a sha256 digest; a garbled reference never resolves (SPEC 13.7)`);
|
|
88
|
+
return hex;
|
|
89
|
+
}
|
|
90
|
+
/** A value's canonical artifact bytes (strict RFC 8785 over I-JSON): what publication stores
|
|
91
|
+
* and what the digest identifies. Non-interchangeable values refuse. */
|
|
92
|
+
export function contractArtifactCanonicalBytes(value) {
|
|
93
|
+
try {
|
|
94
|
+
return new TextEncoder().encode(canonicalJson(value));
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
throw new EpEnvelopeError("contract-invalid", `the value is not interchangeable JSON (${e.message}); only strict I-JSON is a contract artifact (SPEC 13.7)`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Prove presented bytes ARE their own parse's strict RFC 8785 canonical serialization and
|
|
101
|
+
* return the parsed value (§13.7 content identity): invalid UTF-8, non-JSON, and every
|
|
102
|
+
* noncanonical encoding (reordered keys, whitespace, duplicate keys, noncanonical numbers)
|
|
103
|
+
* refuse — an equivalent value in a different encoding never gets its own identity, so two
|
|
104
|
+
* implementations can never disagree on an artifact's digest. */
|
|
105
|
+
export function assertCanonicalArtifactBytes(bytes, what) {
|
|
106
|
+
let text;
|
|
107
|
+
try {
|
|
108
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
throw new EpEnvelopeError("contract-invalid", `${what} is not valid UTF-8; only strict canonical JSON is a contract artifact (SPEC 13.7)`);
|
|
112
|
+
}
|
|
113
|
+
let value;
|
|
114
|
+
try {
|
|
115
|
+
value = JSON.parse(text);
|
|
116
|
+
}
|
|
117
|
+
catch (e) {
|
|
118
|
+
throw new EpEnvelopeError("contract-invalid", `${what} does not parse as JSON (${e.message}); only strict canonical JSON is a contract artifact (SPEC 13.7)`);
|
|
119
|
+
}
|
|
120
|
+
let canonical;
|
|
121
|
+
try {
|
|
122
|
+
canonical = canonicalJson(value);
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
throw new EpEnvelopeError("contract-invalid", `${what} is not interchangeable I-JSON (${e.message}); only strict canonical JSON is a contract artifact (SPEC 13.7)`);
|
|
126
|
+
}
|
|
127
|
+
if (canonical !== text)
|
|
128
|
+
throw new EpEnvelopeError("contract-invalid", `${what} is not its own RFC 8785 canonical serialization; a noncanonical encoding never gets its own content identity (SPEC 13.7)`);
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
/** Publish one artifact at its content address, create-only. The bytes are DETACHED at entry
|
|
132
|
+
* (a caller mutating the buffer across the publish await changes nothing), PROVEN canonical
|
|
133
|
+
* ({@link assertCanonicalArtifactBytes}), and the digest is computed FROM that snapshot; a
|
|
134
|
+
* lost CAS fetches the recorded artifact and — because the subject IS the digest and the
|
|
135
|
+
* fetch verifies — the loss is an idempotent no-op ({won: false}). Oversize refuses. */
|
|
136
|
+
export async function publishContractArtifact(ctx, bytes) {
|
|
137
|
+
const { js } = resources(ctx);
|
|
138
|
+
if (!(bytes instanceof Uint8Array) || bytes.length === 0)
|
|
139
|
+
throw new EpEnvelopeError("contract-invalid", "a contract artifact is non-empty canonical JSON bytes (SPEC 13.7)");
|
|
140
|
+
if (bytes.length > CONTRACT_ARTIFACT_MAX_BYTES)
|
|
141
|
+
throw new EpEnvelopeError("contract-invalid", `a contract artifact is bounded at ${CONTRACT_ARTIFACT_MAX_BYTES} bytes (got ${bytes.length}); a document above the bound cannot ride one message and is refused, never chunked (SPEC 13.7/13.12)`);
|
|
142
|
+
const snapshot = new Uint8Array(bytes); // detached at entry (a real copy — Buffer.slice would ALIAS): validated, digested, and published as ONE value
|
|
143
|
+
assertCanonicalArtifactBytes(snapshot, "the artifact");
|
|
144
|
+
const digestHex = contractArtifactDigestHex(snapshot);
|
|
145
|
+
const subject = epcSubject(ctx.space, digestHex);
|
|
146
|
+
const h = natsHeaders();
|
|
147
|
+
h.set("Nats-Expected-Last-Subject-Sequence", "0");
|
|
148
|
+
try {
|
|
149
|
+
await js.publish(subject, snapshot, { headers: h });
|
|
150
|
+
return { digestHex, won: true };
|
|
151
|
+
}
|
|
152
|
+
catch (e) {
|
|
153
|
+
const code = e?.code;
|
|
154
|
+
if (code !== 10071 && code !== 10164)
|
|
155
|
+
throw e;
|
|
156
|
+
// The subject is the digest: a prior write with this address holds these bytes (verified
|
|
157
|
+
// on fetch). The loss is idempotent; an unreadable prior write is loud.
|
|
158
|
+
const prior = await fetchContractArtifact(ctx, digestHex);
|
|
159
|
+
if (prior === undefined)
|
|
160
|
+
throw new EpEnvelopeError("internal", `the artifact CAS for ${subject} was lost but the recorded artifact is not readable (SPEC 13.4)`);
|
|
161
|
+
return { digestHex, won: false };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/** Fetch one artifact by digest (`undefined` = not published). VERIFY-ON-READ is unconditional
|
|
165
|
+
* and TWO-PROOF: the fetched bytes must recompute the digest AND be strict canonical JSON —
|
|
166
|
+
* content addressing is the tamper boundary and a garbled store never serves (§13.7). */
|
|
167
|
+
export async function fetchContractArtifact(ctx, digestHex) {
|
|
168
|
+
const { jsm } = resources(ctx);
|
|
169
|
+
const subject = epcSubject(ctx.space, digestHex); // validates the token
|
|
170
|
+
let stored;
|
|
171
|
+
try {
|
|
172
|
+
stored = await jsm.direct.getMessage(epcStreamName(ctx.space), { last_by_subj: subject });
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
if (e?.code === 10037)
|
|
176
|
+
return undefined; // the ONLY "genuinely absent" result
|
|
177
|
+
throw new EpEnvelopeError("unavailable", `the contract-store read for ${digestHex} failed (a failed observation is never absence, SPEC 13.7): ${e?.message ?? String(e)}`);
|
|
178
|
+
}
|
|
179
|
+
if (stored === null)
|
|
180
|
+
return undefined;
|
|
181
|
+
// The 256 KiB document bound is enforced on EVERY consuming read, not just publication (distsys
|
|
182
|
+
// 8dcad72 M2): a directly-planted oversize artifact (bypassing publishContractArtifact) must
|
|
183
|
+
// never be served, and the closure walk (which reads through here) inherits the per-document cap.
|
|
184
|
+
if (stored.data.length > CONTRACT_ARTIFACT_MAX_BYTES)
|
|
185
|
+
throw new EpEnvelopeError("internal", `the artifact at ${subject} is ${stored.data.length} bytes, above the ${CONTRACT_ARTIFACT_MAX_BYTES} document bound; a store that admitted an oversize artifact never serves it (SPEC 13.7)`);
|
|
186
|
+
if (contractArtifactDigestHex(stored.data) !== digestHex)
|
|
187
|
+
throw new EpEnvelopeError("internal", `the artifact at ${subject} does not recompute its digest; verify-on-read is the tamper boundary and fails loud (SPEC 13.7)`);
|
|
188
|
+
try {
|
|
189
|
+
assertCanonicalArtifactBytes(stored.data, `the artifact at ${subject}`);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
throw new EpEnvelopeError("internal", `the artifact at ${subject} is not strict canonical JSON; a garbled store never serves (SPEC 13.7)`);
|
|
193
|
+
}
|
|
194
|
+
return stored.data;
|
|
195
|
+
}
|
|
196
|
+
const sha256Ref = (hex) => `sha256:${hex}`;
|
|
197
|
+
/** A manifest digest field is EXACTLY `sha256:<64-hex>` (frozen SPEC 13.7:1858-1861), never a
|
|
198
|
+
* bare `<hex>` or any other spelling (distsys 8dcad72 HIGH): two manifests differing only in
|
|
199
|
+
* digest spelling are both canonical JSON, receive DIFFERENT closure digests, yet verify the same
|
|
200
|
+
* walked graph - so one closure would have two identities. Normalization belongs in the BUILDER's
|
|
201
|
+
* input; the consuming parse requires the canonical prefixed form and refuses anything else. */
|
|
202
|
+
const SHA256_REF = /^sha256:[0-9a-f]{64}$/;
|
|
203
|
+
function assertManifestDigest(ref, what) {
|
|
204
|
+
if (typeof ref !== "string" || !SHA256_REF.test(ref))
|
|
205
|
+
throw new EpEnvelopeError("contract-invalid", `${what} must be exactly "sha256:<64-hex>" (SPEC 13.7); a bare-hex or otherwise-spelled digest gives one closure two identities and never names it`);
|
|
206
|
+
}
|
|
207
|
+
/** Build the canonical manifest for a walked closure: refs normalize, members sort + dedup.
|
|
208
|
+
* The ROOT is named by its own field and belongs in `members` only when a reference
|
|
209
|
+
* re-reaches it (the §13.7 "reachable THROUGH references" rule, pinned here so two
|
|
210
|
+
* implementations always mint the identical manifest). */
|
|
211
|
+
export function buildContractClosureManifest(rootRef, memberRefs) {
|
|
212
|
+
const root = sha256Ref(contractRefToHex(rootRef));
|
|
213
|
+
const members = [...new Set(memberRefs.map((r) => sha256Ref(contractRefToHex(r))))].sort();
|
|
214
|
+
return { v: 1, root, members };
|
|
215
|
+
}
|
|
216
|
+
function parseClosureManifest(value, what) {
|
|
217
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
218
|
+
throw new EpEnvelopeError("contract-invalid", `${what} is not a manifest object (SPEC 13.7)`);
|
|
219
|
+
const o = value;
|
|
220
|
+
for (const k of Object.keys(o))
|
|
221
|
+
if (!["v", "root", "members"].includes(k))
|
|
222
|
+
throw new EpEnvelopeError("contract-invalid", `${what} carries the unknown field "${k}"; the manifest schema is closed (SPEC 13.7)`);
|
|
223
|
+
if (o.v !== 1 || typeof o.root !== "string" || !Array.isArray(o.members))
|
|
224
|
+
throw new EpEnvelopeError("contract-invalid", `${what} is not { v: 1, root, members } (SPEC 13.7)`);
|
|
225
|
+
assertManifestDigest(o.root, `${what} root`);
|
|
226
|
+
if (o.members.length > CONTRACT_CLOSURE_MAX_ARTIFACTS)
|
|
227
|
+
throw new EpEnvelopeError("contract-invalid", `${what} names ${o.members.length} members, above the ${CONTRACT_CLOSURE_MAX_ARTIFACTS}-artifact ceiling (SPEC 13.7)`);
|
|
228
|
+
for (let i = 0; i < o.members.length; i++) {
|
|
229
|
+
const m = o.members[i];
|
|
230
|
+
assertManifestDigest(m, `${what} member ${i}`);
|
|
231
|
+
// The canonical form IS sorted + deduplicated: an unsorted or duplicated members list is a
|
|
232
|
+
// DIFFERENT byte sequence claiming the same closure — refused, never silently normalized.
|
|
233
|
+
if (i > 0 && o.members[i - 1] >= m)
|
|
234
|
+
throw new EpEnvelopeError("contract-invalid", `${what} members are not strictly sorted/deduplicated at index ${i}; a noncanonical manifest never names a closure (SPEC 13.7)`);
|
|
235
|
+
}
|
|
236
|
+
return { v: 1, root: o.root, members: o.members };
|
|
237
|
+
}
|
|
238
|
+
/** Publish a closure's manifest as an ordinary canonical artifact; the returned digest IS the
|
|
239
|
+
* closure digest (§13.7). */
|
|
240
|
+
export async function publishContractClosureManifest(ctx, manifest) {
|
|
241
|
+
resources(ctx); // brand-check (resources unused here; the delegated publish/fetch use them)
|
|
242
|
+
const value = parseClosureManifest(manifest, "the manifest");
|
|
243
|
+
const res = await publishContractArtifact(ctx, contractArtifactCanonicalBytes(value));
|
|
244
|
+
return { closureDigestHex: res.digestHex, won: res.won };
|
|
245
|
+
}
|
|
246
|
+
/** Fetch and VERIFY a closure by its CLOSURE digest (§13.7): fetch the manifest artifact at
|
|
247
|
+
* that digest (its identity is proven by the fetch), then walk the by-digest references from
|
|
248
|
+
* `root` through the `extractRefs` resolution seam and PROVE the walked set equals
|
|
249
|
+
* `manifest.members` exactly — an under-naming manifest (the walk reaches an unlisted
|
|
250
|
+
* artifact) and an over-naming one (a listed member is never reached) both refuse; a missing
|
|
251
|
+
* artifact anywhere is all-or-nothing loud. The walk is BOUNDED: the frozen closure byte
|
|
252
|
+
* bound, the reference-chain depth, the artifact-count ceiling (non-raiseable; a caller may
|
|
253
|
+
* narrow), a per-artifact reference cap, and ONE total monotonic time budget. `extractRefs`
|
|
254
|
+
* receives a DETACHED copy of each artifact's bytes (a mutating seam cannot poison the
|
|
255
|
+
* returned map) and its answer is size-capped. Returns digest → bytes (manifest excluded;
|
|
256
|
+
* root first, then first-visit order) plus the parsed manifest. */
|
|
257
|
+
export async function fetchContractClosure(ctx, closureDigestRef, extractRefs, opts = {}) {
|
|
258
|
+
resources(ctx); // brand-check (resources unused here; the delegated publish/fetch use them)
|
|
259
|
+
const max = opts.maxArtifacts ?? CONTRACT_CLOSURE_MAX_ARTIFACTS;
|
|
260
|
+
if (!Number.isSafeInteger(max) || max <= 0 || max > CONTRACT_CLOSURE_MAX_ARTIFACTS)
|
|
261
|
+
throw new EpEnvelopeError("failed-precondition", `maxArtifacts must be a positive integer at or below the frozen ${CONTRACT_CLOSURE_MAX_ARTIFACTS}-artifact ceiling (a caller narrows, never widens); got ${JSON.stringify(opts.maxArtifacts)} (SPEC 13.7)`);
|
|
262
|
+
const budgetMs = opts.walkBudgetMs ?? 30_000;
|
|
263
|
+
if (!Number.isSafeInteger(budgetMs) || budgetMs <= 0)
|
|
264
|
+
throw new EpEnvelopeError("failed-precondition", `walkBudgetMs must be a positive integer; got ${JSON.stringify(opts.walkBudgetMs)}`);
|
|
265
|
+
if (typeof extractRefs !== "function")
|
|
266
|
+
throw new EpEnvelopeError("failed-precondition", "a closure fetch requires the reference-resolution seam (extractRefs); the store never guesses a document's reference shape (SPEC 13.7)");
|
|
267
|
+
const startedAt = performance.now();
|
|
268
|
+
const overBudget = () => performance.now() - startedAt > budgetMs;
|
|
269
|
+
// The ONE monotonic budget bounds the WHOLE walk, including the awaited reads and the synchronous
|
|
270
|
+
// extractor (distsys 8dcad72 M1): every Direct Get is raced against the REMAINING budget (a stuck
|
|
271
|
+
// read is a bounded deadline, never a hang), and overBudget() is checked after each extractRefs
|
|
272
|
+
// and before returning, so a slow synchronous seam cannot blow the budget and still return success.
|
|
273
|
+
const raceBudget = async (p, what) => {
|
|
274
|
+
const rem = budgetMs - (performance.now() - startedAt);
|
|
275
|
+
if (rem <= 0)
|
|
276
|
+
throw new EpEnvelopeError("deadline-exceeded", `the closure walk exhausted its ${budgetMs}ms budget before ${what} (SPEC 13.7/13.8)`);
|
|
277
|
+
let timer;
|
|
278
|
+
const deadline = new Promise((_, reject) => { timer = setTimeout(() => reject(new EpEnvelopeError("deadline-exceeded", `the closure walk exceeded its ${budgetMs}ms budget during ${what}; a closure fetch is bounded and fails loud, never hangs (SPEC 13.7/13.8)`)), rem); });
|
|
279
|
+
try {
|
|
280
|
+
return await Promise.race([p, deadline]);
|
|
281
|
+
}
|
|
282
|
+
finally {
|
|
283
|
+
clearTimeout(timer);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
const closureHex = contractRefToHex(closureDigestRef);
|
|
287
|
+
const manifestBytes = await raceBudget(fetchContractArtifact(ctx, closureHex), `the manifest fetch for ${closureHex}`);
|
|
288
|
+
if (manifestBytes === undefined)
|
|
289
|
+
throw new EpEnvelopeError("failed-precondition", `the closure manifest ${closureHex} is not published; a closure digest names its manifest artifact (SPEC 13.7)`);
|
|
290
|
+
const manifest = parseClosureManifest(assertCanonicalArtifactBytes(manifestBytes, `the manifest ${closureHex}`), `the manifest ${closureHex}`);
|
|
291
|
+
const rootHex = contractRefToHex(manifest.root);
|
|
292
|
+
const out = new Map();
|
|
293
|
+
let totalBytes = 0;
|
|
294
|
+
let rootReReached = false; // a reference naming the root makes it reference-reachable (a member)
|
|
295
|
+
// BFS with depth tracking: the queue carries (hex, depth); depth is the reference-chain
|
|
296
|
+
// length from the root (§13.7 ref chain <= 32).
|
|
297
|
+
const queue = [{ hex: rootHex, depth: 0 }];
|
|
298
|
+
while (queue.length > 0) {
|
|
299
|
+
if (overBudget())
|
|
300
|
+
throw new EpEnvelopeError("deadline-exceeded", `the closure walk for ${closureHex} exceeded its ${budgetMs}ms budget; a closure fetch is bounded and fails loud, never hangs (SPEC 13.7/13.8)`);
|
|
301
|
+
const { hex, depth } = queue.shift();
|
|
302
|
+
// The reference-chain depth is checked on EVERY dequeued edge, BEFORE the visited dedupe
|
|
303
|
+
// (distsys 8dcad72 M4): an over-depth edge that happens to re-reach an already-visited node
|
|
304
|
+
// (e.g. a 33-deep cycle back to the root) must still trip the bound, never be silently skipped.
|
|
305
|
+
if (depth > CONTRACT_CLOSURE_MAX_REF_DEPTH)
|
|
306
|
+
throw new EpEnvelopeError("contract-invalid", `the closure walk for ${closureHex} exceeds the ${CONTRACT_CLOSURE_MAX_REF_DEPTH}-deep reference chain bound (SPEC 13.7)`);
|
|
307
|
+
if (out.has(hex))
|
|
308
|
+
continue; // cycle/diamond-safe (after the depth bound)
|
|
309
|
+
if (out.size >= max)
|
|
310
|
+
throw new EpEnvelopeError("failed-precondition", `the contract closure exceeds the ${max}-artifact bound; a truncated closure never verifies as complete (SPEC 13.7)`);
|
|
311
|
+
const bytes = await raceBudget(fetchContractArtifact(ctx, hex), `the fetch of ${hex}`);
|
|
312
|
+
if (bytes === undefined)
|
|
313
|
+
throw new EpEnvelopeError("failed-precondition", `the closure references artifact ${hex} but it is not published; a closure is fetched all-or-nothing (SPEC 13.7)`);
|
|
314
|
+
totalBytes += bytes.length;
|
|
315
|
+
if (totalBytes > CONTRACT_CLOSURE_MAX_BYTES)
|
|
316
|
+
throw new EpEnvelopeError("contract-invalid", `the closure for ${closureHex} exceeds the ${CONTRACT_CLOSURE_MAX_BYTES}-byte bound (SPEC 13.7)`);
|
|
317
|
+
out.set(hex, bytes);
|
|
318
|
+
const refs = extractRefs(new Uint8Array(bytes), hex); // DETACHED copy (a real copy — Buffer.slice would ALIAS the stored bytes): a mutating seam cannot poison the returned map
|
|
319
|
+
if (!Array.isArray(refs) || !refs.every((r) => typeof r === "string"))
|
|
320
|
+
throw new EpEnvelopeError("internal", `the reference-resolution seam returned a non-string-array for ${hex}; a garbled resolution never extends a closure (SPEC 13.7)`);
|
|
321
|
+
if (refs.length > CONTRACT_CLOSURE_MAX_ARTIFACTS)
|
|
322
|
+
throw new EpEnvelopeError("contract-invalid", `artifact ${hex} names ${refs.length} references, above the ${CONTRACT_CLOSURE_MAX_ARTIFACTS}-reference cap; an unbounded reference list never extends a closure (SPEC 13.7)`);
|
|
323
|
+
if (overBudget()) // a slow synchronous extractRefs cannot be interrupted mid-run, but it must not blow the budget and still return success (M1)
|
|
324
|
+
throw new EpEnvelopeError("deadline-exceeded", `the closure walk for ${closureHex} exceeded its ${budgetMs}ms budget resolving references for ${hex} (SPEC 13.7/13.8)`);
|
|
325
|
+
for (const r of refs) {
|
|
326
|
+
const rHex = contractRefToHex(r);
|
|
327
|
+
if (rHex === rootHex)
|
|
328
|
+
rootReReached = true; // a cycle back makes the root a member too
|
|
329
|
+
queue.push({ hex: rHex, depth: depth + 1 });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// The walked reachable-THROUGH-references set must equal the manifest's members EXACTLY
|
|
333
|
+
// (§13.7): the root belongs iff a reference re-reached it — tracked during the walk, so
|
|
334
|
+
// exactly ONE manifest (one closure digest) names any given closure, never two.
|
|
335
|
+
const walked = new Set();
|
|
336
|
+
for (const hex of out.keys())
|
|
337
|
+
if (hex !== rootHex || rootReReached)
|
|
338
|
+
walked.add(sha256Ref(hex));
|
|
339
|
+
const memberSet = new Set(manifest.members);
|
|
340
|
+
for (const m of walked)
|
|
341
|
+
if (!memberSet.has(m))
|
|
342
|
+
throw new EpEnvelopeError("contract-invalid", `the closure walk reached ${m} but the manifest ${closureHex} does not name it; an under-naming manifest never verifies (SPEC 13.7)`);
|
|
343
|
+
for (const m of memberSet)
|
|
344
|
+
if (!walked.has(m))
|
|
345
|
+
throw new EpEnvelopeError("contract-invalid", `the manifest ${closureHex} names ${m} but the walk never reached it; an over-naming manifest never verifies (SPEC 13.7)`);
|
|
346
|
+
return { manifest, artifacts: out };
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=endpoint-contract-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoint-contract-store.js","sourceRoot":"","sources":["../src/endpoint-contract-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v0.4 endpoint control-surface envelope (SPEC §13.3) — the versioned typed shapes riding the
|
|
3
|
+
* §13.2 rails (`EndpointRequest`/`EndpointReply`/`EndpointEvent`), the structured error catalog,
|
|
4
|
+
* W3C Trace Context correlation, and the consuming-boundary validators.
|
|
5
|
+
*
|
|
6
|
+
* Validation is FAIL-LOUD with the exact catalog code the spec assigns to each violation
|
|
7
|
+
* (`unsupported-version`, `bad-request`, `contract-mismatch`, `op-mismatch`, `target-mismatch`,
|
|
8
|
+
* `sender-mismatch`) so a rejecting boundary never invents a classification. Unknown object
|
|
9
|
+
* fields are ignored (§5); the parsers return a picked copy carrying exactly the defined fields,
|
|
10
|
+
* so nothing downstream can quietly grow a dependency on an undeclared one. Contract-schema
|
|
11
|
+
* validation of `args`/`data` is §13.7's job ({@link import("./schema-profile.js")}); the helpers
|
|
12
|
+
* here only map its outcome to the invocation-time code (`bad-request`, distinct from
|
|
13
|
+
* registration-time `contract-invalid`).
|
|
14
|
+
*/
|
|
15
|
+
import type { ValidateFunction } from "ajv/dist/2020.js";
|
|
16
|
+
import { type ParsedEpRequest } from "./endpoint-subjects.js";
|
|
17
|
+
import type { EndpointRef } from "./types.js";
|
|
18
|
+
/** The envelope schema version — independent of the wire `protocolVersion`; starts at its own
|
|
19
|
+
* v1 inside the v0.4 revision. Other values are rejected (`unsupported-version`). */
|
|
20
|
+
export declare const EP_ENVELOPE_V = 1;
|
|
21
|
+
/** The §13.3 error catalog. Extensions add codes only under reverse-DNS; any code (catalog or
|
|
22
|
+
* extension) is one token of at most 64 bytes. */
|
|
23
|
+
export declare const EP_ERROR_CODES: readonly ["bad-request", "unsupported-version", "op-mismatch", "class-mismatch", "target-mismatch", "sender-mismatch", "unauthenticated", "permission-denied", "not-found", "already-exists", "conflict", "contract-mismatch", "contract-invalid", "failed-precondition", "deadline-exceeded", "cancelled", "expired", "unavailable", "unimplemented", "resource-exhausted", "internal"];
|
|
24
|
+
export type EpErrorCode = (typeof EP_ERROR_CODES)[number];
|
|
25
|
+
/** True iff `code` is a catalog token or a reverse-DNS extension token within the 64-byte bound. */
|
|
26
|
+
export declare function isEpErrorCode(code: string): boolean;
|
|
27
|
+
/** One `details[]` entry: `kind` is reverse-DNS-namespaced (§13.3), the rest is open. */
|
|
28
|
+
export interface EpErrorDetail {
|
|
29
|
+
kind: string;
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
/** The `EndpointReply.error` shape. */
|
|
33
|
+
export interface EpError {
|
|
34
|
+
code: string;
|
|
35
|
+
message: string;
|
|
36
|
+
details?: EpErrorDetail[];
|
|
37
|
+
}
|
|
38
|
+
/** A consuming-boundary rejection: the catalog code plus a human message. Boundaries convert it
|
|
39
|
+
* to an `EndpointReply` error via {@link EpEnvelopeError.toEpError} (or, on reply-less planes,
|
|
40
|
+
* to the §13.4 decision/quarantine fact carrying the same code). */
|
|
41
|
+
export declare class EpEnvelopeError extends Error {
|
|
42
|
+
readonly code: EpErrorCode;
|
|
43
|
+
readonly details?: EpErrorDetail[] | undefined;
|
|
44
|
+
constructor(code: EpErrorCode, message: string, details?: EpErrorDetail[] | undefined);
|
|
45
|
+
toEpError(): EpError;
|
|
46
|
+
}
|
|
47
|
+
/** W3C Trace Context correlation slot, propagated to downstream calls, events, facts, receipts. */
|
|
48
|
+
export interface EpCorrelation {
|
|
49
|
+
traceparent?: string;
|
|
50
|
+
tracestate?: string;
|
|
51
|
+
baggage?: string;
|
|
52
|
+
}
|
|
53
|
+
/** The invocation binding: endpoint + command MUST agree with the subject (`op-mismatch`); the
|
|
54
|
+
* digests pin the described contract and are REQUIRED on every command except `describe`
|
|
55
|
+
* (`contract-mismatch` when missing — a payload-free side pins the void schema's digest). */
|
|
56
|
+
export interface EpOp {
|
|
57
|
+
endpoint: string;
|
|
58
|
+
command: string;
|
|
59
|
+
inputDigest?: string;
|
|
60
|
+
outputDigest?: string;
|
|
61
|
+
}
|
|
62
|
+
/** The body target block (§13.3): present exactly for the targeted modes, `owner` pinned to the
|
|
63
|
+
* subject token; `actor`/`lifecycleUid` are validator-compared against the current mapping. */
|
|
64
|
+
export interface EpTargetBlock {
|
|
65
|
+
owner: string;
|
|
66
|
+
actor: string;
|
|
67
|
+
lifecycleUid: string;
|
|
68
|
+
mappingRevision?: number;
|
|
69
|
+
}
|
|
70
|
+
/** A submission's declared delivery contract (`record` is a state contract, never a request
|
|
71
|
+
* class; an action command's submissions are `journal`). */
|
|
72
|
+
export type EpClass = "ephemeral" | "journal";
|
|
73
|
+
export interface EndpointRequest {
|
|
74
|
+
v: typeof EP_ENVELOPE_V;
|
|
75
|
+
id: string;
|
|
76
|
+
op: EpOp;
|
|
77
|
+
class: EpClass;
|
|
78
|
+
/** The verb: `true` = call (reply expected, `deadlineMs` required), `false` = cast. The
|
|
79
|
+
* subject shape is identical for both; the verb never changes the grammar. */
|
|
80
|
+
replyExpected: boolean;
|
|
81
|
+
/** MUST for a command whose contract declares the action composite (a contract-level rule the
|
|
82
|
+
* serve machinery enforces with the contract in hand); shape-checked here when present. */
|
|
83
|
+
goalId?: string;
|
|
84
|
+
target?: EpTargetBlock;
|
|
85
|
+
/** The input payload: a JSON object, or explicit `null` — a canonical-void side's payload is
|
|
86
|
+
* absent OR `null` (§13.7), so `null` must survive parsing for the command's own schema
|
|
87
|
+
* validator to decide (an object-input contract still rejects it there, as `bad-request`). */
|
|
88
|
+
args?: Record<string, unknown> | null;
|
|
89
|
+
from: EndpointRef;
|
|
90
|
+
/** Caller deadline budget, bounded never unbounded: required for calls and for journal-class
|
|
91
|
+
* submissions (there it is the decision deadline, §13.4). */
|
|
92
|
+
deadlineMs?: number;
|
|
93
|
+
correlation?: EpCorrelation;
|
|
94
|
+
/** Opaque signed authorization-context slot; identity never rides it. Its fingerprint binding
|
|
95
|
+
* is {@link authDigest} over these bytes exactly as carried. */
|
|
96
|
+
auth?: string;
|
|
97
|
+
}
|
|
98
|
+
export interface EndpointReply {
|
|
99
|
+
v: typeof EP_ENVELOPE_V;
|
|
100
|
+
id: string;
|
|
101
|
+
ok: boolean;
|
|
102
|
+
data?: unknown;
|
|
103
|
+
error?: EpError;
|
|
104
|
+
/** Opaque signed receipt slot (§13.10). */
|
|
105
|
+
receipt?: string;
|
|
106
|
+
}
|
|
107
|
+
/** An event (incl. per-goal progress) on the `epe` plane. The publishing instance and epoch are
|
|
108
|
+
* read from the SUBJECT (§13.2), never from payload fields. */
|
|
109
|
+
export interface EndpointEvent {
|
|
110
|
+
v: typeof EP_ENVELOPE_V;
|
|
111
|
+
topic: string;
|
|
112
|
+
ts: number;
|
|
113
|
+
data: unknown;
|
|
114
|
+
correlation?: EpCorrelation;
|
|
115
|
+
}
|
|
116
|
+
/** `authDigest`: `sha256:<hex>` over the UTF-8 bytes of the `auth` slot EXACTLY as carried. The
|
|
117
|
+
* slot is already a canonical signed artifact, so it is digested as bytes, never
|
|
118
|
+
* re-canonicalized; absent from the §13.4 fingerprint iff `auth` is absent. A malformed-UTF-16
|
|
119
|
+
* slot is refused (`bad-request`): a lone surrogate has no UTF-8 encoding, so its "digest"
|
|
120
|
+
* would be over a substituted value and two distinct slots could share one fingerprint. */
|
|
121
|
+
export declare function authDigest(auth: string): string;
|
|
122
|
+
/** Shape-validate an incoming request envelope (the pre-subject half of the consuming boundary;
|
|
123
|
+
* {@link checkRequestSubjectAgreement} is the other half). Throws {@link EpEnvelopeError} with
|
|
124
|
+
* the exact catalog code; returns a picked copy with exactly the §13.3 fields. */
|
|
125
|
+
export declare function parseEndpointRequest(raw: unknown): EndpointRequest;
|
|
126
|
+
/** The body↔subject agreement half of the consuming boundary (§13.3): `op` MUST agree with the
|
|
127
|
+
* subject (`op-mismatch`); the body target is ABSENT for `self`/untargeted forms (a supplied
|
|
128
|
+
* one is `target-mismatch`, never ignored) and REQUIRED with a subject-equal owner (and, in
|
|
129
|
+
* `handle` mode, subject-equal actor + lifecycleUid) for the targeted forms; `from.id` MUST
|
|
130
|
+
* equal the subject sender principal (`sender-mismatch`). Currency of `target.actor`/
|
|
131
|
+
* `target.lifecycleUid` against the live mapping is the handler's fresh-read (`expired`),
|
|
132
|
+
* deliberately not checkable here. */
|
|
133
|
+
export declare function checkRequestSubjectAgreement(env: EndpointRequest, subject: ParsedEpRequest): void;
|
|
134
|
+
/** The contract-class agreement check (`class-mismatch`): the envelope's declared class MUST
|
|
135
|
+
* equal the command's contract class. Split out because it needs the contract in hand. */
|
|
136
|
+
export declare function assertClassMatches(env: EndpointRequest, declaredClass: EpClass): void;
|
|
137
|
+
/** Shape-validate a reply at the caller's consuming boundary. `data` is schema-validated by the
|
|
138
|
+
* caller against its PINNED output digest (§13.7), not here. */
|
|
139
|
+
export declare function parseEndpointReply(raw: unknown): EndpointReply;
|
|
140
|
+
/** Shape-validate an event at its consuming boundary (§13.3: every plane is runtime-validated).
|
|
141
|
+
* The publishing instance and epoch come from the SUBJECT; payload claims are display data. */
|
|
142
|
+
export declare function parseEndpointEvent(raw: unknown): EndpointEvent;
|
|
143
|
+
/** Validate `args` against the command's compiled input schema BEFORE any effect: failure is the
|
|
144
|
+
* invocation-time `bad-request` (registration-time violations are `contract-invalid`,
|
|
145
|
+
* {@link import("./schema-profile.js").ContractInvalidError}). Against the void schema the
|
|
146
|
+
* payload is absent or `null` (§13.7), so `undefined` args validate as `null` here and only
|
|
147
|
+
* here; an explicit `null` passes through unchanged, and it is the SCHEMA (an object-typed
|
|
148
|
+
* input contract) that rejects null for non-void commands. The §13.8 validation budget is the
|
|
149
|
+
* PROFILE's fixed 10ms, read internally so no caller
|
|
150
|
+
* can tune it away, and is enforced post-hoc, fail-loud as `bad-request` (the spec's
|
|
151
|
+
* over-budget code for validate time, distinct from compile's `contract-invalid`). Post-hoc
|
|
152
|
+
* measurement classifies, it cannot preempt — the pre-emptive defense is the registration-time
|
|
153
|
+
* bounded-pattern gate (schema-profile), which keeps the exponential backtracking class out of
|
|
154
|
+
* registered contracts in the first place. */
|
|
155
|
+
export declare function assertArgsValid(validate: ValidateFunction, args: Record<string, unknown> | null | undefined): unknown;
|
|
156
|
+
/** Validate an output payload against the command's compiled output schema at EITHER §13.7
|
|
157
|
+
* boundary — the responder's, before the success publish, or the caller's, on the consumed
|
|
158
|
+
* reply — the symmetric budgeted half of {@link assertArgsValid}, under the SAME fixed
|
|
159
|
+
* §13.8 `validateBudgetMs` (read internally, never caller-tunable). Both failure classes are
|
|
160
|
+
* structured `internal`: an invalid output is a responder bug (§13.3/§13.7) whichever side
|
|
161
|
+
* detects it, and an over-budget output validation is the same bug class, never the caller's
|
|
162
|
+
* `bad-request`. A void output is `undefined`, validated as `null` against the void schema,
|
|
163
|
+
* mirroring the args side. */
|
|
164
|
+
export declare function assertOutputValid(validate: ValidateFunction, data: unknown): void;
|
|
165
|
+
//# sourceMappingURL=endpoint-envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoint-envelope.d.ts","sourceRoot":"","sources":["../src/endpoint-envelope.ts"],"names":[],"mappings":"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"}
|