@suveren/gateway 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2009,8 +2009,8 @@ app.get("/active-authorizations", authGuard, async (_req, res) => {
2009
2009
  });
2010
2010
  app.post("/gate-content", jsonParser, authGuard, async (req, res) => {
2011
2011
  try {
2012
- const { frameHash, boundsHash, contextHash, context, path, gateContent } = req.body;
2013
- await pushGateContent({ frameHash, boundsHash, contextHash, context, path, gateContent });
2012
+ const { authorizationId, boundsHash, contextHash, context, path, gateContent } = req.body;
2013
+ await pushGateContent({ authorizationId, boundsHash, contextHash, context, path, gateContent });
2014
2014
  res.json({ ok: true });
2015
2015
  } catch (err) {
2016
2016
  console.error("[Control Plane] Gate content forward error:", err);
@@ -63,8 +63,8 @@ var SPClient = class {
63
63
  /**
64
64
  * Get all attestations for a frame hash.
65
65
  */
66
- async getAttestations(frameHash) {
67
- const res = await this.fetch(`/api/attestations?frame_hash=${encodeURIComponent(frameHash)}`);
66
+ async getAttestations(authorizationId) {
67
+ const res = await this.fetch(`/api/attestations?authorization_id=${encodeURIComponent(authorizationId)}`);
68
68
  if (!res.ok) throw new Error(`SP attestations request failed: ${res.status}`);
69
69
  return res.json();
70
70
  }
@@ -138,7 +138,7 @@ var SPClient = class {
138
138
  const res = await this.fetch("/api/proposals", {
139
139
  method: "POST",
140
140
  body: JSON.stringify({
141
- frame_hash: data.frameHash,
141
+ authorization_id: data.authorizationId,
142
142
  profile_id: data.profileId,
143
143
  path: data.path,
144
144
  pending_domains: data.pendingDomains,
@@ -157,11 +157,11 @@ var SPClient = class {
157
157
  return res.json();
158
158
  }
159
159
  /**
160
- * Phase 6: Fetch frame metadata for an authority by its boundsHash / frameHash.
160
+ * Phase 6: Fetch the authorization summary by its per-ceremony id.
161
161
  * Used to read aboveCap and approversFrozen at action time.
162
162
  */
163
- async getFrameMetadata(frameHash) {
164
- const res = await this.fetch(`/api/as/frame/${encodeURIComponent(frameHash)}`);
163
+ async getAuthorizationSummary(authorizationId) {
164
+ const res = await this.fetch(`/api/authorizations/${encodeURIComponent(authorizationId)}`);
165
165
  if (res.status === 404) return null;
166
166
  if (!res.ok) return null;
167
167
  return res.json();
@@ -228,16 +228,19 @@ var AttestationCache = class {
228
228
  /**
229
229
  * Fetch attestation data from SP for a frame hash and cache it.
230
230
  */
231
- async syncAuthorization(frameHash) {
232
- const result = await this.spClient.getAttestations(frameHash);
233
- if (!result.profile_id || !result.frame) return null;
231
+ async syncAuthorization(authorizationId) {
232
+ const result = await this.spClient.getAttestations(authorizationId);
233
+ if (!result.profile_id) return null;
234
234
  if (result.revoked) {
235
- this.authorizations.delete(frameHash);
236
- if (result.frame_hash) this.authorizations.delete(result.frame_hash);
235
+ this.authorizations.delete(authorizationId);
237
236
  return null;
238
237
  }
239
- const storageHash = result.frame_hash ?? result.bounds_hash;
240
- const bounds = result.bounds ?? result.frame;
238
+ if (!result.authorization_id) {
239
+ throw new Error(
240
+ "Authority Server response lacks authorization_id \u2014 the AS predates per-ceremony identity. Update the Authority Server (lockstep deploy)."
241
+ );
242
+ }
243
+ const bounds = result.bounds ?? result.frame ?? {};
241
244
  let signedCommitmentMode;
242
245
  let subjects;
243
246
  const firstBlob = result.attestations[0]?.blob;
@@ -251,12 +254,12 @@ var AttestationCache = class {
251
254
  }
252
255
  }
253
256
  const auth = {
254
- frameHash: storageHash,
257
+ authorizationId: result.authorization_id,
255
258
  boundsHash: result.bounds_hash,
256
259
  // content fingerprint (undefined for pre-v0.4 records)
257
260
  contextHash: result.context_hash,
258
261
  profileId: result.profile_id,
259
- path: result.path ?? result.profile_id,
262
+ path: result.profile_id,
260
263
  frame: bounds,
261
264
  // compat alias
262
265
  bounds: result.bounds,
@@ -273,7 +276,7 @@ var AttestationCache = class {
273
276
  subjects,
274
277
  complete: result.complete
275
278
  };
276
- this.authorizations.set(auth.frameHash ?? auth.path, auth);
279
+ this.authorizations.set(auth.authorizationId, auth);
277
280
  return auth;
278
281
  }
279
282
  /**
@@ -302,7 +305,7 @@ var AttestationCache = class {
302
305
  * Cache an authorization directly (e.g., from SP response after creation).
303
306
  */
304
307
  cacheAuthorization(auth) {
305
- this.authorizations.set(auth.frameHash ?? auth.path, auth);
308
+ this.authorizations.set(auth.authorizationId, auth);
306
309
  }
307
310
  /**
308
311
  * Remove a cached authorization by path. Called when the SP reports the
@@ -717,9 +720,9 @@ var SharedState = class {
717
720
  this.executionLog = new ExecutionLog(gateStorePath);
718
721
  this.gatekeeper = new MCPGatekeeper(this.cache, this.executionLog);
719
722
  }
720
- setGateContent(path, frameHash, profileId, content, opts) {
721
- this.gateStore.set(path, {
722
- frameHash,
723
+ setGateContent(path, authorizationId, profileId, content, opts) {
724
+ this.gateStore.set(authorizationId, {
725
+ authorizationId,
723
726
  boundsHash: opts?.boundsHash,
724
727
  contextHash: opts?.contextHash,
725
728
  path,
@@ -739,7 +742,7 @@ var SharedState = class {
739
742
  getEnrichedAuthorizations() {
740
743
  const authorizations = this.cache.getAllAuthorizations();
741
744
  return authorizations.map((auth) => {
742
- const gateEntry = (auth.frameHash ? this.gateStore.get(auth.frameHash) : null) ?? (auth.boundsHash ? this.gateStore.get(auth.boundsHash) : null) ?? this.gateStore.get(auth.path) ?? null;
745
+ const gateEntry = this.gateStore.get(auth.authorizationId) ?? null;
743
746
  return {
744
747
  ...auth,
745
748
  gateContent: gateEntry?.gateContent ?? null,
@@ -1134,14 +1137,14 @@ function createGatedToolHandler(tool, integrationManager2, state2) {
1134
1137
  }
1135
1138
  const errors = [];
1136
1139
  for (const auth of matchingAuths) {
1137
- const { result } = await state2.gatekeeper.verifyExecution(auth.frameHash ?? auth.path, execution, {
1140
+ const { result } = await state2.gatekeeper.verifyExecution(auth.authorizationId, execution, {
1138
1141
  bounds: auth.bounds,
1139
1142
  context: auth.context
1140
1143
  });
1141
1144
  if (!result.approved) {
1142
1145
  }
1143
1146
  if (result.approved) {
1144
- const authHash = auth.frameHash ?? auth.boundsHash;
1147
+ const authzId = auth.authorizationId;
1145
1148
  if (isCommitmentDowngrade(auth)) {
1146
1149
  return {
1147
1150
  content: [{
@@ -1155,7 +1158,7 @@ function createGatedToolHandler(tool, integrationManager2, state2) {
1155
1158
  try {
1156
1159
  const enrichedArgs = await attachImagePreview(args);
1157
1160
  const { proposal } = await state2.spClient.submitProposal({
1158
- frameHash: authHash,
1161
+ authorizationId: authzId,
1159
1162
  profileId: auth.profileId,
1160
1163
  path: auth.path,
1161
1164
  pendingDomains: auth.deferredCommitmentDomains,
@@ -1187,10 +1190,9 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
1187
1190
  }
1188
1191
  const binding = computeContentBinding(auth.profileId, tool, args);
1189
1192
  const { receipt } = await state2.spClient.postReceipt({
1190
- // v0.5: send the bare content address; the AS reconstructs the
1191
- // per-user storage key. Fall back to frameHash only for legacy
1192
- // (pre-v0.4) records that predate bounds_hash.
1193
- boundsHash: auth.boundsHash ?? authHash,
1193
+ authorizationId: authzId,
1194
+ // Optional cross-check the AS fails closed on a mismatch.
1195
+ boundsHash: auth.boundsHash,
1194
1196
  profileId: auth.profileId,
1195
1197
  action: tool.namespacedName,
1196
1198
  actionType,
@@ -1206,20 +1208,20 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
1206
1208
  let pendingApprovers = spBody.approvers ?? [];
1207
1209
  if (pendingApprovers.length === 0) {
1208
1210
  try {
1209
- const frameMeta = await state2.spClient.getFrameMetadata(authHash);
1210
- if (frameMeta?.approversFrozen) {
1211
- pendingApprovers = frameMeta.approversFrozen;
1211
+ const summary = await state2.spClient.getAuthorizationSummary(authzId);
1212
+ if (summary?.approvers_frozen) {
1213
+ pendingApprovers = summary.approvers_frozen;
1212
1214
  }
1213
- if (frameMeta?.createdBy) {
1214
- pendingApprovers = [frameMeta.createdBy, ...pendingApprovers];
1215
+ if (summary?.created_by) {
1216
+ pendingApprovers = [summary.created_by, ...pendingApprovers];
1215
1217
  }
1216
1218
  } catch {
1217
1219
  }
1218
1220
  } else {
1219
1221
  try {
1220
- const frameMeta = await state2.spClient.getFrameMetadata(authHash);
1221
- if (frameMeta?.createdBy) {
1222
- pendingApprovers = [frameMeta.createdBy, ...pendingApprovers];
1222
+ const summary = await state2.spClient.getAuthorizationSummary(authzId);
1223
+ if (summary?.created_by) {
1224
+ pendingApprovers = [summary.created_by, ...pendingApprovers];
1223
1225
  }
1224
1226
  } catch {
1225
1227
  }
@@ -1228,7 +1230,7 @@ Proposal ID: ${proposal.id}. Check status with check-pending-commitments(proposa
1228
1230
  try {
1229
1231
  const enrichedArgs = await attachImagePreview(args);
1230
1232
  const { proposal } = await state2.spClient.submitProposal({
1231
- frameHash: authHash,
1233
+ authorizationId: authzId,
1232
1234
  profileId: auth.profileId,
1233
1235
  path: auth.path,
1234
1236
  pendingDomains: [],
@@ -1259,7 +1261,7 @@ Proposal ID: ${proposal.id}. Use check-pending-commitments to track status.`
1259
1261
  }
1260
1262
  if (err instanceof SPReceiptError && err.statusCode === 403) {
1261
1263
  if (/revoked/i.test(err.message)) {
1262
- state2.cache.invalidate(auth.frameHash ?? auth.path);
1264
+ state2.cache.invalidate(auth.authorizationId);
1263
1265
  }
1264
1266
  return {
1265
1267
  content: [{ type: "text", text: `Blocked by SP: ${err.message}` }],
@@ -1425,8 +1427,8 @@ function listAuthorizationsHandler(state2, integrationManager2, contextDir) {
1425
1427
  const reviewDomains = auth.deferredCommitmentDomains ?? [];
1426
1428
  output2.push(reviewDomains.length > 0 ? " Mode: review \u2014 each action requires your approval before it runs (a proposal is created, not executed)" : " Mode: automatic \u2014 actions run immediately within bounds");
1427
1429
  try {
1428
- const meta = await state2.spClient.getFrameMetadata(auth.frameHash ?? auth.boundsHash ?? auth.path);
1429
- if (meta?.aboveCap) {
1430
+ const meta = await state2.spClient.getAuthorizationSummary(auth.authorizationId);
1431
+ if (meta?.above_cap) {
1430
1432
  output2.push(" \u26A0 Above team cap \u2014 actions here require approval even within these bounds.");
1431
1433
  }
1432
1434
  } catch {
@@ -1638,10 +1640,9 @@ async function executeCommitted(proposal, state2, integrationManager2) {
1638
1640
  }
1639
1641
  let receiptId;
1640
1642
  try {
1641
- const boundsHash = proposal.frameHash.split(":").slice(0, 2).join(":");
1642
1643
  const binding = computeContentBinding(proposal.profileId, discovered, proposal.toolArgs);
1643
1644
  const { receipt } = await state2.spClient.postReceipt({
1644
- boundsHash,
1645
+ authorizationId: proposal.authorizationId,
1645
1646
  profileId: proposal.profileId,
1646
1647
  action: proposal.tool,
1647
1648
  actionType: proposalActionType,
@@ -2501,18 +2502,17 @@ app.post("/internal/configure", internalOnly, (req, res) => {
2501
2502
  });
2502
2503
  app.post("/internal/gate-content", internalOnly, async (req, res) => {
2503
2504
  try {
2504
- const { frameHash, boundsHash, contextHash, context, path: rawPath, gateContent } = req.body;
2505
- const storageHash = frameHash ?? boundsHash;
2505
+ const { authorizationId, boundsHash, contextHash, context, path: rawPath, gateContent } = req.body;
2506
2506
  const hasIntent = !!gateContent?.intent;
2507
2507
  const legacy = gateContent;
2508
2508
  const hasLegacy = !!legacy?.problem && !!legacy?.objective && !!legacy?.tradeoffs;
2509
- if (!storageHash || !hasIntent && !hasLegacy) {
2510
- res.status(400).json({ error: "Missing required fields: frameHash (or boundsHash), gateContent.{intent} or gateContent.{problem,objective,tradeoffs}" });
2509
+ if (!authorizationId || !hasIntent && !hasLegacy) {
2510
+ res.status(400).json({ error: "Missing required fields: authorizationId, gateContent.{intent} or gateContent.{problem,objective,tradeoffs}" });
2511
2511
  return;
2512
2512
  }
2513
- const auth = await state.cache.syncAuthorization(storageHash);
2513
+ const auth = await state.cache.syncAuthorization(authorizationId);
2514
2514
  if (!auth) {
2515
- res.status(404).json({ error: `No attestation found for frame hash ${storageHash}` });
2515
+ res.status(404).json({ error: `No authorization found for ${authorizationId}` });
2516
2516
  return;
2517
2517
  }
2518
2518
  const verification = verifyGateContentHashes(gateContent, auth);
@@ -2520,8 +2520,8 @@ app.post("/internal/gate-content", internalOnly, async (req, res) => {
2520
2520
  res.status(400).json({ error: "Gate content hash mismatch", details: verification.errors });
2521
2521
  return;
2522
2522
  }
2523
- const path = rawPath || storageHash;
2524
- state.setGateContent(path, storageHash, auth.profileId, gateContent, {
2523
+ const path = rawPath || authorizationId;
2524
+ state.setGateContent(path, authorizationId, auth.profileId, gateContent, {
2525
2525
  boundsHash,
2526
2526
  contextHash,
2527
2527
  context
@@ -2577,10 +2577,10 @@ app.post("/internal/resync-gates", internalOnly, async (_req, res) => {
2577
2577
  let orphaned = 0;
2578
2578
  for (const gate of gates) {
2579
2579
  try {
2580
- const syncHash = gate.frameHash ?? gate.boundsHash;
2581
- const auth = await state.cache.syncAuthorization(syncHash);
2580
+ const authzId = gate.authorizationId;
2581
+ const auth = await state.cache.syncAuthorization(authzId);
2582
2582
  if (auth) {
2583
- state.setGateContent(syncHash, syncHash, auth.profileId, gate.gateContent, {
2583
+ state.setGateContent(auth.path, authzId, auth.profileId, gate.gateContent, {
2584
2584
  boundsHash: gate.boundsHash,
2585
2585
  contextHash: gate.contextHash,
2586
2586
  context: gate.context
@@ -2588,8 +2588,8 @@ app.post("/internal/resync-gates", internalOnly, async (_req, res) => {
2588
2588
  synced++;
2589
2589
  console.error(`[Suveren MCP] Re-synced gate: ${gate.path}`);
2590
2590
  } else {
2591
- state.cache.invalidate(syncHash);
2592
- state.gateStore.delete(syncHash);
2591
+ state.cache.invalidate(authzId);
2592
+ state.gateStore.delete(authzId);
2593
2593
  orphaned++;
2594
2594
  console.error(`[Suveren MCP] Orphan gate purged (SP attestation deleted): ${gate.path}`);
2595
2595
  }
@@ -2744,9 +2744,7 @@ app.get("/internal/gate-content", internalOnly, (req, res) => {
2744
2744
  const path = req.query.path;
2745
2745
  const gates = state.gateStore.getAll();
2746
2746
  if (path) {
2747
- const entry = gates.find(
2748
- (g) => g.path === path || g.profileId === path || g.boundsHash === path || g.frameHash === path
2749
- );
2747
+ const entry = gates.find((g) => g.authorizationId === path);
2750
2748
  res.json({ entry: entry ?? null });
2751
2749
  } else {
2752
2750
  res.json({ entries: gates });
@@ -2755,7 +2753,7 @@ app.get("/internal/gate-content", internalOnly, (req, res) => {
2755
2753
  app.get("/internal/authorizations", internalOnly, (_req, res) => {
2756
2754
  const authorizations = state.getEnrichedAuthorizations().map((a) => ({
2757
2755
  profileId: a.profileId,
2758
- frameHash: a.frameHash,
2756
+ authorizationId: a.authorizationId,
2759
2757
  bounds: a.frame,
2760
2758
  context: a.context ?? {},
2761
2759
  intent: a.gateContent?.intent ?? null,