@casimesh/contracts 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.
- package/index.d.ts +1120 -21
- package/index.js +247 -10
- package/index.js.map +1 -1
- 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
|
// src/agent.ts
|
|
45
52
|
var AgentKind = z.enum(["standard", "mesh"]);
|
|
@@ -70,6 +77,53 @@ var UpdateAgentRequest = 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
|
+
var AuditEntry = 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
|
+
var AuditVerification = 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" });
|
|
126
|
+
var GENESIS_HASH = "0".repeat(64);
|
|
73
127
|
var PrincipalKind = z.enum(["user", "service"]);
|
|
74
128
|
var Principal = z.object({
|
|
75
129
|
id: PrincipalId,
|
|
@@ -78,7 +132,12 @@ var Principal = z.object({
|
|
|
78
132
|
publicKey: z.string().nullable(),
|
|
79
133
|
createdAt: Timestamp
|
|
80
134
|
}).meta({ id: "Principal", description: "A user or service that can be authenticated and granted roles." });
|
|
81
|
-
var Role = z.enum(["node_owner", "org_owner"]);
|
|
135
|
+
var Role = z.enum(["node_owner", "org_owner", "viewer", "auditor", "agent_operator"]);
|
|
136
|
+
var NODE_ROLES = ["node_owner", "viewer", "auditor"];
|
|
137
|
+
var ORGANIZATION_ROLES = ["org_owner", "agent_operator"];
|
|
138
|
+
var OWNER_ROLES = ["node_owner", "org_owner"];
|
|
139
|
+
var TokenScope = z.enum(["read", "write", "admin"]);
|
|
140
|
+
var ALL_SCOPES = ["read", "write", "admin"];
|
|
82
141
|
var RoleScope = z.discriminatedUnion("type", [
|
|
83
142
|
z.object({ type: z.literal("node"), nodeId: NodeId }),
|
|
84
143
|
z.object({ type: z.literal("organization"), organizationId: OrganizationId, nodeId: NodeId })
|
|
@@ -90,10 +149,15 @@ var RoleBinding = z.object({
|
|
|
90
149
|
scope: RoleScope,
|
|
91
150
|
createdAt: Timestamp
|
|
92
151
|
}).refine(
|
|
93
|
-
(b) => b.role
|
|
94
|
-
"node_owner
|
|
152
|
+
(b) => NODE_ROLES.includes(b.role) ? b.scope.type === "node" : b.scope.type === "organization",
|
|
153
|
+
"node_owner, viewer and auditor are node-scoped; org_owner and agent_operator organization-scoped"
|
|
95
154
|
).meta({ id: "RoleBinding" });
|
|
96
155
|
var GrantOwnerRequest = z.object({ principalId: PrincipalId }).meta({ id: "GrantOwnerRequest" });
|
|
156
|
+
var RoleTarget = z.discriminatedUnion("type", [
|
|
157
|
+
z.object({ type: z.literal("node"), nodeId: NodeId }),
|
|
158
|
+
z.object({ type: z.literal("organization"), organizationId: OrganizationId })
|
|
159
|
+
]);
|
|
160
|
+
var GrantRoleRequest = z.object({ principalId: PrincipalId, role: Role, target: RoleTarget }).meta({ id: "GrantRoleRequest" });
|
|
97
161
|
var TransportKind = z.enum(["memory", "ws-peer", "tcp", "mqtt", "meshtastic", "meshcore"]);
|
|
98
162
|
var LinkState = z.enum(["requested", "up", "down"]);
|
|
99
163
|
var LinkQuality = z.object({
|
|
@@ -111,6 +175,29 @@ var Link = z.object({
|
|
|
111
175
|
...EntityAudit
|
|
112
176
|
}).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
177
|
var CreateLinkRequest = z.object({ aNodeId: NodeId, bNodeId: NodeId, transport: TransportKind }).refine((body) => body.aNodeId !== body.bNodeId, "A link must connect two different nodes").meta({ id: "CreateLinkRequest" });
|
|
178
|
+
var Ed25519Key = z.string().startsWith("ed25519:");
|
|
179
|
+
var NodeCertificate = z.object({
|
|
180
|
+
v: z.literal(1),
|
|
181
|
+
nodeId: NodeId,
|
|
182
|
+
publicKey: Ed25519Key,
|
|
183
|
+
issuedAt: Timestamp,
|
|
184
|
+
expiresAt: Timestamp,
|
|
185
|
+
/** The mesh root's public key. */
|
|
186
|
+
rootKey: Ed25519Key,
|
|
187
|
+
/** The root's signature over the canonical JSON of every other field. */
|
|
188
|
+
sig: z.string()
|
|
189
|
+
}).meta({ id: "NodeCertificate", description: "A node key certified by the mesh root (ADR-0025)." });
|
|
190
|
+
var KeyRevocation = z.object({
|
|
191
|
+
v: z.literal(1),
|
|
192
|
+
publicKey: Ed25519Key,
|
|
193
|
+
nodeId: NodeId,
|
|
194
|
+
revokedAt: Timestamp,
|
|
195
|
+
reason: z.string().max(500),
|
|
196
|
+
rootKey: Ed25519Key,
|
|
197
|
+
sig: z.string()
|
|
198
|
+
}).meta({ id: "KeyRevocation", description: "A node key revoked by the mesh root (ADR-0025)." });
|
|
199
|
+
|
|
200
|
+
// src/node.ts
|
|
114
201
|
var NodeStatus = z.enum(["online", "degraded", "offline"]);
|
|
115
202
|
var Node = z.object({
|
|
116
203
|
id: NodeId,
|
|
@@ -121,6 +208,8 @@ var Node = z.object({
|
|
|
121
208
|
status: NodeStatus,
|
|
122
209
|
metadata: Metadata,
|
|
123
210
|
lastSeenAt: Timestamp.nullable(),
|
|
211
|
+
/** The mesh root's certificate for `publicKey`, in a rooted mesh (ADR-0025). */
|
|
212
|
+
certificate: NodeCertificate.nullable().default(null),
|
|
124
213
|
...EntityAudit
|
|
125
214
|
}).meta({ id: "Node", description: "A physical or virtual endpoint connected to the mesh." });
|
|
126
215
|
var UpdateNodeRequest = z.object({
|
|
@@ -128,12 +217,75 @@ var UpdateNodeRequest = z.object({
|
|
|
128
217
|
endpoint: z.url().nullable().optional(),
|
|
129
218
|
metadata: Metadata.optional()
|
|
130
219
|
}).refine((body) => Object.keys(body).length > 0, "At least one field must be provided").meta({ id: "UpdateNodeRequest" });
|
|
220
|
+
var Effect = z.enum(["allow", "deny"]);
|
|
221
|
+
var NamePattern = z.string().regex(/^(\*|[a-z][a-z0-9_-]*(?:\.[a-z][a-z0-9_-]*)*(?:\.\*)?)$/i, "An exact name, `prefix.*` or `*`").max(100);
|
|
222
|
+
var CapabilityRule = z.object({
|
|
223
|
+
effect: Effect,
|
|
224
|
+
capability: NamePattern,
|
|
225
|
+
organizationId: OrganizationId.optional(),
|
|
226
|
+
nodeId: NodeId.optional()
|
|
227
|
+
});
|
|
228
|
+
var ToolRule = z.object({ effect: Effect, tool: NamePattern, agentId: AgentId.optional() });
|
|
229
|
+
var section = (rule) => z.object({ default: Effect.default("allow"), rules: z.array(rule).max(100).default([]) });
|
|
230
|
+
var OrganizationPolicy = z.object({
|
|
231
|
+
inbound: section(CapabilityRule).default({ default: "allow", rules: [] }),
|
|
232
|
+
outbound: section(CapabilityRule).default({ default: "allow", rules: [] }),
|
|
233
|
+
tools: section(ToolRule).default({ default: "allow", rules: [] })
|
|
234
|
+
}).meta({
|
|
235
|
+
id: "OrganizationPolicy",
|
|
236
|
+
description: "Who may request which capabilities, whom its agents may ask, and which tools they may use."
|
|
237
|
+
});
|
|
238
|
+
var OPEN_POLICY = {
|
|
239
|
+
inbound: { default: "allow", rules: [] },
|
|
240
|
+
outbound: { default: "allow", rules: [] },
|
|
241
|
+
tools: { default: "allow", rules: [] }
|
|
242
|
+
};
|
|
243
|
+
function matchesPattern(pattern, name) {
|
|
244
|
+
if (pattern === "*") return true;
|
|
245
|
+
if (pattern.endsWith(".*")) return name.startsWith(pattern.slice(0, -1));
|
|
246
|
+
return pattern === name;
|
|
247
|
+
}
|
|
248
|
+
function decide(policySection, matches) {
|
|
249
|
+
const matching = policySection.rules.filter(matches);
|
|
250
|
+
const deny = matching.find((r) => r.effect === "deny");
|
|
251
|
+
if (deny) return { allowed: false, rule: deny };
|
|
252
|
+
const allow = matching.find((r) => r.effect === "allow");
|
|
253
|
+
if (allow) return { allowed: true, rule: allow };
|
|
254
|
+
return { allowed: policySection.default === "allow", rule: void 0 };
|
|
255
|
+
}
|
|
256
|
+
var party = (rule, other) => (rule.organizationId === void 0 || rule.organizationId === other.organizationId) && (rule.nodeId === void 0 || rule.nodeId === other.nodeId);
|
|
257
|
+
function mayRequest(requester, provider, capability) {
|
|
258
|
+
if (requester.id === provider.id) return { allowed: true, rule: void 0 };
|
|
259
|
+
const outbound = decide(
|
|
260
|
+
requester.policy.outbound,
|
|
261
|
+
(r) => matchesPattern(r.capability, capability) && party(r, { organizationId: provider.id, nodeId: provider.nodeId })
|
|
262
|
+
);
|
|
263
|
+
if (!outbound.allowed) return outbound;
|
|
264
|
+
return decide(
|
|
265
|
+
provider.policy.inbound,
|
|
266
|
+
(r) => matchesPattern(r.capability, capability) && party(r, { organizationId: requester.id, nodeId: requester.nodeId })
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
function inboundAllows(provider, requester, capability) {
|
|
270
|
+
if (requester.organizationId === provider.id) return { allowed: true, rule: void 0 };
|
|
271
|
+
return decide(provider.policy.inbound, (r) => matchesPattern(r.capability, capability) && party(r, requester));
|
|
272
|
+
}
|
|
273
|
+
function mayUseTool(organization, agentId, tool) {
|
|
274
|
+
return decide(
|
|
275
|
+
organization.policy.tools,
|
|
276
|
+
(r) => matchesPattern(r.tool, tool) && (r.agentId === void 0 || r.agentId === agentId)
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// src/organization.ts
|
|
131
281
|
var Organization = z.object({
|
|
132
282
|
id: OrganizationId,
|
|
133
283
|
nodeId: NodeId,
|
|
134
284
|
name: DisplayName,
|
|
135
285
|
slug: Slug,
|
|
136
286
|
metadata: Metadata,
|
|
287
|
+
/** Capability and tool policy (ADR-0023). Organizations from before it have the open policy. */
|
|
288
|
+
policy: OrganizationPolicy.default(OPEN_POLICY),
|
|
137
289
|
...EntityAudit
|
|
138
290
|
}).meta({ id: "Organization", description: "A logical boundary on a node that owns policies, identities and agents." });
|
|
139
291
|
var CreateOrganizationRequest = z.object({
|
|
@@ -146,7 +298,8 @@ var CreateOrganizationRequest = z.object({
|
|
|
146
298
|
}).meta({ id: "CreateOrganizationRequest" });
|
|
147
299
|
var UpdateOrganizationRequest = z.object({
|
|
148
300
|
name: DisplayName.optional(),
|
|
149
|
-
metadata: Metadata.optional()
|
|
301
|
+
metadata: Metadata.optional(),
|
|
302
|
+
policy: OrganizationPolicy.optional()
|
|
150
303
|
}).refine((body) => Object.keys(body).length > 0, "At least one field must be provided").meta({ id: "UpdateOrganizationRequest" });
|
|
151
304
|
var TaskStatus = z.enum(["requested", "accepted", "completed", "failed"]);
|
|
152
305
|
var TERMINAL_TASK_STATUSES = ["completed", "failed"];
|
|
@@ -192,7 +345,9 @@ var CapabilityProvider = z.object({
|
|
|
192
345
|
organizationName: z.string(),
|
|
193
346
|
nodeId: NodeId,
|
|
194
347
|
/** Running, on a node that is not offline — i.e. a task could be routed to it now. */
|
|
195
|
-
available: z.boolean()
|
|
348
|
+
available: z.boolean(),
|
|
349
|
+
/** Whether both organizations' policies let the agent named by `forAgent` ask this provider (ADR-0023); null without it. */
|
|
350
|
+
permitted: z.boolean().nullable()
|
|
196
351
|
}).meta({ id: "CapabilityProvider" });
|
|
197
352
|
|
|
198
353
|
// src/events.ts
|
|
@@ -203,6 +358,7 @@ var AgentSubject = subject("agent", AgentId);
|
|
|
203
358
|
var LinkSubject = subject("link", LinkId);
|
|
204
359
|
var RoleBindingSubject = subject("roleBinding", RoleBindingId);
|
|
205
360
|
var TaskSubject = subject("task", TaskId);
|
|
361
|
+
var RevocationSubject = subject("revocation", RevocationId);
|
|
206
362
|
var event = (type, subjectSchema, payload) => z.object({ type: z.literal(type), subject: subjectSchema, payload });
|
|
207
363
|
var AgentRegistration = z.object({
|
|
208
364
|
meshAgentId: AgentId,
|
|
@@ -259,6 +415,7 @@ var TaskRequested = event("task.requested", TaskSubject, z.object({ task: Task }
|
|
|
259
415
|
var TaskAccepted = event("task.accepted", TaskSubject, z.object({ task: Task }));
|
|
260
416
|
var TaskCompleted = event("task.completed", TaskSubject, z.object({ task: Task }));
|
|
261
417
|
var TaskFailed = event("task.failed", TaskSubject, z.object({ task: Task }));
|
|
418
|
+
var MeshKeyRevoked = event("mesh.key_revoked", RevocationSubject, z.object({ revocation: KeyRevocation }));
|
|
262
419
|
var DomainEvent = z.discriminatedUnion("type", [
|
|
263
420
|
NodeCreated,
|
|
264
421
|
NodeUpdated,
|
|
@@ -284,7 +441,8 @@ var DomainEvent = z.discriminatedUnion("type", [
|
|
|
284
441
|
TaskRequested,
|
|
285
442
|
TaskAccepted,
|
|
286
443
|
TaskCompleted,
|
|
287
|
-
TaskFailed
|
|
444
|
+
TaskFailed,
|
|
445
|
+
MeshKeyRevoked
|
|
288
446
|
]).meta({ id: "DomainEvent" });
|
|
289
447
|
var DOMAIN_EVENT_TYPES = DomainEvent.options.map((o) => o.shape.type.value);
|
|
290
448
|
var ENVELOPE_VERSION = 1;
|
|
@@ -310,13 +468,37 @@ var HealthResponse = z.object({
|
|
|
310
468
|
uptimeSeconds: z.number().nonnegative(),
|
|
311
469
|
timestamp: z.iso.datetime()
|
|
312
470
|
}).meta({ id: "HealthResponse", description: "Liveness and version information for a Node Server." });
|
|
313
|
-
var Me = z.object({
|
|
471
|
+
var Me = z.object({
|
|
472
|
+
principal: Principal,
|
|
473
|
+
roles: z.array(RoleBinding),
|
|
474
|
+
/** What the credential used for this request may do (ADR-0022). */
|
|
475
|
+
scopes: z.array(TokenScope)
|
|
476
|
+
}).meta({ id: "Me", description: "The authenticated principal and its role bindings." });
|
|
314
477
|
var CreatePrincipalRequest = z.object({ name: DisplayName, kind: PrincipalKind.default("user") }).meta({ id: "CreatePrincipalRequest" });
|
|
315
478
|
var Credential = z.discriminatedUnion("type", [
|
|
316
479
|
z.object({ type: z.literal("token"), token: z.string(), expiresAt: Timestamp }),
|
|
317
480
|
z.object({ type: z.literal("apiKey"), apiKey: z.string() })
|
|
318
481
|
]).meta({ id: "Credential" });
|
|
319
482
|
var CreatePrincipalResponse = z.object({ principal: Principal, credential: Credential }).meta({ id: "CreatePrincipalResponse" });
|
|
483
|
+
var MAX_TOKEN_TTL_SECONDS = 365 * 24 * 60 * 60;
|
|
484
|
+
var IssueTokenRequest = z.object({
|
|
485
|
+
/** Defaults to all scopes the caller's own credential has. */
|
|
486
|
+
scopes: z.array(TokenScope).min(1).optional(),
|
|
487
|
+
ttlSeconds: z.int().min(60).max(MAX_TOKEN_TTL_SECONDS).default(30 * 24 * 60 * 60),
|
|
488
|
+
label: z.string().trim().max(120).optional()
|
|
489
|
+
}).meta({ id: "IssueTokenRequest" });
|
|
490
|
+
var IssuedToken = z.object({ id: TokenId, token: z.string(), scopes: z.array(TokenScope), expiresAt: Timestamp }).meta({ id: "IssuedToken" });
|
|
491
|
+
var CredentialInfo = z.object({
|
|
492
|
+
id: z.union([TokenId, ApiKeyId]),
|
|
493
|
+
type: z.enum(["token", "apiKey"]),
|
|
494
|
+
principalId: PrincipalId,
|
|
495
|
+
scopes: z.array(TokenScope),
|
|
496
|
+
label: z.string().nullable(),
|
|
497
|
+
createdAt: Timestamp,
|
|
498
|
+
expiresAt: Timestamp.nullable(),
|
|
499
|
+
revokedAt: Timestamp.nullable(),
|
|
500
|
+
lastUsedAt: Timestamp.nullable()
|
|
501
|
+
}).meta({ id: "CredentialInfo" });
|
|
320
502
|
var MeshCursor = z.record(NodeId, z.int().min(0)).meta({ id: "MeshCursor" });
|
|
321
503
|
var MeshSnapshot = z.object({
|
|
322
504
|
nodes: z.array(Node),
|
|
@@ -394,6 +576,8 @@ function project(event2) {
|
|
|
394
576
|
case "task.completed":
|
|
395
577
|
case "task.failed":
|
|
396
578
|
return { op: "upsert", kind: "task", entity: event2.payload.task };
|
|
579
|
+
case "mesh.key_revoked":
|
|
580
|
+
return { op: "upsert", kind: "revocation", entity: { ...event2.payload.revocation, id: event2.subject.id } };
|
|
397
581
|
// Registration is implied by organization membership; heartbeats are handled by the presence detector.
|
|
398
582
|
case "mesh_agent.agent_registered":
|
|
399
583
|
case "mesh_agent.agent_deregistered":
|
|
@@ -402,6 +586,55 @@ function project(event2) {
|
|
|
402
586
|
}
|
|
403
587
|
}
|
|
404
588
|
|
|
589
|
+
// src/writers.ts
|
|
590
|
+
var owner = (...nodeIds) => ({ kind: "owner", nodeIds });
|
|
591
|
+
var ANY = { kind: "any" };
|
|
592
|
+
function writersOf(event2) {
|
|
593
|
+
switch (event2.type) {
|
|
594
|
+
case "node.created":
|
|
595
|
+
case "node.updated":
|
|
596
|
+
return owner(event2.payload.node.id);
|
|
597
|
+
case "node.heartbeat":
|
|
598
|
+
return owner(event2.payload.nodeId);
|
|
599
|
+
case "organization.created":
|
|
600
|
+
case "organization.updated":
|
|
601
|
+
return owner(event2.payload.organization.nodeId);
|
|
602
|
+
case "agent.created":
|
|
603
|
+
case "agent.updated":
|
|
604
|
+
case "agent.status_changed":
|
|
605
|
+
return owner(event2.payload.agent.nodeId);
|
|
606
|
+
case "mesh_agent.agent_registered":
|
|
607
|
+
return owner(event2.payload.nodeId);
|
|
608
|
+
case "link.up":
|
|
609
|
+
case "link.updated":
|
|
610
|
+
case "link.down":
|
|
611
|
+
return owner(event2.payload.link.aNodeId);
|
|
612
|
+
case "link.requested":
|
|
613
|
+
return owner(event2.payload.link.aNodeId, event2.payload.link.bNodeId);
|
|
614
|
+
case "role.granted":
|
|
615
|
+
return owner(event2.payload.binding.scope.nodeId);
|
|
616
|
+
case "task.requested":
|
|
617
|
+
return owner(event2.payload.task.requester.nodeId);
|
|
618
|
+
case "task.accepted":
|
|
619
|
+
case "task.completed":
|
|
620
|
+
case "task.failed":
|
|
621
|
+
return owner(event2.payload.task.assignee.nodeId);
|
|
622
|
+
case "node.removed":
|
|
623
|
+
case "node.status_changed":
|
|
624
|
+
case "organization.removed":
|
|
625
|
+
case "agent.removed":
|
|
626
|
+
case "mesh_agent.agent_deregistered":
|
|
627
|
+
case "link.removed":
|
|
628
|
+
case "role.revoked":
|
|
629
|
+
case "mesh.key_revoked":
|
|
630
|
+
return ANY;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
function isAuthorizedWriter(event2, originNodeId) {
|
|
634
|
+
const writers = writersOf(event2);
|
|
635
|
+
return writers.kind === "any" || writers.nodeIds.includes(originNodeId);
|
|
636
|
+
}
|
|
637
|
+
|
|
405
638
|
// src/mesh-state.ts
|
|
406
639
|
var byId = (items) => Object.fromEntries(items.map((item) => [item.id, item]));
|
|
407
640
|
function meshStateFromSnapshot(snapshot) {
|
|
@@ -419,6 +652,7 @@ function applyMeshEvent(state, event2) {
|
|
|
419
652
|
const seen = state.cursor[event2.origin.nodeId] ?? 0;
|
|
420
653
|
if (event2.seq <= seen) return state;
|
|
421
654
|
const cursor = { ...state.cursor, [event2.origin.nodeId]: event2.seq };
|
|
655
|
+
if (!isAuthorizedWriter(event2, event2.origin.nodeId)) return { ...state, cursor };
|
|
422
656
|
const op = project(event2);
|
|
423
657
|
switch (op.op) {
|
|
424
658
|
case "upsert":
|
|
@@ -432,6 +666,7 @@ function applyMeshEvent(state, event2) {
|
|
|
432
666
|
case "link":
|
|
433
667
|
return { ...state, cursor, links: { ...state.links, [op.entity.id]: op.entity } };
|
|
434
668
|
case "roleBinding":
|
|
669
|
+
case "revocation":
|
|
435
670
|
return { ...state, cursor };
|
|
436
671
|
case "task": {
|
|
437
672
|
const current = state.tasks[op.entity.id];
|
|
@@ -474,7 +709,7 @@ var ProblemDetails = z.object({
|
|
|
474
709
|
code: z.string().meta({ description: "Stable machine-readable error code" }),
|
|
475
710
|
errors: z.array(z.object({ path: z.string(), message: z.string() })).optional()
|
|
476
711
|
}).meta({ id: "ProblemDetails" });
|
|
477
|
-
var Topic = z.enum(["node", "organization", "agent", "link", "roleBinding", "task"]);
|
|
712
|
+
var Topic = z.enum(["node", "organization", "agent", "link", "roleBinding", "task", "revocation"]);
|
|
478
713
|
var ClientMessage = z.discriminatedUnion("type", [
|
|
479
714
|
z.object({
|
|
480
715
|
type: z.literal("hello"),
|
|
@@ -497,10 +732,12 @@ var RealtimeCloseCode = {
|
|
|
497
732
|
goingAway: 1001,
|
|
498
733
|
badMessage: 4400,
|
|
499
734
|
unauthenticated: 4401,
|
|
735
|
+
/** Authenticated, but without a role on this node (ADR-0022). */
|
|
736
|
+
forbidden: 4403,
|
|
500
737
|
helloTimeout: 4408,
|
|
501
738
|
resyncRequired: 4409
|
|
502
739
|
};
|
|
503
740
|
|
|
504
|
-
export { Agent, AgentCreated, AgentId, AgentKind, AgentRemoved, AgentStatus, AgentStatusChanged, AgentUpdated, Capability, CapabilityName, CapabilityProvider, ClientMessage, CommandId, CreateAgentRequest, CreateLinkRequest, CreateOrganizationRequest, CreatePrincipalRequest, CreatePrincipalResponse, Credential, DOMAIN_EVENT_TYPES, DisplayName, DomainEvent, ENVELOPE_VERSION, EntityAudit, EnvelopeMeta, EventId, GrantOwnerRequest, HealthResponse, ID_PREFIX, Link, LinkDown, LinkId, LinkQuality, LinkRemoved, LinkRequested, LinkState, LinkUp, LinkUpdated, Me, MeshAgentAgentDeregistered, MeshAgentAgentRegistered, MeshCursor, MeshEvent, MeshSnapshot, MeshStats, Metadata, Node, NodeCreated, NodeDetail, NodeHeartbeat, NodeId, NodeRemoved, NodeStatus, NodeStatusChanged, NodeUpdated, Organization, OrganizationCreated, OrganizationId, OrganizationRemoved, OrganizationUpdated, Page, PageQuery, Principal, PrincipalId, PrincipalKind, ProblemDetails, RealtimeCloseCode, RequestTaskRequest, Role, RoleBinding, RoleBindingId, RoleGranted, RoleRevoked, RoleScope, ServerMessage, Slug, TERMINAL_TASK_STATUSES, Task, TaskAccepted, TaskAssignee, TaskCompleted, TaskError, TaskFailed, TaskId, TaskParty, TaskRequested, TaskStatus, TaskStep, Timestamp, Topic, TransportKind, ULID_PATTERN, UpdateAgentRequest, UpdateNodeRequest, UpdateOrganizationRequest, Version, applyMeshEvent, decodeCursor, encodeCursor, meshStateFromSnapshot, prefixedId, project };
|
|
741
|
+
export { ALL_SCOPES, Agent, AgentCreated, AgentId, AgentKind, AgentRemoved, AgentStatus, AgentStatusChanged, AgentUpdated, ApiKeyId, AuditEntry, AuditId, AuditKind, AuditOutcome, AuditTarget, AuditVerification, Capability, CapabilityName, CapabilityProvider, CapabilityRule, ClientMessage, CommandId, CreateAgentRequest, CreateLinkRequest, CreateOrganizationRequest, CreatePrincipalRequest, CreatePrincipalResponse, Credential, CredentialInfo, DOMAIN_EVENT_TYPES, DisplayName, DomainEvent, ENVELOPE_VERSION, Effect, EntityAudit, EnvelopeMeta, EventId, GENESIS_HASH, GrantOwnerRequest, GrantRoleRequest, HealthResponse, ID_PREFIX, IssueTokenRequest, IssuedToken, KeyRevocation, Link, LinkDown, LinkId, LinkQuality, LinkRemoved, LinkRequested, LinkState, LinkUp, LinkUpdated, MAX_TOKEN_TTL_SECONDS, Me, MeshAgentAgentDeregistered, MeshAgentAgentRegistered, MeshCursor, MeshEvent, MeshKeyRevoked, MeshSnapshot, MeshStats, Metadata, NODE_ROLES, NamePattern, Node, NodeCertificate, NodeCreated, NodeDetail, NodeHeartbeat, NodeId, NodeRemoved, NodeStatus, NodeStatusChanged, NodeUpdated, OPEN_POLICY, ORGANIZATION_ROLES, OWNER_ROLES, Organization, OrganizationCreated, OrganizationId, OrganizationPolicy, OrganizationRemoved, OrganizationUpdated, Page, PageQuery, Principal, PrincipalId, PrincipalKind, ProblemDetails, RealtimeCloseCode, RequestTaskRequest, RevocationId, Role, RoleBinding, RoleBindingId, RoleGranted, RoleRevoked, RoleScope, RoleTarget, ServerMessage, Slug, TERMINAL_TASK_STATUSES, Task, TaskAccepted, TaskAssignee, TaskCompleted, TaskError, TaskFailed, TaskId, TaskParty, TaskRequested, TaskStatus, TaskStep, Timestamp, TokenId, TokenScope, ToolRule, Topic, TransportKind, ULID_PATTERN, UpdateAgentRequest, UpdateNodeRequest, UpdateOrganizationRequest, Version, applyMeshEvent, decodeCursor, encodeCursor, inboundAllows, isAuthorizedWriter, matchesPattern, mayRequest, mayUseTool, meshStateFromSnapshot, prefixedId, project, writersOf };
|
|
505
742
|
//# sourceMappingURL=index.js.map
|
|
506
743
|
//# sourceMappingURL=index.js.map
|