@voltro/protocol 0.79.1 → 0.81.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.
@@ -63,7 +63,14 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
63
63
  tenantId: n.Null,
64
64
  scopes: c,
65
65
  metadata: n.optional(s)
66
- }), m = n.Union(l, u, d, f, p), h = n.Union(l.omit("scopes"), u.omit("scopes"), d.omit("scopes"), f, p.omit("scopes")), ee = (e) => {
66
+ }), m = n.Struct({
67
+ type: n.Literal("service"),
68
+ id: n.String,
69
+ service: n.String,
70
+ tenantId: n.Null,
71
+ scopes: c,
72
+ metadata: n.optional(s)
73
+ }), ee = n.Union(l, u, d, f, p, m), te = n.Union(l.omit("scopes"), u.omit("scopes"), d.omit("scopes"), f, p.omit("scopes"), m.omit("scopes")), h = (e) => {
67
74
  if (e.type === "anonymous") return e;
68
75
  let { scopes: t, ...n } = e;
69
76
  return n;
@@ -72,35 +79,41 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
72
79
  id: null,
73
80
  tenantId: e,
74
81
  ...t === void 0 ? {} : { credentialRejected: t }
75
- }), v = (e, t = ["admin:full"]) => ({
82
+ }), ne = (e, t = ["admin:full"]) => ({
76
83
  type: "system",
77
84
  id: e,
78
85
  tenantId: null,
79
86
  scopes: t
80
- }), y = (e) => {
87
+ }), re = (e) => ({
88
+ type: "service",
89
+ id: `service:${e}`,
90
+ service: e,
91
+ tenantId: null,
92
+ scopes: []
93
+ }), ie = (e) => e.type === "service", ae = (e) => e.type === "system" || e.type === "service", v = (e) => {
81
94
  if (e.type === "system") return null;
82
95
  if (e.type === "serviceAccount" && e.auditActorId !== void 0) return e.auditActorId;
83
96
  if (e.type !== "apiKey") return e.id;
84
97
  let t = e.metadata?.userId;
85
98
  return typeof t == "string" && t !== "" ? t : e.id;
86
- }, te = (e, t) => {
99
+ }, y = (e, t) => {
87
100
  let n = e;
88
101
  return {
89
102
  type: "serviceAccount",
90
103
  id: e.id ?? "system",
91
104
  tenantId: t,
92
- auditActorId: y(e),
105
+ auditActorId: v(e),
93
106
  scopes: g(e),
94
107
  ...n.metadata === void 0 ? {} : { metadata: n.metadata }
95
108
  };
96
- }, ne = (e) => e.type === "system", b = "impersonation", x = (e) => e.metadata?.impersonation ?? null, S = class extends e.Tag("@voltro/Subject")() {}, C = class extends e.Tag("@voltro/ConnectionInfo")() {}, w = class extends n.TaggedError()("Unauthenticated", { reason: n.optional(n.String) }) {}, T = class extends r.Tag()("@voltro/AuthMiddleware", {
97
- provides: S,
98
- failure: w
99
- }) {}, re = class extends r.Tag()("@voltro/ConnectionInfoMiddleware", { provides: C }) {}, ie = (e) => typeof e.mountRoutes == "function", ae = (e) => Array.isArray(e) ? {
109
+ }, b = (e) => e.type === "system", oe = "impersonation", se = (e) => e.metadata?.impersonation ?? null, x = class extends e.Tag("@voltro/Subject")() {}, S = class extends e.Tag("@voltro/ConnectionInfo")() {}, C = class extends n.TaggedError()("Unauthenticated", { reason: n.optional(n.String) }) {}, ce = class extends r.Tag()("@voltro/AuthMiddleware", {
110
+ provides: x,
111
+ failure: C
112
+ }) {}, le = class extends r.Tag()("@voltro/ConnectionInfoMiddleware", { provides: S }) {}, ue = (e) => typeof e.mountRoutes == "function", de = (e) => Array.isArray(e) ? {
100
113
  kind: "grant",
101
114
  scopes: e
102
- } : e, E = (e, t) => {
103
- if (t.kind === "vetoed") throw new w({ reason: `${t.code}: ${t.message}` });
115
+ } : e, w = (e, t) => {
116
+ if (t.kind === "vetoed") throw new C({ reason: `${t.code}: ${t.message}` });
104
117
  if (e.type === "anonymous") return {
105
118
  subject: e,
106
119
  removed: []
@@ -128,14 +141,14 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
128
141
  },
129
142
  removed: i
130
143
  };
131
- }, oe = 3e4, se = 1e4, D = (e) => `${e.type}\u0000${e.tenantId ?? ""}\u0000${e.id ?? ""}`, ce = () => {
144
+ }, fe = 3e4, pe = 1e4, T = (e) => `${e.type}\u0000${e.tenantId ?? ""}\u0000${e.id ?? ""}`, me = () => {
132
145
  if (typeof process > "u") return;
133
146
  let e = process.env.VOLTRO_AUTH_SCOPE_CACHE_TTL_MS;
134
147
  if (e === void 0 || e === "") return;
135
148
  let t = Number(e);
136
149
  return Number.isFinite(t) && t >= 0 ? t : void 0;
137
- }, O = (e = {}) => {
138
- let t = e.ttlMs ?? ce() ?? 3e4, n = e.maxEntries ?? 1e4, r = e.now ?? Date.now, i = /* @__PURE__ */ new Map(), a = () => {
150
+ }, E = (e = {}) => {
151
+ let t = e.ttlMs ?? me() ?? 3e4, n = e.maxEntries ?? 1e4, r = e.now ?? Date.now, i = /* @__PURE__ */ new Map(), a = () => {
139
152
  if (i.size <= n) return;
140
153
  let e = r();
141
154
  for (let [t, n] of i) n.freshUntil <= e && i.delete(t);
@@ -171,10 +184,10 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
171
184
  };
172
185
  }
173
186
  };
174
- }, le = (e) => typeof e.resolve == "function", ue = (e) => {
187
+ }, he = (e) => typeof e.resolve == "function", ge = (e) => {
175
188
  let t = e.authorization ?? e.Authorization;
176
189
  if (t !== void 0) return /^Bearer\s+(.+)$/i.exec(t.trim())?.[1]?.trim();
177
- }, de = async (e, t, n, r) => {
190
+ }, _e = async (e, t, n, r) => {
178
191
  if (!i(e)) return e;
179
192
  let s = {
180
193
  type: e.type,
@@ -203,40 +216,44 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
203
216
  }
204
217
  return e;
205
218
  }
206
- }, k = (e, t) => {
207
- let n = ue(t);
219
+ }, D = (e, t) => {
220
+ let n = ge(t);
208
221
  if (n === void 0) return e;
209
222
  let r = e.filter((e) => e.claimsBearerPrefix !== void 0 && e.claimsBearerPrefix !== "" && n.startsWith(e.claimsBearerPrefix));
210
223
  if (r.length === 0) return e;
211
224
  let i = new Set(r);
212
225
  return [...r, ...e.filter((e) => !i.has(e))];
213
- }, A = (e, t) => {
226
+ }, O = (e, t) => {
214
227
  let n = t?.resolveScopes, r = t?.subjectGuards ?? [];
215
228
  if (t?.memberships === void 0 != (t?.tenantSelection === void 0)) throw Error("composeAuthStrategies: `memberships` and `tenantSelection` come together — a tenant selection is verified against the memberships and read from the store; configure both or neither.");
216
229
  let i = t?.memberships !== void 0 && t.tenantSelection !== void 0 ? {
217
230
  memberships: t.memberships,
218
231
  store: t.tenantSelection
219
- } : void 0, a = n !== void 0 || r.length > 0, o = !a || t?.scopeCache === !1 ? null : t?.scopeCache !== void 0 && le(t.scopeCache) ? t.scopeCache : O(t?.scopeCache ?? {});
232
+ } : void 0, a = n !== void 0 || r.length > 0, o = !a || t?.scopeCache === !1 ? null : t?.scopeCache !== void 0 && he(t.scopeCache) ? t.scopeCache : E(t?.scopeCache ?? {});
220
233
  return async (s) => {
221
234
  let c = t?.getStore?.(), l = c === void 0 ? s : {
222
235
  ...s,
223
236
  store: c
224
237
  }, u;
225
- for (let s of k(e, l.headers)) {
238
+ for (let s of D(e, l.headers)) {
226
239
  let e = await s.resolve(l);
227
240
  if (e.kind === "matched") {
228
- let c = i === void 0 ? e.subject : await de(e.subject, l, i, (e) => (t?.onStrategyFailed?.({
241
+ if (e.subject.type === "service") return { subject: {
242
+ ...e.subject,
243
+ scopes: []
244
+ } };
245
+ let c = i === void 0 ? e.subject : await _e(e.subject, l, i, (e) => (t?.onStrategyFailed?.({
229
246
  strategyId: s.id,
230
247
  reason: e
231
- }), new w({ reason: e }))), u = c;
248
+ }), new C({ reason: e }))), u = c;
232
249
  if (a) {
233
250
  let e = async () => {
234
251
  let e = n === void 0 ? {
235
252
  kind: "grant",
236
253
  scopes: []
237
- } : ae(await n(c, l));
254
+ } : de(await n(c, l));
238
255
  if (e.kind === "unavailable" || r.length === 0) return e;
239
- let t = E(c, e).subject, i = {
256
+ let t = w(c, e).subject, i = {
240
257
  origin: "request",
241
258
  headers: l.headers,
242
259
  clientId: l.clientId,
@@ -254,22 +271,22 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
254
271
  }, { decision: i, fresh: a } = o === null ? {
255
272
  decision: await e(),
256
273
  fresh: !0
257
- } : await o.resolve(D(c), e);
274
+ } : await o.resolve(T(c), e);
258
275
  if (i.kind === "vetoed") {
259
276
  let e = `${i.code}: ${i.message}`;
260
277
  throw t?.onStrategyFailed?.({
261
278
  strategyId: s.id,
262
279
  reason: e
263
- }), new w({ reason: e });
280
+ }), new C({ reason: e });
264
281
  }
265
282
  if (i.kind === "unavailable") {
266
283
  let e = `scope resolution unavailable: ${i.reason}`;
267
284
  throw t?.onStrategyFailed?.({
268
285
  strategyId: s.id,
269
286
  reason: e
270
- }), new w({ reason: e });
287
+ }), new C({ reason: e });
271
288
  }
272
- let d = E(c, i);
289
+ let d = w(c, i);
273
290
  a && d.removed.length > 0 && t?.onScopesNarrowed?.({
274
291
  strategyId: s.id,
275
292
  subjectType: c.type,
@@ -298,52 +315,62 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
298
315
  credentialRejected: u
299
316
  } : e };
300
317
  }
301
- if (u !== void 0) throw new w({ reason: `credential rejected: ${u}` });
318
+ if (u !== void 0) throw new C({ reason: `credential rejected: ${u}` });
302
319
  if (t?.anonymousTenantId !== void 0) return { subject: _(t.anonymousTenantId) };
303
320
  let d = l.headers["x-tenant"] ?? t?.anonymousTenantDefault ?? null;
304
- if (d === null && t?.anonymousTenantRequired === !0) throw new w({ reason: "tenant required (x-tenant header missing)" });
321
+ if (d === null && t?.anonymousTenantRequired === !0) throw new C({ reason: "tenant required (x-tenant header missing)" });
305
322
  return { subject: _(d) };
306
323
  };
307
- }, j = (e, t) => {
308
- if (e.type === "anonymous") throw new w(t === void 0 ? {} : { reason: t });
309
- }, M = "admin:full", N = "apikeys:issue:self", P = "apikeys:issue:org", F = "apikeys:issue:other", I = "devices:manage", L = class extends n.TaggedError()("ScopeError", {
324
+ }, k = (e, t) => {
325
+ if (e.type === "anonymous") throw new C(t === void 0 ? {} : { reason: t });
326
+ }, A = "admin:full", j = "apikeys:issue:self", M = "apikeys:issue:org", N = "apikeys:issue:other", P = "devices:manage", F = class extends n.TaggedError()("ScopeError", {
310
327
  required: n.String,
311
328
  message: n.String
312
- }) {}, R = (e) => "scopes" in e && e.scopes ? e.scopes : [], z = /* @__PURE__ */ new WeakMap(), B = (e, t) => {
313
- typeof e == "object" && e && z.set(e, t);
314
- }, V = (e) => {
329
+ }) {}, I = (e) => "scopes" in e && e.scopes ? e.scopes : [], L = /* @__PURE__ */ new WeakMap(), ve = (e, t) => {
330
+ typeof e == "object" && e && L.set(e, t);
331
+ }, R = (e) => {
315
332
  if (typeof e == "object" && e) {
316
- let t = z.get(e);
333
+ let t = L.get(e);
317
334
  if (t) return t;
318
335
  }
319
- return R(e);
320
- }, H = (e, t) => {
321
- let n = V(e);
322
- return n.includes("admin:full") || n.includes(t);
323
- }, fe = (e, t) => {
336
+ return I(e);
337
+ }, z = (e, t) => {
324
338
  let n = R(e);
325
339
  return n.includes("admin:full") || n.includes(t);
326
- }, pe = (e, n) => H(e, n) ? t.void : t.fail(new L({
340
+ }, ye = (e, t) => {
341
+ let n = I(e);
342
+ return n.includes("admin:full") || n.includes(t);
343
+ }, be = (e, n) => z(e, n) ? t.void : t.fail(new F({
327
344
  required: n,
328
345
  message: `missing required scope '${n}'`
329
- })), U = (e, t) => {
346
+ })), B = (e, t) => {
330
347
  let n;
331
348
  try {
332
349
  n = typeof e.action == "function" ? e.action(t) : e.action;
333
350
  } catch {
334
- throw new L({
351
+ throw new F({
335
352
  required: e.resourceType,
336
353
  message: "policy action selector failed"
337
354
  });
338
355
  }
339
- if (typeof n != "string" || n.trim() === "") throw new L({
356
+ if (typeof n != "string" || n.trim() === "") throw new F({
340
357
  required: e.resourceType,
341
358
  message: "policy action selector returned no action"
342
359
  });
343
360
  return n;
344
- }, W = (e) => (typeof e.action == "string" || typeof e.action == "function") && typeof e.resourceType == "string", G = (e) => typeof e.open == "string", K = (e) => e.authenticated === !0, me = (e) => ({ open: e }), q = (e) => typeof e == "string" ? [e] : e, J, he = (e) => {
361
+ }, V = (e) => (typeof e.action == "string" || typeof e.action == "function") && typeof e.resourceType == "string", H = (e) => typeof e.open == "string", U = (e) => Array.isArray(e.service), xe = (e) => ({ service: [...e] }), W = (e) => e?.find(U)?.service, G = (e, t) => {
362
+ let n = W(t);
363
+ if (e.type === "service") return n !== void 0 && n.includes(e.service) ? null : new F({
364
+ required: "service-procedure",
365
+ message: n === void 0 ? `service '${e.service}' may only call procedures that declare it in \`service: { callers }\`` : `service '${e.service}' is not among this procedure's callers [${n.join(", ")}]`
366
+ });
367
+ if (n !== void 0) return Q(e, new F({
368
+ required: `service:${n.join("|")}`,
369
+ message: `this procedure is callable only by the service${n.length === 1 ? "" : "s"} [${n.join(", ")}]`
370
+ }));
371
+ }, K = (e) => e.authenticated === !0, Se = (e) => ({ open: e }), q = (e) => typeof e == "string" ? [e] : e, J, Ce = (e) => {
345
372
  J = e;
346
- }, ge = (e) => J === void 0 ? e.filter((e) => (e.guards ?? []).some((e) => !W(e) && e.resource !== void 0)).map((e) => e.tag) : [], _e = (e) => [
373
+ }, we = (e) => J === void 0 ? e.filter((e) => (e.guards ?? []).some((e) => !V(e) && e.resource !== void 0)).map((e) => e.tag) : [], Te = (e) => [
347
374
  `${e.length} procedure${e.length === 1 ? "" : "s"} declare a per-resource guard that nothing enforces:`,
348
375
  ...e.slice(0, 10).map((e) => ` ${e}`),
349
376
  ...e.length > 10 ? [` … and ${e.length - 10} more`] : [],
@@ -357,56 +384,58 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
357
384
  " • setResourceScopeResolver((req) => Effect<boolean>) — answers \"does this",
358
385
  " subject hold `req.scope` on `req.resource`\" from your own tables.",
359
386
  " • Drop the `resource` extractor if a global scope check is what you meant."
360
- ].join("\n"), ve = () => J, Y, ye = (e) => {
387
+ ].join("\n"), Ee = () => J, Y, X = (e) => {
361
388
  Y = e;
362
- }, X = () => Y, Z = (e) => new L({
389
+ }, De = () => Y, Z = (e) => new F({
363
390
  required: "access-decision",
364
391
  message: `${e ?? "this procedure"} declares no access decision: it has neither \`guards:\` nor \`openAccess:\`, and the app runs with \`security.defaultDeny\`. Add the scope(s) a caller must hold (\`guards: [{ scope: '…' }]\`), or declare it open on purpose with \`openAccess: '<why anyone may call this>'\`.`
365
392
  }), Q = (e, t) => {
366
393
  let n = e.credentialRejected;
367
- return n === void 0 ? t : new w({ reason: `${n} — the request carried a credential and it was REJECTED, so this caller resolved to anonymous. The guard then refused (${t.message}); the scope is not the problem. Re-authenticate and retry.` });
394
+ return n === void 0 ? t : new C({ reason: `${n} — the request carried a credential and it was REJECTED, so this caller resolved to anonymous. The guard then refused (${t.message}); the scope is not the problem. Re-authenticate and retry.` });
368
395
  }, $ = (e, t, n) => {
396
+ let r = G(e, t);
397
+ if (r !== void 0) return r;
369
398
  if (!t || t.length === 0) return n?.defaultDeny === !0 ? Z(n.procedure) : null;
370
399
  for (let n of t) {
371
- if (G(n)) continue;
400
+ if (H(n) || U(n)) continue;
372
401
  if (K(n)) {
373
402
  if (e.id !== null && e.id !== void 0) continue;
374
- return Q(e, new L({
403
+ return Q(e, new F({
375
404
  required: "authenticated",
376
405
  message: "this procedure requires an authenticated caller"
377
406
  }));
378
407
  }
379
- if (W(n)) {
380
- if (H(e, "admin:full")) continue;
381
- return Q(e, new L({
408
+ if (V(n)) {
409
+ if (z(e, "admin:full")) continue;
410
+ return Q(e, new F({
382
411
  required: `${n.resourceType}:${n.action}`,
383
412
  message: `relationship guard '${n.action}' on '${n.resourceType}' cannot be checked synchronously`
384
413
  }));
385
414
  }
386
415
  let t = q(n.scope);
387
416
  if (t.length === 0) continue;
388
- let r = t.filter((t) => H(e, t));
417
+ let r = t.filter((t) => z(e, t));
389
418
  if (!((n.mode ?? "all") === "any" ? r.length > 0 : r.length === t.length)) {
390
- let r = (n.mode ?? "all") === "any" ? t[0] : t.find((t) => !H(e, t));
391
- return Q(e, new L({
419
+ let r = (n.mode ?? "all") === "any" ? t[0] : t.find((t) => !z(e, t));
420
+ return Q(e, new F({
392
421
  required: r,
393
422
  message: (n.mode ?? "all") === "any" ? `missing all of required scopes [${t.join(", ")}]` : `missing required scope '${r}'`
394
423
  }));
395
424
  }
396
425
  }
397
426
  return null;
398
- }, be = (e, n, r) => {
427
+ }, Oe = (e, n, r) => {
399
428
  let i;
400
429
  try {
401
- i = U(n, r);
430
+ i = B(n, r);
402
431
  } catch (e) {
403
432
  return t.succeed(e);
404
433
  }
405
- let a = (e) => new L({
434
+ let a = (e) => new F({
406
435
  required: `${n.resourceType}:${i}`,
407
436
  message: e
408
437
  });
409
- if (H(e, "admin:full")) return t.succeed(null);
438
+ if (z(e, "admin:full")) return t.succeed(null);
410
439
  let o = Y;
411
440
  if (o === void 0) return t.succeed(a(`no policy resolver registered — cannot authorize '${i}' on '${n.resourceType}'`));
412
441
  let s = n.resource(r);
@@ -416,37 +445,39 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
416
445
  resourceType: n.resourceType,
417
446
  resourceId: s
418
447
  })).pipe(t.catchAllCause(() => t.succeed(!1)), t.map((e) => e ? null : a(`no '${i}' relation on ${n.resourceType}:${s}`)));
419
- }, xe = (e, n, r, i) => {
448
+ }, ke = (e, n, r, i) => {
449
+ let a = G(e, n);
450
+ if (a !== void 0) return t.succeed(a);
420
451
  if (!n || n.length === 0) return t.succeed($(e, n, i));
421
- let a = J;
422
- return !n.some(W) && (!a || !n.some((e) => e.resource !== void 0)) ? t.succeed($(e, n, i)) : t.gen(function* () {
452
+ let o = J;
453
+ return !n.some(V) && (!o || !n.some((e) => e.resource !== void 0)) ? t.succeed($(e, n, i)) : t.gen(function* () {
423
454
  for (let i of n) {
424
- if (G(i)) continue;
455
+ if (H(i) || U(i)) continue;
425
456
  if (K(i)) {
426
457
  if (e.id !== null && e.id !== void 0) continue;
427
- return Q(e, new L({
458
+ return Q(e, new F({
428
459
  required: "authenticated",
429
460
  message: "this procedure requires an authenticated caller"
430
461
  }));
431
462
  }
432
- if (W(i)) {
433
- let t = yield* be(e, i, r);
463
+ if (V(i)) {
464
+ let t = yield* Oe(e, i, r);
434
465
  if (t) return t;
435
466
  continue;
436
467
  }
437
468
  let n = q(i.scope);
438
469
  if (n.length === 0) continue;
439
- let o = i.resource ? i.resource(r) : void 0, s = [];
470
+ let a = i.resource ? i.resource(r) : void 0, s = [];
440
471
  for (let r of n) {
441
- if (H(e, r)) {
472
+ if (z(e, r)) {
442
473
  s.push(!0);
443
474
  continue;
444
475
  }
445
- if (o !== void 0 && a !== void 0) {
446
- let n = yield* t.suspend(() => a({
476
+ if (a !== void 0 && o !== void 0) {
477
+ let n = yield* t.suspend(() => o({
447
478
  subject: e,
448
479
  scope: r,
449
- resource: o
480
+ resource: a
450
481
  })).pipe(t.catchAllCause(() => t.succeed(!1)));
451
482
  s.push(n);
452
483
  } else s.push(!1);
@@ -454,9 +485,9 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
454
485
  let c = i.mode ?? "all";
455
486
  if (!(c === "any" ? s.some(Boolean) : s.every(Boolean))) {
456
487
  let e = c === "any" ? n[0] : n[n.findIndex((e, t) => !s[t])];
457
- return new L({
488
+ return new F({
458
489
  required: e,
459
- message: c === "any" ? `missing all of required scopes [${n.join(", ")}]` : `missing required scope '${e}'` + (o === void 0 ? "" : ` on resource '${o}'`)
490
+ message: c === "any" ? `missing all of required scopes [${n.join(", ")}]` : `missing required scope '${e}'` + (a === void 0 ? "" : ` on resource '${a}'`)
460
491
  });
461
492
  }
462
493
  }
@@ -464,4 +495,4 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
464
495
  });
465
496
  };
466
497
  //#endregion
467
- export { o as $, se as A, A as B, B as C, T as D, R as E, S as F, x as G, ne as H, w as I, ee as J, D as K, _ as L, b as M, m as N, C as O, h as P, i as Q, E as R, U as S, he as T, O as U, ie as V, k as W, te as X, v as Y, a as Z, G as _, I as a, me as b, $ as c, ge as d, X as f, K as g, fe as h, N as i, oe as j, re as k, xe as l, H as m, P as n, L as o, ve as p, y as q, F as r, _e as s, M as t, V as u, W as v, ye as w, pe as x, Z as y, j as z };
498
+ export { v as $, ce as A, _ as B, B as C, X as D, ve as E, oe as F, ae as G, k as H, ee as I, E as J, ie as K, te as L, le as M, pe as N, Ce as O, fe as P, re as Q, x as R, be as S, W as T, O as U, w as V, ue as W, se as X, D as Y, T as Z, H as _, P as a, o as at, Z as b, $ as c, we as d, h as et, De as f, K as g, ye as h, j as i, i as it, S as j, I as k, ke as l, z as m, M as n, y as nt, F as o, Ee as p, b as q, N as r, a as rt, Te as s, A as t, ne as tt, R as u, V as v, xe as w, Se as x, U as y, C as z };
package/dist/session.d.ts CHANGED
@@ -189,6 +189,12 @@ declare const SessionPayload_2: Schema.Struct<{
189
189
  id: typeof Schema.String;
190
190
  tenantId: typeof Schema.Null;
191
191
  metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
192
+ }>, Schema.Struct<{
193
+ type: Schema.Literal<["service"]>;
194
+ id: typeof Schema.String;
195
+ tenantId: typeof Schema.Null;
196
+ metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
197
+ service: typeof Schema.String;
192
198
  }>]>;
193
199
  /** Unix seconds when this session expires. Verify rejects past expiry. */
194
200
  exp: typeof Schema.Number;
@@ -311,6 +317,14 @@ declare const Subject: Schema.Union<[Schema.Struct<{
311
317
  tenantId: typeof Schema.Null;
312
318
  scopes: Schema.optional<Schema.Array$<typeof Schema.String>>;
313
319
  metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
320
+ }>, Schema.Struct<{
321
+ type: Schema.Literal<["service"]>;
322
+ id: typeof Schema.String;
323
+ /** The calling app's name, as the callee lists it in `serviceCallers`. */
324
+ service: typeof Schema.String;
325
+ tenantId: typeof Schema.Null;
326
+ scopes: Schema.optional<Schema.Array$<typeof Schema.String>>;
327
+ metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
314
328
  }>]>;
315
329
 
316
330
  declare type Subject = typeof Subject.Type;
@@ -359,6 +373,12 @@ declare const SubjectIdentity: Schema.Union<[Schema.Struct<{
359
373
  id: typeof Schema.String;
360
374
  tenantId: typeof Schema.Null;
361
375
  metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
376
+ }>, Schema.Struct<{
377
+ type: Schema.Literal<["service"]>;
378
+ id: typeof Schema.String;
379
+ tenantId: typeof Schema.Null;
380
+ metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
381
+ service: typeof Schema.String;
362
382
  }>]>;
363
383
 
364
384
  declare type SubjectIdentity = typeof SubjectIdentity.Type;
package/dist/session.js CHANGED
@@ -1,11 +1,11 @@
1
- import { E as e, J as t, P as n } from "./scopes-BbIyym8y.js";
1
+ import { L as e, et as t, k as n } from "./scopes-CvU5QBQ4.js";
2
2
  import { Schema as r } from "effect";
3
3
  import { createHmac as i, timingSafeEqual as a } from "node:crypto";
4
4
  import { createLogger as o } from "@voltro/logger";
5
5
  //#region src/session.ts
6
6
  var s = o({ scope: "@voltro/protocol:session" }), c = 2, l = r.Struct({
7
7
  v: r.Literal(2),
8
- subject: n,
8
+ subject: e,
9
9
  exp: r.Number,
10
10
  iat: r.Number,
11
11
  kid: r.optional(r.String)
@@ -38,14 +38,14 @@ var s = o({ scope: "@voltro/protocol:session" }), c = 2, l = r.Struct({
38
38
  }, C = 604800, w = .7, T = (e, t) => x(i("sha256", t).update(e).digest()), E = (e) => typeof e == "string" ? {
39
39
  kid: "k0",
40
40
  secret: e
41
- } : e, D = (n, r, i = {}) => {
41
+ } : e, D = (e, r, i = {}) => {
42
42
  let a = E(r);
43
43
  if (!a.secret) throw Error("signSession: secret must be a non-empty string");
44
- let o = e(n);
45
- if (o.length > 0) throw Error(`signSession: refusing to mint a session cookie for a ${n.type} subject carrying ${o.length} scope(s) (${o.slice(0, 5).join(", ")}). A session cookie carries IDENTITY only — authority is resolved per request, so that removing a role takes effect on the session that already exists instead of in up to 7 days. Mint the session from the identity and move the scopes into the auth.resolveScopes resolver, which runs on every request and can be invalidated the moment a role changes.`);
44
+ let o = n(e);
45
+ if (o.length > 0) throw Error(`signSession: refusing to mint a session cookie for a ${e.type} subject carrying ${o.length} scope(s) (${o.slice(0, 5).join(", ")}). A session cookie carries IDENTITY only — authority is resolved per request, so that removing a role takes effect on the session that already exists instead of in up to 7 days. Mint the session from the identity and move the scopes into the auth.resolveScopes resolver, which runs on every request and can be invalidated the moment a role changes.`);
46
46
  let s = i.ttlSeconds ?? C, c = Math.floor((i.now ?? Date.now)() / 1e3), l = {
47
47
  v: 2,
48
- subject: t(n),
48
+ subject: t(e),
49
49
  exp: c + s,
50
50
  iat: c,
51
51
  ...typeof r == "string" ? {} : { kid: a.kid }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/protocol",
3
- "version": "0.79.1",
3
+ "version": "0.81.0",
4
4
  "description": "The Voltro wire + plugin contract — defineQuery/Mutation/Action/Stream, definePlugin, sessions / JWT / API-keys, and the RPC protocol.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -65,8 +65,8 @@
65
65
  "dependencies": {
66
66
  "@effect/experimental": "^0.61.1",
67
67
  "@effect/sql": "^0.52.1",
68
- "@voltro/database": "0.79.1",
69
- "@voltro/logger": "0.79.1",
68
+ "@voltro/database": "0.81.0",
69
+ "@voltro/logger": "0.81.0",
70
70
  "jose": "^6.2.12"
71
71
  },
72
72
  "peerDependencies": {