@mcp-index/sdk 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/README.md +178 -0
  3. package/dist/src/ai.d.ts +96 -0
  4. package/dist/src/ai.d.ts.map +1 -0
  5. package/dist/src/ai.js +151 -0
  6. package/dist/src/ai.js.map +1 -0
  7. package/dist/src/lock/check.d.ts +143 -0
  8. package/dist/src/lock/check.d.ts.map +1 -0
  9. package/dist/src/lock/check.js +274 -0
  10. package/dist/src/lock/check.js.map +1 -0
  11. package/dist/src/lock/digest.d.ts +48 -0
  12. package/dist/src/lock/digest.d.ts.map +1 -0
  13. package/dist/src/lock/digest.js +234 -0
  14. package/dist/src/lock/digest.js.map +1 -0
  15. package/dist/src/lock/events.d.ts +82 -0
  16. package/dist/src/lock/events.d.ts.map +1 -0
  17. package/dist/src/lock/events.js +80 -0
  18. package/dist/src/lock/events.js.map +1 -0
  19. package/dist/src/lock/index.d.ts +20 -0
  20. package/dist/src/lock/index.d.ts.map +1 -0
  21. package/dist/src/lock/index.js +14 -0
  22. package/dist/src/lock/index.js.map +1 -0
  23. package/dist/src/lock/lock.d.ts +195 -0
  24. package/dist/src/lock/lock.d.ts.map +1 -0
  25. package/dist/src/lock/lock.js +1075 -0
  26. package/dist/src/lock/lock.js.map +1 -0
  27. package/dist/src/lock/pg.d.ts +150 -0
  28. package/dist/src/lock/pg.d.ts.map +1 -0
  29. package/dist/src/lock/pg.js +539 -0
  30. package/dist/src/lock/pg.js.map +1 -0
  31. package/dist/src/lock/rawList.d.ts +127 -0
  32. package/dist/src/lock/rawList.d.ts.map +1 -0
  33. package/dist/src/lock/rawList.js +561 -0
  34. package/dist/src/lock/rawList.js.map +1 -0
  35. package/dist/src/lock/signing.d.ts +35 -0
  36. package/dist/src/lock/signing.d.ts.map +1 -0
  37. package/dist/src/lock/signing.js +85 -0
  38. package/dist/src/lock/signing.js.map +1 -0
  39. package/dist/src/lock/store.d.ts +149 -0
  40. package/dist/src/lock/store.d.ts.map +1 -0
  41. package/dist/src/lock/store.js +459 -0
  42. package/dist/src/lock/store.js.map +1 -0
  43. package/dist/src/wrap.d.ts.map +1 -1
  44. package/dist/src/wrap.js +85 -3
  45. package/dist/src/wrap.js.map +1 -1
  46. package/package.json +31 -2
@@ -0,0 +1,539 @@
1
+ /**
2
+ * Postgres LockStore (`@mcp-index/sdk/lock/pg`). `pg` is an optional peer
3
+ * dependency: pass your own pool, or call createPgPool, which imports `pg` on
4
+ * first use.
5
+ *
6
+ * Tenancy. The tenant comes from an AuthenticatedPrincipal the host passes in
7
+ * after it has authenticated the caller. It is never read from a header or a
8
+ * request body. Row-level security on both tables (FORCE, so the table owner
9
+ * is held to it too) limits every row to one tenant. A superuser bypasses RLS;
10
+ * do not connect as one. There are two modes, chosen by the `tenancy` option
11
+ * and by which SQL the database administrator ran:
12
+ *
13
+ * app-scoped (the default; MIGRATION_SQL). Every transaction first sets
14
+ * `mcplock.tenant_id` with set_config(..., true), the parameterized form of
15
+ * SET LOCAL, from the principal, and the policies compare rows against that
16
+ * setting. This protects against query bugs in the store and the host: a
17
+ * statement that forgets its tenant filter still sees one tenant. It does
18
+ * not protect against a compromised application login. Any session may set
19
+ * the setting, so whoever holds a shared mcplock_runtime login can read
20
+ * every tenant, and whoever holds a shared mcplock_approver login can append
21
+ * to every tenant.
22
+ *
23
+ * role-bound (ROLE_BOUND_SQL after MIGRATION_SQL). The policies read the
24
+ * tenant from mcplock.tenant_logins, an admin-owned table keyed by
25
+ * session_user, through the SECURITY DEFINER function mcplock.login_tenant().
26
+ * The session setting is ignored; the store does not set it. Each tenant
27
+ * gets its own login roles (one in mcplock_runtime, one in mcplock_approver),
28
+ * and the administrator maps each login to its tenant. session_user cannot
29
+ * be changed by SET ROLE, and only a superuser can run SET SESSION
30
+ * AUTHORIZATION, so a login reads and writes its own tenant and nothing
31
+ * else. The store checks at the start of each transaction that the login is
32
+ * mapped to the principal's tenant and fails otherwise. Running
33
+ * MIGRATION_SQL again later leaves the role-bound policies in place, since
34
+ * it only creates policies that are missing.
35
+ *
36
+ * Roles. ROLE_SQL creates two NOLOGIN roles: `mcplock_runtime` may only
37
+ * SELECT, and is what hosts in lock mode connect as; `mcplock_approver` may
38
+ * also INSERT records and INSERT or UPDATE the head row, and is a separate
39
+ * credential held by the approval path. Neither role may UPDATE or DELETE a
40
+ * record.
41
+ *
42
+ * Integrity. Records are append-only and hash-chained: each record_hash is
43
+ * sha256 over the previous hash and the record's canonical fields, tenant
44
+ * included. The head row holds the last seq and hash, and an optional
45
+ * signature from a host-supplied signer. A load recomputes the chain, checks
46
+ * seq is contiguous from 1, that the head matches the last record, and that
47
+ * every contract hashes to its digest; any failure is lock-unreadable (or
48
+ * contract-mismatch, or lock-signature-invalid for the head signature). A load
49
+ * whose head seq is below one this store instance or its sequence store
50
+ * already accepted is lock-unreadable, which catches deletes of trailing
51
+ * records together with the head update. Deleting every row for a tenant
52
+ * cannot be told apart from a new tenant without that external anchor, so a
53
+ * store with a headVerifier must also be given a sequenceStore; without one
54
+ * every load is lock-signature-invalid (section 8.2, draft 1c).
55
+ * The chain has no key. Without a headVerifier, whoever can write the tables
56
+ * (the schema owner, a superuser) can rewrite a record and recompute every
57
+ * hash after it; only a signed head gives tamper evidence against them.
58
+ *
59
+ * Use role-bound before sharing one database across tenants that do not
60
+ * trust each other.
61
+ *
62
+ * Staleness (spec section 9.8). Each snapshot carries snapshotId (the head
63
+ * hash) and loadedAt. Hosts pass maxStalenessMs to the CallGate (LockOptions
64
+ * in rawList.ts); calls on a decision older than that are refused with
65
+ * `lock-stale` unless a reload succeeds, and a reload only shrinks the
66
+ * allow-list.
67
+ *
68
+ * SQL is parameterized throughout. A DSN never reaches a log or an error
69
+ * message unredacted; see redactDsn.
70
+ */
71
+ import { canonicalize, instructionsDigest, sha256Hex } from "./digest.js";
72
+ import { FrozenMap, LockError, identityKey, validateIdentity } from "./lock.js";
73
+ import { checkApprovalFields, prepareTool, sequenceFloor, snapshotOfError } from "./store.js";
74
+ export const SCHEMA_VERSION = 1;
75
+ export const GENESIS = "sha256:" + "0".repeat(64);
76
+ /** Tables, constraints and RLS policies. Idempotent; run once as the schema owner. */
77
+ export const MIGRATION_SQL = `
78
+ CREATE SCHEMA IF NOT EXISTS mcplock;
79
+
80
+ CREATE TABLE IF NOT EXISTS mcplock.records (
81
+ tenant_id text NOT NULL,
82
+ seq bigint NOT NULL CHECK (seq > 0),
83
+ op text NOT NULL CHECK (op IN ('approve', 'revoke')),
84
+ identity_key text NOT NULL,
85
+ tool_name text,
86
+ digest text CHECK (digest IS NULL OR digest ~ '^sha256:[0-9a-f]{64}$'),
87
+ contract bytea,
88
+ subject text NOT NULL,
89
+ via text NOT NULL CHECK (via IN ('pr', 'runtime', 'api', 'migrated')),
90
+ ref text,
91
+ at text NOT NULL,
92
+ prev_hash text NOT NULL,
93
+ record_hash text NOT NULL,
94
+ PRIMARY KEY (tenant_id, seq)
95
+ );
96
+
97
+ -- Draft 1c: digest of the approved top-level _meta. Added in place on older schemas.
98
+ ALTER TABLE mcplock.records ADD COLUMN IF NOT EXISTS meta_digest text
99
+ CHECK (meta_digest IS NULL OR meta_digest ~ '^sha256:[0-9a-f]{64}$');
100
+
101
+ CREATE TABLE IF NOT EXISTS mcplock.heads (
102
+ tenant_id text PRIMARY KEY,
103
+ seq bigint NOT NULL CHECK (seq >= 0),
104
+ head_hash text NOT NULL,
105
+ signature text,
106
+ updated_at timestamptz NOT NULL DEFAULT now()
107
+ );
108
+
109
+ ALTER TABLE mcplock.records ENABLE ROW LEVEL SECURITY;
110
+ ALTER TABLE mcplock.records FORCE ROW LEVEL SECURITY;
111
+ ALTER TABLE mcplock.heads ENABLE ROW LEVEL SECURITY;
112
+ ALTER TABLE mcplock.heads FORCE ROW LEVEL SECURITY;
113
+
114
+ DO $$
115
+ BEGIN
116
+ IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname = 'mcplock' AND tablename = 'records' AND policyname = 'tenant_records') THEN
117
+ CREATE POLICY tenant_records ON mcplock.records
118
+ USING (tenant_id = current_setting('mcplock.tenant_id', true))
119
+ WITH CHECK (tenant_id = current_setting('mcplock.tenant_id', true));
120
+ END IF;
121
+ IF NOT EXISTS (SELECT 1 FROM pg_policies WHERE schemaname = 'mcplock' AND tablename = 'heads' AND policyname = 'tenant_heads') THEN
122
+ CREATE POLICY tenant_heads ON mcplock.heads
123
+ USING (tenant_id = current_setting('mcplock.tenant_id', true))
124
+ WITH CHECK (tenant_id = current_setting('mcplock.tenant_id', true));
125
+ END IF;
126
+ END $$;
127
+ `;
128
+ /**
129
+ * The two roles. Run once by a database administrator, then grant each role
130
+ * to its own login: hosts get mcplock_runtime, the approval service gets
131
+ * mcplock_approver. No role gets UPDATE or DELETE on records.
132
+ */
133
+ export const ROLE_SQL = `
134
+ DO $$
135
+ BEGIN
136
+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'mcplock_runtime') THEN
137
+ CREATE ROLE mcplock_runtime NOLOGIN;
138
+ END IF;
139
+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'mcplock_approver') THEN
140
+ CREATE ROLE mcplock_approver NOLOGIN;
141
+ END IF;
142
+ END $$;
143
+ GRANT USAGE ON SCHEMA mcplock TO mcplock_runtime, mcplock_approver;
144
+ GRANT SELECT ON mcplock.records, mcplock.heads TO mcplock_runtime;
145
+ GRANT SELECT, INSERT ON mcplock.records TO mcplock_approver;
146
+ GRANT SELECT, INSERT, UPDATE ON mcplock.heads TO mcplock_approver;
147
+ `;
148
+ /**
149
+ * Role-bound tenancy. Run once by the schema owner after MIGRATION_SQL and
150
+ * ROLE_SQL, then map each login with
151
+ * INSERT INTO mcplock.tenant_logins (login, tenant_id) VALUES ('<login>', '<tenant>');
152
+ * Neither mcplock role gets any privilege on the mapping table; the policies
153
+ * read it only through mcplock.login_tenant(). Replacing the policies inside
154
+ * one transaction leaves no moment with the old ones gone and the new ones
155
+ * missing, and FORCE RLS with no policy denies every row anyway.
156
+ */
157
+ export const ROLE_BOUND_SQL = `
158
+ BEGIN;
159
+ CREATE TABLE IF NOT EXISTS mcplock.tenant_logins (
160
+ login name PRIMARY KEY,
161
+ tenant_id text NOT NULL CHECK (tenant_id ~ '^[!-~]{1,128}$')
162
+ );
163
+ REVOKE ALL ON mcplock.tenant_logins FROM PUBLIC;
164
+
165
+ CREATE OR REPLACE FUNCTION mcplock.login_tenant() RETURNS text
166
+ LANGUAGE sql STABLE SECURITY DEFINER
167
+ SET search_path = pg_catalog, pg_temp
168
+ AS $$ SELECT tenant_id FROM mcplock.tenant_logins WHERE login = session_user $$;
169
+ REVOKE ALL ON FUNCTION mcplock.login_tenant() FROM PUBLIC;
170
+ GRANT EXECUTE ON FUNCTION mcplock.login_tenant() TO mcplock_runtime, mcplock_approver;
171
+
172
+ DROP POLICY IF EXISTS tenant_records ON mcplock.records;
173
+ CREATE POLICY tenant_records ON mcplock.records
174
+ USING (tenant_id = mcplock.login_tenant())
175
+ WITH CHECK (tenant_id = mcplock.login_tenant());
176
+ DROP POLICY IF EXISTS tenant_heads ON mcplock.heads;
177
+ CREATE POLICY tenant_heads ON mcplock.heads
178
+ USING (tenant_id = mcplock.login_tenant())
179
+ WITH CHECK (tenant_id = mcplock.login_tenant());
180
+ COMMIT;
181
+ `;
182
+ /** Replace any password or userinfo in a connection string before it is shown. */
183
+ export function redactDsn(dsn) {
184
+ return dsn
185
+ .replace(/^([a-z][a-z0-9+.-]*:\/\/)[^/?#]*@/i, "$1***@")
186
+ .replace(/([?&](?:password|sslpassword|passfile|sslkey)=)[^&#]*/gi, "$1***")
187
+ .replace(/(\bpassword\s*=\s*)('[^']*'|\S+)/gi, "$1***");
188
+ }
189
+ function scrub(message, dsn) {
190
+ let out = message;
191
+ if (dsn)
192
+ out = out.split(dsn).join(redactDsn(dsn));
193
+ return redactDsn(out);
194
+ }
195
+ /** Build a pg Pool, importing `pg` on first use. Errors never carry the DSN. */
196
+ export async function createPgPool(connectionString, extra = {}) {
197
+ let mod;
198
+ try {
199
+ const name = "pg";
200
+ mod = (await import(name));
201
+ }
202
+ catch {
203
+ throw new Error("the optional peer dependency `pg` is not installed");
204
+ }
205
+ const pg = (mod.default ?? mod);
206
+ const pool = new pg.Pool({ ...extra, connectionString });
207
+ // pg emits idle-client errors on the pool; keep the DSN out of them.
208
+ pool.on?.("error", () => undefined);
209
+ return pool;
210
+ }
211
+ const TENANT = /^[\x21-\x7e]{1,128}$/;
212
+ function recordHash(tenant, r) {
213
+ // metaDigest joins the hashed fields only when set, so records written
214
+ // before draft 1c keep the hash they were written with.
215
+ const fields = {
216
+ tenant,
217
+ seq: r.seq,
218
+ op: r.op,
219
+ identityKey: r.identityKey,
220
+ tool: r.toolName,
221
+ digest: r.digest,
222
+ contract: r.contract === null ? null : sha256Hex(r.contract),
223
+ subject: r.subject,
224
+ via: r.via,
225
+ ref: r.ref,
226
+ at: r.at,
227
+ };
228
+ if (r.metaDigest !== null)
229
+ fields.metaDigest = r.metaDigest;
230
+ const body = canonicalize(fields);
231
+ return "sha256:" + sha256Hex(`${r.prevHash}\n${body}`);
232
+ }
233
+ function headPayload(tenant, seq, head) {
234
+ return Buffer.from(canonicalize({ tenant, seq, head }), "utf8");
235
+ }
236
+ function toRow(raw) {
237
+ const contract = raw.contract;
238
+ return {
239
+ seq: Number(raw.seq),
240
+ op: raw.op,
241
+ identityKey: String(raw.identity_key),
242
+ toolName: raw.tool_name === null ? null : String(raw.tool_name),
243
+ digest: raw.digest === null ? null : String(raw.digest),
244
+ contract: contract === null || contract === undefined ? null : Buffer.from(contract),
245
+ metaDigest: raw.meta_digest === null || raw.meta_digest === undefined ? null : String(raw.meta_digest),
246
+ subject: String(raw.subject),
247
+ via: raw.via,
248
+ ref: raw.ref === null ? null : String(raw.ref),
249
+ at: String(raw.at),
250
+ prevHash: String(raw.prev_hash),
251
+ recordHash: String(raw.record_hash),
252
+ };
253
+ }
254
+ export class PgLockStore {
255
+ #pool;
256
+ #tenant;
257
+ #subject;
258
+ #signer;
259
+ #verifier;
260
+ #dsn;
261
+ #now;
262
+ #tenancy;
263
+ #sequences;
264
+ #lastSeq = 0;
265
+ constructor(options) {
266
+ const p = options.principal;
267
+ if (p === null || typeof p !== "object" || typeof p.tenantId !== "string" || !TENANT.test(p.tenantId)) {
268
+ throw new Error("principal.tenantId must be 1-128 printable ASCII characters");
269
+ }
270
+ if (typeof p.subject !== "string" || p.subject.length === 0)
271
+ throw new Error("principal.subject is required");
272
+ this.#pool = options.pool;
273
+ this.#tenant = p.tenantId;
274
+ this.#subject = p.subject;
275
+ this.#signer = options.headSigner;
276
+ this.#verifier = options.headVerifier;
277
+ this.#dsn = options.dsn;
278
+ this.#now = options.now ?? Date.now;
279
+ this.#sequences = options.sequenceStore;
280
+ const tenancy = options.tenancy ?? "app-scoped";
281
+ if (tenancy !== "app-scoped" && tenancy !== "role-bound")
282
+ throw new Error(`unknown tenancy ${String(tenancy)}`);
283
+ this.#tenancy = tenancy;
284
+ }
285
+ async #tx(mode, fn) {
286
+ let client;
287
+ try {
288
+ client = await this.#pool.connect();
289
+ }
290
+ catch (exc) {
291
+ throw new Error(`lock store unreachable: ${scrub(exc.message ?? "", this.#dsn)}`);
292
+ }
293
+ try {
294
+ await client.query(mode === "read" ? "BEGIN ISOLATION LEVEL REPEATABLE READ READ ONLY" : "BEGIN ISOLATION LEVEL SERIALIZABLE");
295
+ if (this.#tenancy === "app-scoped") {
296
+ await client.query("SELECT set_config('mcplock.tenant_id', $1, true)", [this.#tenant]);
297
+ }
298
+ else {
299
+ const bound = (await client.query("SELECT mcplock.login_tenant() AS tenant")).rows[0]?.tenant;
300
+ if (bound === null || bound === undefined)
301
+ throw new Error("role-bound tenancy: this login is not mapped to a tenant");
302
+ if (bound !== this.#tenant)
303
+ throw new Error("role-bound tenancy: this login is mapped to a different tenant");
304
+ }
305
+ const out = await fn(client);
306
+ await client.query("COMMIT");
307
+ client.release();
308
+ return out;
309
+ }
310
+ catch (exc) {
311
+ try {
312
+ await client.query("ROLLBACK");
313
+ }
314
+ catch {
315
+ // connection may be gone
316
+ }
317
+ client.release(true);
318
+ if (exc instanceof LockError)
319
+ throw exc;
320
+ const err = new Error(scrub(exc.message ?? String(exc), this.#dsn));
321
+ err.code = exc.code;
322
+ throw err;
323
+ }
324
+ }
325
+ async #readState(c, forUpdate) {
326
+ const headRes = await c.query(`SELECT seq, head_hash, signature FROM mcplock.heads WHERE tenant_id = $1${forUpdate ? " FOR UPDATE" : ""}`, [this.#tenant]);
327
+ const h = headRes.rows[0];
328
+ const head = h === undefined
329
+ ? null
330
+ : { seq: Number(h.seq), hash: String(h.head_hash), signature: h.signature === null ? null : String(h.signature) };
331
+ const recRes = await c.query(`SELECT seq, op, identity_key, tool_name, digest, contract, meta_digest, subject, via, ref, at, prev_hash, record_hash
332
+ FROM mcplock.records WHERE tenant_id = $1 ORDER BY seq`, [this.#tenant]);
333
+ return { head, rows: recRes.rows.map(toRow) };
334
+ }
335
+ /** Verify the chain and fold it into a Lock. Throws LockError. */
336
+ async #fold(head, rows) {
337
+ if (head === null) {
338
+ if (rows.length > 0)
339
+ throw new LockError("lock-unreadable", "records without a head row");
340
+ if (this.#lastSeq > 0)
341
+ throw new LockError("lock-unreadable", `head missing; last accepted seq ${this.#lastSeq}`);
342
+ if (this.#verifier !== undefined)
343
+ throw new LockError("lock-signature-invalid", "no signed head");
344
+ const empty = Object.freeze({ shards: new FrozenMap([]), aliases: new FrozenMap([]), sequence: 0 });
345
+ return { lock: empty, snapshotId: GENESIS, seq: 0 };
346
+ }
347
+ let prev = GENESIS;
348
+ const shards = new Map();
349
+ rows.forEach((r, i) => {
350
+ if (r.seq !== i + 1)
351
+ throw new LockError("lock-unreadable", `record seq gap at ${i + 1}`);
352
+ if (r.prevHash !== prev)
353
+ throw new LockError("lock-unreadable", `record ${r.seq} does not chain`);
354
+ const { recordHash: stored, ...rest } = r;
355
+ if (recordHash(this.#tenant, rest) !== stored)
356
+ throw new LockError("lock-unreadable", `record ${r.seq} hash mismatch`);
357
+ prev = stored;
358
+ let identity;
359
+ try {
360
+ identity = validateIdentity(JSON.parse(r.identityKey));
361
+ }
362
+ catch (exc) {
363
+ if (exc instanceof LockError)
364
+ throw exc;
365
+ throw new LockError("lock-unreadable", `record ${r.seq} identity is not JSON`);
366
+ }
367
+ if (identityKey(identity) !== r.identityKey)
368
+ throw new LockError("lock-unreadable", `record ${r.seq} identity key not canonical`);
369
+ let shard = shards.get(r.identityKey);
370
+ if (shard === undefined) {
371
+ shard = { identity, instructions: null, tools: new Map() };
372
+ shards.set(r.identityKey, shard);
373
+ }
374
+ if (r.op === "approve") {
375
+ if (r.digest === null)
376
+ throw new LockError("lock-unreadable", `record ${r.seq} approves without a digest`);
377
+ if (r.toolName === null) {
378
+ shard.instructions = r.digest;
379
+ return;
380
+ }
381
+ if (r.contract === null || "sha256:" + sha256Hex(r.contract) !== r.digest) {
382
+ throw new LockError("contract-mismatch", `record ${r.seq} contract differs from digest`);
383
+ }
384
+ const approval = r.ref === null
385
+ ? Object.freeze({ subject: r.subject, at: r.at, via: r.via })
386
+ : Object.freeze({ subject: r.subject, at: r.at, via: r.via, ref: r.ref });
387
+ if (r.metaDigest !== null && !/^sha256:[0-9a-f]{64}$/.test(r.metaDigest)) {
388
+ throw new LockError("lock-unreadable", `record ${r.seq} metaDigest malformed`);
389
+ }
390
+ const contract = `sha256-${r.digest.slice(7)}.json`;
391
+ shard.tools.set(r.toolName, r.metaDigest === null
392
+ ? Object.freeze({ digest: r.digest, contract, approval })
393
+ : Object.freeze({ digest: r.digest, contract, metaDigest: r.metaDigest, approval }));
394
+ }
395
+ else if (r.toolName === null) {
396
+ shard.instructions = null;
397
+ }
398
+ else {
399
+ shard.tools.delete(r.toolName);
400
+ }
401
+ });
402
+ const lastSeq = rows.length;
403
+ if (head.seq !== lastSeq || head.hash !== prev)
404
+ throw new LockError("lock-unreadable", "head does not match the last record");
405
+ if (head.seq < this.#lastSeq)
406
+ throw new LockError("lock-unreadable", `head seq ${head.seq} < last accepted ${this.#lastSeq}`);
407
+ if (this.#verifier !== undefined) {
408
+ let ok = false;
409
+ try {
410
+ ok = head.signature !== null && (await this.#verifier.verify(headPayload(this.#tenant, head.seq, head.hash), head.signature));
411
+ }
412
+ catch {
413
+ ok = false;
414
+ }
415
+ if (!ok)
416
+ throw new LockError("lock-signature-invalid", "head signature missing or invalid");
417
+ }
418
+ const frozen = new Map();
419
+ for (const [key, s] of shards) {
420
+ frozen.set(key, Object.freeze({ identity: s.identity, attributes: null, instructions: s.instructions, tools: new FrozenMap(s.tools) }));
421
+ }
422
+ const lock = Object.freeze({ shards: new FrozenMap(frozen), aliases: new FrozenMap([]), sequence: head.seq });
423
+ return { lock, snapshotId: head.hash, seq: head.seq };
424
+ }
425
+ async loadSnapshot() {
426
+ const loadedAt = this.#now();
427
+ if (this.#verifier !== undefined && this.#sequences === undefined) {
428
+ return snapshotOfError(new LockError("lock-signature-invalid", "a head verifier is configured but the host config names no sequence store; rollback cannot be refused"), loadedAt);
429
+ }
430
+ let stored = null;
431
+ try {
432
+ stored = this.#sequences?.read() ?? null;
433
+ }
434
+ catch (exc) {
435
+ return snapshotOfError(new LockError("lock-signature-invalid", exc.message ?? "sequence unreadable"), loadedAt);
436
+ }
437
+ this.#lastSeq = sequenceFloor(stored, this.#lastSeq) ?? 0;
438
+ const state = await this.#tx("read", (c) => this.#readState(c, false));
439
+ try {
440
+ const { lock, snapshotId, seq } = await this.#fold(state.head, state.rows);
441
+ if (seq > this.#lastSeq) {
442
+ this.#lastSeq = seq;
443
+ try {
444
+ this.#sequences?.write(seq);
445
+ }
446
+ catch {
447
+ // The in-memory floor still holds; see FileLockStore.loadSnapshot.
448
+ }
449
+ }
450
+ return Object.freeze({ snapshotId, loadedAt, lock });
451
+ }
452
+ catch (exc) {
453
+ if (exc instanceof LockError)
454
+ return snapshotOfError(exc, loadedAt);
455
+ throw exc;
456
+ }
457
+ }
458
+ async #append(identity, record, precondition) {
459
+ const ident = validateIdentity(identity);
460
+ const key = identityKey(ident);
461
+ await this.#tx("write", async (c) => {
462
+ await c.query("INSERT INTO mcplock.heads (tenant_id, seq, head_hash) VALUES ($1, 0, $2) ON CONFLICT (tenant_id) DO NOTHING", [this.#tenant, GENESIS]);
463
+ const state = await this.#readState(c, true);
464
+ // Never extend a chain that does not verify. A fresh head row has seq 0
465
+ // and no records; fold accepts that as empty only without a verifier, so
466
+ // treat it as empty here directly.
467
+ const fresh = state.head !== null && state.head.seq === 0 && state.rows.length === 0;
468
+ const folded = fresh
469
+ ? { lock: Object.freeze({ shards: new FrozenMap([]), aliases: new FrozenMap([]), sequence: 0 }), seq: 0 }
470
+ : await this.#fold(state.head, state.rows);
471
+ precondition(folded.lock);
472
+ const prevHash = fresh ? GENESIS : state.head.hash;
473
+ const seq = folded.seq + 1;
474
+ const row = {
475
+ seq,
476
+ op: record.op,
477
+ identityKey: key,
478
+ toolName: record.toolName,
479
+ digest: record.digest,
480
+ contract: record.contract,
481
+ metaDigest: record.metaDigest,
482
+ subject: record.approval.subject,
483
+ via: record.approval.via,
484
+ ref: record.approval.ref ?? null,
485
+ at: record.approval.at,
486
+ prevHash,
487
+ };
488
+ const hash = recordHash(this.#tenant, row);
489
+ await c.query(`INSERT INTO mcplock.records
490
+ (tenant_id, seq, op, identity_key, tool_name, digest, contract, meta_digest, subject, via, ref, at, prev_hash, record_hash)
491
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)`, [
492
+ this.#tenant,
493
+ seq,
494
+ row.op,
495
+ key,
496
+ row.toolName,
497
+ row.digest,
498
+ row.contract,
499
+ row.metaDigest,
500
+ row.subject,
501
+ row.via,
502
+ row.ref,
503
+ row.at,
504
+ prevHash,
505
+ hash,
506
+ ]);
507
+ const signature = this.#signer ? await this.#signer.sign(headPayload(this.#tenant, seq, hash)) : null;
508
+ await c.query("UPDATE mcplock.heads SET seq = $2, head_hash = $3, signature = $4, updated_at = now() WHERE tenant_id = $1", [this.#tenant, seq, hash, signature]);
509
+ });
510
+ }
511
+ async approve(request) {
512
+ // The subject is the authenticated principal, whatever the request says.
513
+ const approval = checkApprovalFields(request, this.#subject);
514
+ if ("tool" in request) {
515
+ const p = prepareTool(request.tool);
516
+ await this.#append(request.identity, { op: "approve", toolName: p.name, digest: p.digest, contract: p.body, metaDigest: p.metaDigest, approval }, () => undefined);
517
+ return;
518
+ }
519
+ const digest = instructionsDigest(request.instructions);
520
+ if (digest === null)
521
+ throw new Error("instructions have no digest; they cannot be approved");
522
+ await this.#append(request.identity, { op: "approve", toolName: null, digest, contract: null, metaDigest: null, approval }, () => undefined);
523
+ }
524
+ async revoke(request) {
525
+ const ident = validateIdentity(request.identity);
526
+ const key = identityKey(ident);
527
+ const approval = checkApprovalFields({ identity: ident, via: "api" }, this.#subject);
528
+ const toolName = "tool" in request ? request.tool : null;
529
+ await this.#append(ident, { op: "revoke", toolName, digest: null, contract: null, metaDigest: null, approval }, (lock) => {
530
+ const shard = lock.shards.get(key);
531
+ if (shard === undefined)
532
+ throw new Error("no approvals for this identity");
533
+ if (toolName === null ? shard.instructions === null : !shard.tools.has(toolName)) {
534
+ throw new Error(toolName === null ? "instructions are not approved" : `tool ${JSON.stringify(toolName)} is not approved`);
535
+ }
536
+ });
537
+ }
538
+ }
539
+ //# sourceMappingURL=pg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pg.js","sourceRoot":"","sources":["../../../src/lock/pg.ts"],"names":[],"mappings":"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"}