@casimesh/sdk 0.1.0 → 0.2.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 (4) hide show
  1. package/index.d.ts +1490 -30
  2. package/index.js +300 -15
  3. package/index.js.map +1 -1
  4. package/package.json +1 -1
package/index.js CHANGED
@@ -27,7 +27,10 @@ var ID_PREFIX = {
27
27
  principal: "prn",
28
28
  roleBinding: "rol",
29
29
  apiKey: "key",
30
- task: "tsk"
30
+ task: "tsk",
31
+ token: "tok",
32
+ audit: "aud",
33
+ revocation: "rev"
31
34
  };
32
35
  var ULID_PATTERN = "[0-9A-HJKMNP-TV-Z]{26}";
33
36
  var prefixedId = (kind) => z.string().regex(new RegExp(`^${ID_PREFIX[kind]}_${ULID_PATTERN}$`), `Expected a ${kind} id (${ID_PREFIX[kind]}_<ULID>)`).meta({ description: `${kind} identifier`, example: `${ID_PREFIX[kind]}_01J9Z3K7Q8R5T2V4W6X8Y0A1B2` });
@@ -40,6 +43,10 @@ var CommandId = prefixedId("command");
40
43
  var PrincipalId = prefixedId("principal");
41
44
  var RoleBindingId = prefixedId("roleBinding");
42
45
  var TaskId = prefixedId("task");
46
+ var TokenId = prefixedId("token");
47
+ var AuditId = prefixedId("audit");
48
+ var RevocationId = prefixedId("revocation");
49
+ var ApiKeyId = prefixedId("apiKey");
43
50
 
44
51
  // ../contracts/src/agent.ts
45
52
  var AgentKind = z.enum(["standard", "mesh"]);
@@ -70,6 +77,52 @@ z.object({
70
77
  model: z.string().max(100).nullable().optional(),
71
78
  metadata: Metadata.optional()
72
79
  }).refine((body) => Object.keys(body).length > 0, "At least one field must be provided").meta({ id: "UpdateAgentRequest" });
80
+ var AuditKind = z.enum([
81
+ "authorization",
82
+ "authentication",
83
+ "credential",
84
+ "principal",
85
+ "policy",
86
+ "replication"
87
+ ]);
88
+ var AuditOutcome = z.enum(["allowed", "denied", "done"]);
89
+ var AuditTarget = z.object({
90
+ nodeId: z.string().optional(),
91
+ organizationId: z.string().optional(),
92
+ /** The entity, credential or event concerned, when there is one. */
93
+ id: z.string().optional()
94
+ });
95
+ z.object({
96
+ /** 1, 2, 3… with no gaps: a missing number is a removed entry. */
97
+ seq: z.int().min(1),
98
+ id: AuditId,
99
+ at: Timestamp,
100
+ kind: AuditKind,
101
+ /** What was attempted or done, e.g. `agent.create`, `token.issue`, `event.refused`. */
102
+ action: z.string().max(64),
103
+ outcome: AuditOutcome,
104
+ principalId: PrincipalId.nullable(),
105
+ credentialId: z.string().nullable(),
106
+ target: AuditTarget,
107
+ detail: z.string().max(500),
108
+ /** SHA-256 (hex) of the previous entry; 64 zeros for the first. */
109
+ prevHash: z.string().regex(/^[0-9a-f]{64}$/),
110
+ /** SHA-256 (hex) of this entry's canonical JSON without `hash` and `sig`. */
111
+ hash: z.string().regex(/^[0-9a-f]{64}$/),
112
+ /** The node key's Ed25519 signature over `hash`. */
113
+ sig: z.string(),
114
+ /** The node key that signed (keys rotate; ADR-0025). Not part of `hash`; null for entries from before rotation. */
115
+ signer: z.string().nullable()
116
+ }).meta({ id: "AuditEntry", description: "One signed, hash-chained record of the node's audit log." });
117
+ z.object({
118
+ valid: z.boolean(),
119
+ entries: z.int().min(0),
120
+ /** The first entry that failed, or null when the whole chain holds. */
121
+ brokenAt: z.int().nullable(),
122
+ reason: z.string().nullable(),
123
+ /** The node's current key; entries may also be signed by its earlier keys. */
124
+ publicKey: z.string()
125
+ }).meta({ id: "AuditVerification" });
73
126
  var PrincipalKind = z.enum(["user", "service"]);
74
127
  var Principal = z.object({
75
128
  id: PrincipalId,
@@ -78,7 +131,9 @@ var Principal = z.object({
78
131
  publicKey: z.string().nullable(),
79
132
  createdAt: Timestamp
80
133
  }).meta({ id: "Principal", description: "A user or service that can be authenticated and granted roles." });
81
- var Role = z.enum(["node_owner", "org_owner"]);
134
+ var Role = z.enum(["node_owner", "org_owner", "viewer", "auditor", "agent_operator"]);
135
+ var NODE_ROLES = ["node_owner", "viewer", "auditor"];
136
+ var TokenScope = z.enum(["read", "write", "admin"]);
82
137
  var RoleScope = z.discriminatedUnion("type", [
83
138
  z.object({ type: z.literal("node"), nodeId: NodeId }),
84
139
  z.object({ type: z.literal("organization"), organizationId: OrganizationId, nodeId: NodeId })
@@ -90,10 +145,15 @@ var RoleBinding = z.object({
90
145
  scope: RoleScope,
91
146
  createdAt: Timestamp
92
147
  }).refine(
93
- (b) => b.role === "node_owner" ? b.scope.type === "node" : b.scope.type === "organization",
94
- "node_owner must be node-scoped and org_owner organization-scoped"
148
+ (b) => NODE_ROLES.includes(b.role) ? b.scope.type === "node" : b.scope.type === "organization",
149
+ "node_owner, viewer and auditor are node-scoped; org_owner and agent_operator organization-scoped"
95
150
  ).meta({ id: "RoleBinding" });
96
151
  z.object({ principalId: PrincipalId }).meta({ id: "GrantOwnerRequest" });
152
+ var RoleTarget = z.discriminatedUnion("type", [
153
+ z.object({ type: z.literal("node"), nodeId: NodeId }),
154
+ z.object({ type: z.literal("organization"), organizationId: OrganizationId })
155
+ ]);
156
+ z.object({ principalId: PrincipalId, role: Role, target: RoleTarget }).meta({ id: "GrantRoleRequest" });
97
157
  var TransportKind = z.enum(["memory", "ws-peer", "tcp", "mqtt", "meshtastic", "meshcore"]);
98
158
  var LinkState = z.enum(["requested", "up", "down"]);
99
159
  var LinkQuality = z.object({
@@ -111,6 +171,29 @@ var Link = z.object({
111
171
  ...EntityAudit
112
172
  }).refine((link) => link.aNodeId !== link.bNodeId, "A link must connect two different nodes").meta({ id: "Link", description: "Transport-level adjacency between two nodes \u2014 the edges of the topology." });
113
173
  z.object({ aNodeId: NodeId, bNodeId: NodeId, transport: TransportKind }).refine((body) => body.aNodeId !== body.bNodeId, "A link must connect two different nodes").meta({ id: "CreateLinkRequest" });
174
+ var Ed25519Key = z.string().startsWith("ed25519:");
175
+ var NodeCertificate = z.object({
176
+ v: z.literal(1),
177
+ nodeId: NodeId,
178
+ publicKey: Ed25519Key,
179
+ issuedAt: Timestamp,
180
+ expiresAt: Timestamp,
181
+ /** The mesh root's public key. */
182
+ rootKey: Ed25519Key,
183
+ /** The root's signature over the canonical JSON of every other field. */
184
+ sig: z.string()
185
+ }).meta({ id: "NodeCertificate", description: "A node key certified by the mesh root (ADR-0025)." });
186
+ var KeyRevocation = z.object({
187
+ v: z.literal(1),
188
+ publicKey: Ed25519Key,
189
+ nodeId: NodeId,
190
+ revokedAt: Timestamp,
191
+ reason: z.string().max(500),
192
+ rootKey: Ed25519Key,
193
+ sig: z.string()
194
+ }).meta({ id: "KeyRevocation", description: "A node key revoked by the mesh root (ADR-0025)." });
195
+
196
+ // ../contracts/src/node.ts
114
197
  var NodeStatus = z.enum(["online", "degraded", "offline"]);
115
198
  var Node = z.object({
116
199
  id: NodeId,
@@ -121,6 +204,8 @@ var Node = z.object({
121
204
  status: NodeStatus,
122
205
  metadata: Metadata,
123
206
  lastSeenAt: Timestamp.nullable(),
207
+ /** The mesh root's certificate for `publicKey`, in a rooted mesh (ADR-0025). */
208
+ certificate: NodeCertificate.nullable().default(null),
124
209
  ...EntityAudit
125
210
  }).meta({ id: "Node", description: "A physical or virtual endpoint connected to the mesh." });
126
211
  z.object({
@@ -128,12 +213,75 @@ z.object({
128
213
  endpoint: z.url().nullable().optional(),
129
214
  metadata: Metadata.optional()
130
215
  }).refine((body) => Object.keys(body).length > 0, "At least one field must be provided").meta({ id: "UpdateNodeRequest" });
216
+ var Effect = z.enum(["allow", "deny"]);
217
+ var NamePattern = z.string().regex(/^(\*|[a-z][a-z0-9_-]*(?:\.[a-z][a-z0-9_-]*)*(?:\.\*)?)$/i, "An exact name, `prefix.*` or `*`").max(100);
218
+ var CapabilityRule = z.object({
219
+ effect: Effect,
220
+ capability: NamePattern,
221
+ organizationId: OrganizationId.optional(),
222
+ nodeId: NodeId.optional()
223
+ });
224
+ var ToolRule = z.object({ effect: Effect, tool: NamePattern, agentId: AgentId.optional() });
225
+ var section = (rule) => z.object({ default: Effect.default("allow"), rules: z.array(rule).max(100).default([]) });
226
+ var OrganizationPolicy = z.object({
227
+ inbound: section(CapabilityRule).default({ default: "allow", rules: [] }),
228
+ outbound: section(CapabilityRule).default({ default: "allow", rules: [] }),
229
+ tools: section(ToolRule).default({ default: "allow", rules: [] })
230
+ }).meta({
231
+ id: "OrganizationPolicy",
232
+ description: "Who may request which capabilities, whom its agents may ask, and which tools they may use."
233
+ });
234
+ var OPEN_POLICY = {
235
+ inbound: { default: "allow", rules: [] },
236
+ outbound: { default: "allow", rules: [] },
237
+ tools: { default: "allow", rules: [] }
238
+ };
239
+ function matchesPattern(pattern, name) {
240
+ if (pattern === "*") return true;
241
+ if (pattern.endsWith(".*")) return name.startsWith(pattern.slice(0, -1));
242
+ return pattern === name;
243
+ }
244
+ function decide(policySection, matches) {
245
+ const matching = policySection.rules.filter(matches);
246
+ const deny = matching.find((r) => r.effect === "deny");
247
+ if (deny) return { allowed: false, rule: deny };
248
+ const allow = matching.find((r) => r.effect === "allow");
249
+ if (allow) return { allowed: true, rule: allow };
250
+ return { allowed: policySection.default === "allow", rule: void 0 };
251
+ }
252
+ var party = (rule, other) => (rule.organizationId === void 0 || rule.organizationId === other.organizationId) && (rule.nodeId === void 0 || rule.nodeId === other.nodeId);
253
+ function mayRequest(requester, provider, capability) {
254
+ if (requester.id === provider.id) return { allowed: true, rule: void 0 };
255
+ const outbound = decide(
256
+ requester.policy.outbound,
257
+ (r) => matchesPattern(r.capability, capability) && party(r, { organizationId: provider.id, nodeId: provider.nodeId })
258
+ );
259
+ if (!outbound.allowed) return outbound;
260
+ return decide(
261
+ provider.policy.inbound,
262
+ (r) => matchesPattern(r.capability, capability) && party(r, { organizationId: requester.id, nodeId: requester.nodeId })
263
+ );
264
+ }
265
+ function inboundAllows(provider, requester, capability) {
266
+ if (requester.organizationId === provider.id) return { allowed: true, rule: void 0 };
267
+ return decide(provider.policy.inbound, (r) => matchesPattern(r.capability, capability) && party(r, requester));
268
+ }
269
+ function mayUseTool(organization, agentId, tool) {
270
+ return decide(
271
+ organization.policy.tools,
272
+ (r) => matchesPattern(r.tool, tool) && (r.agentId === void 0 || r.agentId === agentId)
273
+ );
274
+ }
275
+
276
+ // ../contracts/src/organization.ts
131
277
  var Organization = z.object({
132
278
  id: OrganizationId,
133
279
  nodeId: NodeId,
134
280
  name: DisplayName,
135
281
  slug: Slug,
136
282
  metadata: Metadata,
283
+ /** Capability and tool policy (ADR-0023). Organizations from before it have the open policy. */
284
+ policy: OrganizationPolicy.default(OPEN_POLICY),
137
285
  ...EntityAudit
138
286
  }).meta({ id: "Organization", description: "A logical boundary on a node that owns policies, identities and agents." });
139
287
  z.object({
@@ -146,7 +294,8 @@ z.object({
146
294
  }).meta({ id: "CreateOrganizationRequest" });
147
295
  z.object({
148
296
  name: DisplayName.optional(),
149
- metadata: Metadata.optional()
297
+ metadata: Metadata.optional(),
298
+ policy: OrganizationPolicy.optional()
150
299
  }).refine((body) => Object.keys(body).length > 0, "At least one field must be provided").meta({ id: "UpdateOrganizationRequest" });
151
300
  var TaskStatus = z.enum(["requested", "accepted", "completed", "failed"]);
152
301
  var TaskParty = z.object({ agentId: AgentId, organizationId: OrganizationId, nodeId: NodeId });
@@ -191,7 +340,9 @@ z.object({
191
340
  organizationName: z.string(),
192
341
  nodeId: NodeId,
193
342
  /** Running, on a node that is not offline — i.e. a task could be routed to it now. */
194
- available: z.boolean()
343
+ available: z.boolean(),
344
+ /** Whether both organizations' policies let the agent named by `forAgent` ask this provider (ADR-0023); null without it. */
345
+ permitted: z.boolean().nullable()
195
346
  }).meta({ id: "CapabilityProvider" });
196
347
 
197
348
  // ../contracts/src/events.ts
@@ -202,6 +353,7 @@ var AgentSubject = subject("agent", AgentId);
202
353
  var LinkSubject = subject("link", LinkId);
203
354
  var RoleBindingSubject = subject("roleBinding", RoleBindingId);
204
355
  var TaskSubject = subject("task", TaskId);
356
+ var RevocationSubject = subject("revocation", RevocationId);
205
357
  var event = (type, subjectSchema, payload) => z.object({ type: z.literal(type), subject: subjectSchema, payload });
206
358
  var AgentRegistration = z.object({
207
359
  meshAgentId: AgentId,
@@ -258,6 +410,7 @@ var TaskRequested = event("task.requested", TaskSubject, z.object({ task: Task }
258
410
  var TaskAccepted = event("task.accepted", TaskSubject, z.object({ task: Task }));
259
411
  var TaskCompleted = event("task.completed", TaskSubject, z.object({ task: Task }));
260
412
  var TaskFailed = event("task.failed", TaskSubject, z.object({ task: Task }));
413
+ var MeshKeyRevoked = event("mesh.key_revoked", RevocationSubject, z.object({ revocation: KeyRevocation }));
261
414
  var DomainEvent = z.discriminatedUnion("type", [
262
415
  NodeCreated,
263
416
  NodeUpdated,
@@ -283,7 +436,8 @@ var DomainEvent = z.discriminatedUnion("type", [
283
436
  TaskRequested,
284
437
  TaskAccepted,
285
438
  TaskCompleted,
286
- TaskFailed
439
+ TaskFailed,
440
+ MeshKeyRevoked
287
441
  ]).meta({ id: "DomainEvent" });
288
442
  DomainEvent.options.map((o) => o.shape.type.value);
289
443
  var ENVELOPE_VERSION = 1;
@@ -309,13 +463,37 @@ z.object({
309
463
  uptimeSeconds: z.number().nonnegative(),
310
464
  timestamp: z.iso.datetime()
311
465
  }).meta({ id: "HealthResponse", description: "Liveness and version information for a Node Server." });
312
- z.object({ principal: Principal, roles: z.array(RoleBinding) }).meta({ id: "Me", description: "The authenticated principal and its role bindings." });
466
+ z.object({
467
+ principal: Principal,
468
+ roles: z.array(RoleBinding),
469
+ /** What the credential used for this request may do (ADR-0022). */
470
+ scopes: z.array(TokenScope)
471
+ }).meta({ id: "Me", description: "The authenticated principal and its role bindings." });
313
472
  z.object({ name: DisplayName, kind: PrincipalKind.default("user") }).meta({ id: "CreatePrincipalRequest" });
314
473
  var Credential = z.discriminatedUnion("type", [
315
474
  z.object({ type: z.literal("token"), token: z.string(), expiresAt: Timestamp }),
316
475
  z.object({ type: z.literal("apiKey"), apiKey: z.string() })
317
476
  ]).meta({ id: "Credential" });
318
477
  z.object({ principal: Principal, credential: Credential }).meta({ id: "CreatePrincipalResponse" });
478
+ var MAX_TOKEN_TTL_SECONDS = 365 * 24 * 60 * 60;
479
+ z.object({
480
+ /** Defaults to all scopes the caller's own credential has. */
481
+ scopes: z.array(TokenScope).min(1).optional(),
482
+ ttlSeconds: z.int().min(60).max(MAX_TOKEN_TTL_SECONDS).default(30 * 24 * 60 * 60),
483
+ label: z.string().trim().max(120).optional()
484
+ }).meta({ id: "IssueTokenRequest" });
485
+ z.object({ id: TokenId, token: z.string(), scopes: z.array(TokenScope), expiresAt: Timestamp }).meta({ id: "IssuedToken" });
486
+ z.object({
487
+ id: z.union([TokenId, ApiKeyId]),
488
+ type: z.enum(["token", "apiKey"]),
489
+ principalId: PrincipalId,
490
+ scopes: z.array(TokenScope),
491
+ label: z.string().nullable(),
492
+ createdAt: Timestamp,
493
+ expiresAt: Timestamp.nullable(),
494
+ revokedAt: Timestamp.nullable(),
495
+ lastUsedAt: Timestamp.nullable()
496
+ }).meta({ id: "CredentialInfo" });
319
497
  var MeshCursor = z.record(NodeId, z.int().min(0)).meta({ id: "MeshCursor" });
320
498
  z.object({
321
499
  nodes: z.array(Node),
@@ -393,6 +571,8 @@ function project(event2) {
393
571
  case "task.completed":
394
572
  case "task.failed":
395
573
  return { op: "upsert", kind: "task", entity: event2.payload.task };
574
+ case "mesh.key_revoked":
575
+ return { op: "upsert", kind: "revocation", entity: { ...event2.payload.revocation, id: event2.subject.id } };
396
576
  // Registration is implied by organization membership; heartbeats are handled by the presence detector.
397
577
  case "mesh_agent.agent_registered":
398
578
  case "mesh_agent.agent_deregistered":
@@ -401,6 +581,55 @@ function project(event2) {
401
581
  }
402
582
  }
403
583
 
584
+ // ../contracts/src/writers.ts
585
+ var owner = (...nodeIds) => ({ kind: "owner", nodeIds });
586
+ var ANY = { kind: "any" };
587
+ function writersOf(event2) {
588
+ switch (event2.type) {
589
+ case "node.created":
590
+ case "node.updated":
591
+ return owner(event2.payload.node.id);
592
+ case "node.heartbeat":
593
+ return owner(event2.payload.nodeId);
594
+ case "organization.created":
595
+ case "organization.updated":
596
+ return owner(event2.payload.organization.nodeId);
597
+ case "agent.created":
598
+ case "agent.updated":
599
+ case "agent.status_changed":
600
+ return owner(event2.payload.agent.nodeId);
601
+ case "mesh_agent.agent_registered":
602
+ return owner(event2.payload.nodeId);
603
+ case "link.up":
604
+ case "link.updated":
605
+ case "link.down":
606
+ return owner(event2.payload.link.aNodeId);
607
+ case "link.requested":
608
+ return owner(event2.payload.link.aNodeId, event2.payload.link.bNodeId);
609
+ case "role.granted":
610
+ return owner(event2.payload.binding.scope.nodeId);
611
+ case "task.requested":
612
+ return owner(event2.payload.task.requester.nodeId);
613
+ case "task.accepted":
614
+ case "task.completed":
615
+ case "task.failed":
616
+ return owner(event2.payload.task.assignee.nodeId);
617
+ case "node.removed":
618
+ case "node.status_changed":
619
+ case "organization.removed":
620
+ case "agent.removed":
621
+ case "mesh_agent.agent_deregistered":
622
+ case "link.removed":
623
+ case "role.revoked":
624
+ case "mesh.key_revoked":
625
+ return ANY;
626
+ }
627
+ }
628
+ function isAuthorizedWriter(event2, originNodeId) {
629
+ const writers = writersOf(event2);
630
+ return writers.kind === "any" || writers.nodeIds.includes(originNodeId);
631
+ }
632
+
404
633
  // ../contracts/src/mesh-state.ts
405
634
  var byId = (items) => Object.fromEntries(items.map((item) => [item.id, item]));
406
635
  function meshStateFromSnapshot(snapshot) {
@@ -418,6 +647,7 @@ function applyMeshEvent(state, event2) {
418
647
  const seen = state.cursor[event2.origin.nodeId] ?? 0;
419
648
  if (event2.seq <= seen) return state;
420
649
  const cursor = { ...state.cursor, [event2.origin.nodeId]: event2.seq };
650
+ if (!isAuthorizedWriter(event2, event2.origin.nodeId)) return { ...state, cursor };
421
651
  const op = project(event2);
422
652
  switch (op.op) {
423
653
  case "upsert":
@@ -431,6 +661,7 @@ function applyMeshEvent(state, event2) {
431
661
  case "link":
432
662
  return { ...state, cursor, links: { ...state.links, [op.entity.id]: op.entity } };
433
663
  case "roleBinding":
664
+ case "revocation":
434
665
  return { ...state, cursor };
435
666
  case "task": {
436
667
  const current = state.tasks[op.entity.id];
@@ -472,7 +703,7 @@ z.object({
472
703
  code: z.string().meta({ description: "Stable machine-readable error code" }),
473
704
  errors: z.array(z.object({ path: z.string(), message: z.string() })).optional()
474
705
  }).meta({ id: "ProblemDetails" });
475
- var Topic = z.enum(["node", "organization", "agent", "link", "roleBinding", "task"]);
706
+ var Topic = z.enum(["node", "organization", "agent", "link", "roleBinding", "task", "revocation"]);
476
707
  z.discriminatedUnion("type", [
477
708
  z.object({
478
709
  type: z.literal("hello"),
@@ -493,6 +724,8 @@ z.discriminatedUnion("type", [
493
724
  var RealtimeCloseCode = {
494
725
  normal: 1e3,
495
726
  unauthenticated: 4401,
727
+ /** Authenticated, but without a role on this node (ADR-0022). */
728
+ forbidden: 4403,
496
729
  resyncRequired: 4409
497
730
  };
498
731
 
@@ -712,7 +945,12 @@ var MeshRealtime = class {
712
945
  this.needsSnapshot = true;
713
946
  break;
714
947
  case "error":
715
- this.emitError(new RealtimeError(`${message.code}: ${message.message}`, message.code === "unauthenticated"));
948
+ this.emitError(
949
+ new RealtimeError(
950
+ `${message.code}: ${message.message}`,
951
+ message.code === "unauthenticated" || message.code === "forbidden"
952
+ )
953
+ );
716
954
  break;
717
955
  }
718
956
  }
@@ -727,8 +965,13 @@ var MeshRealtime = class {
727
965
  onClose(code) {
728
966
  this.socket = void 0;
729
967
  if (this.stopped) return;
730
- if (code === RealtimeCloseCode.unauthenticated) {
731
- this.emitError(new RealtimeError("The node rejected the credential", true));
968
+ if (code === RealtimeCloseCode.unauthenticated || code === RealtimeCloseCode.forbidden) {
969
+ this.emitError(
970
+ new RealtimeError(
971
+ code === RealtimeCloseCode.forbidden ? "The credential has no role on this node" : "The node rejected the credential",
972
+ true
973
+ )
974
+ );
732
975
  this.close();
733
976
  return;
734
977
  }
@@ -792,10 +1035,52 @@ function createMeshClient(options) {
792
1035
  stats: () => http.request("GET", "/v1/mesh/stats"),
793
1036
  capabilities: async (search) => (await http.request("GET", "/v1/capabilities", { query: { q: search } })).items,
794
1037
  /** Agents offering a capability, available ones first (ADR-0020). */
795
- capabilityProviders: async (name) => (await http.request("GET", `/v1/capabilities/${id(name)}/providers`)).items,
1038
+ revocations: {
1039
+ /** Revocations published to the mesh, each with whether it holds under the node's root (ADR-0025). */
1040
+ list: async () => (await http.request(
1041
+ "GET",
1042
+ "/v1/mesh/revocations"
1043
+ )).items,
1044
+ /** Publishes a root-signed revocation through this node (node owners). */
1045
+ publish: (revocation) => http.request("POST", "/v1/mesh/revocations", { body: revocation })
1046
+ },
1047
+ /** With `forAgent`, each provider also says whether policy lets that agent ask it (ADR-0023). */
1048
+ capabilityProviders: async (name, options2 = {}) => (await http.request("GET", `/v1/capabilities/${id(name)}/providers`, {
1049
+ query: { ...options2 }
1050
+ })).items,
796
1051
  principals: {
797
1052
  /** Node owners only. The credential is returned exactly once. */
798
- create: (input, options2 = {}) => http.request("POST", "/v1/principals", { body: input, ...options2 })
1053
+ create: (input, options2 = {}) => http.request("POST", "/v1/principals", { body: input, ...options2 }),
1054
+ /** This node's principals (node owners only). */
1055
+ list: async () => (await http.request("GET", "/v1/principals")).items,
1056
+ /** Deletes a principal and its credentials; revoke its roles first (node owners only). */
1057
+ remove: (principalId) => http.request("DELETE", `/v1/principals/${id(principalId)}`),
1058
+ /**
1059
+ * Issues a bearer token with scopes and an expiry (ADR-0022): for yourself, never with more scopes than the
1060
+ * credential you use, or — node owners — for anyone. The token is returned exactly once.
1061
+ */
1062
+ issueToken: (principalId, input = {}, options2 = {}) => http.request("POST", `/v1/principals/${id(principalId)}/tokens`, { body: input, ...options2 }),
1063
+ /** A principal's tokens and API keys, metadata only (yourself, or node owners for anyone). */
1064
+ credentials: async (principalId) => (await http.request("GET", `/v1/principals/${id(principalId)}/credentials`)).items,
1065
+ /** Revokes every credential of the principal, including the one this client may be using. */
1066
+ revokeAllCredentials: (principalId) => http.request("DELETE", `/v1/principals/${id(principalId)}/credentials`)
1067
+ },
1068
+ credentials: {
1069
+ /** Revokes one token (`tok_…`) or API key (`key_…`). */
1070
+ revoke: (credentialId) => http.request("DELETE", `/v1/credentials/${id(credentialId)}`)
1071
+ },
1072
+ audit: {
1073
+ /** Newest first; page backwards with `before` (node owners and auditors; ADR-0024). */
1074
+ list: (filter = {}) => http.request("GET", "/v1/audit", { query: { ...filter } }),
1075
+ /** Recomputes the whole chain on the node: gaps, hashes and signatures. */
1076
+ verify: () => http.request("GET", "/v1/audit/verify")
1077
+ },
1078
+ roles: {
1079
+ /** A principal's bindings, or those of a node or organization; by default this node's and its organizations'. */
1080
+ list: async (filter = {}) => (await http.request("GET", "/v1/role-bindings", { query: { ...filter } })).items,
1081
+ /** Grants node_owner, viewer or auditor on a node, or org_owner or agent_operator on an organization. */
1082
+ grant: (request, options2 = {}) => http.request("POST", "/v1/role-bindings", { body: request, ...options2 }),
1083
+ revoke: (bindingId) => http.request("DELETE", `/v1/role-bindings/${id(bindingId)}`)
799
1084
  },
800
1085
  nodes: {
801
1086
  list: (page = {}) => http.request("GET", "/v1/nodes", { query: { ...page } }),
@@ -879,6 +1164,6 @@ function createMeshClient(options) {
879
1164
  return client;
880
1165
  }
881
1166
 
882
- export { MeshApiError, MeshRealtime, RealtimeError, applyMeshEvent, createMeshClient, decodeCursor, encodeCursor, meshStateFromSnapshot };
1167
+ export { MeshApiError, MeshRealtime, OPEN_POLICY, RealtimeError, applyMeshEvent, createMeshClient, decodeCursor, encodeCursor, inboundAllows, isAuthorizedWriter, matchesPattern, mayRequest, mayUseTool, meshStateFromSnapshot };
883
1168
  //# sourceMappingURL=index.js.map
884
1169
  //# sourceMappingURL=index.js.map