@astrale-os/kernel-client 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 (257) hide show
  1. package/dist/client/batch.d.ts +26 -0
  2. package/dist/client/batch.d.ts.map +1 -0
  3. package/dist/client/batch.js +8 -0
  4. package/dist/client/batch.js.map +1 -0
  5. package/dist/client/bound.d.ts +27 -0
  6. package/dist/client/bound.d.ts.map +1 -0
  7. package/dist/client/bound.js +14 -0
  8. package/dist/client/bound.js.map +1 -0
  9. package/dist/client/client.d.ts +93 -0
  10. package/dist/client/client.d.ts.map +1 -0
  11. package/dist/client/client.js +393 -0
  12. package/dist/client/client.js.map +1 -0
  13. package/dist/client/config.d.ts +112 -0
  14. package/dist/client/config.d.ts.map +1 -0
  15. package/dist/client/config.js +10 -0
  16. package/dist/client/config.js.map +1 -0
  17. package/dist/client/fn-map.d.ts +61 -0
  18. package/dist/client/fn-map.d.ts.map +1 -0
  19. package/dist/client/fn-map.js +2 -0
  20. package/dist/client/fn-map.js.map +1 -0
  21. package/dist/client/freeze.d.ts +32 -0
  22. package/dist/client/freeze.d.ts.map +1 -0
  23. package/dist/client/freeze.js +42 -0
  24. package/dist/client/freeze.js.map +1 -0
  25. package/dist/client/hooks.d.ts +49 -0
  26. package/dist/client/hooks.d.ts.map +1 -0
  27. package/dist/client/hooks.js +2 -0
  28. package/dist/client/hooks.js.map +1 -0
  29. package/dist/client/index.d.ts +7 -0
  30. package/dist/client/index.d.ts.map +1 -0
  31. package/dist/client/index.js +2 -0
  32. package/dist/client/index.js.map +1 -0
  33. package/dist/delegation/cache.d.ts +133 -0
  34. package/dist/delegation/cache.d.ts.map +1 -0
  35. package/dist/delegation/cache.js +258 -0
  36. package/dist/delegation/cache.js.map +1 -0
  37. package/dist/delegation/errors.d.ts +44 -0
  38. package/dist/delegation/errors.d.ts.map +1 -0
  39. package/dist/delegation/errors.js +62 -0
  40. package/dist/delegation/errors.js.map +1 -0
  41. package/dist/delegation/index.d.ts +6 -0
  42. package/dist/delegation/index.d.ts.map +1 -0
  43. package/dist/delegation/index.js +4 -0
  44. package/dist/delegation/index.js.map +1 -0
  45. package/dist/delegation/policy.d.ts +175 -0
  46. package/dist/delegation/policy.d.ts.map +1 -0
  47. package/dist/delegation/policy.js +109 -0
  48. package/dist/delegation/policy.js.map +1 -0
  49. package/dist/errors/errors.d.ts +103 -0
  50. package/dist/errors/errors.d.ts.map +1 -0
  51. package/dist/errors/errors.js +170 -0
  52. package/dist/errors/errors.js.map +1 -0
  53. package/dist/errors/index.d.ts +4 -0
  54. package/dist/errors/index.d.ts.map +1 -0
  55. package/dist/errors/index.js +3 -0
  56. package/dist/errors/index.js.map +1 -0
  57. package/dist/errors/session-error.d.ts +21 -0
  58. package/dist/errors/session-error.d.ts.map +1 -0
  59. package/dist/errors/session-error.js +25 -0
  60. package/dist/errors/session-error.js.map +1 -0
  61. package/dist/index.d.ts +17 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +10 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/pacer/batcher.d.ts +11 -0
  66. package/dist/pacer/batcher.d.ts.map +1 -0
  67. package/dist/pacer/batcher.js +15 -0
  68. package/dist/pacer/batcher.js.map +1 -0
  69. package/dist/pacer/defaults.d.ts +18 -0
  70. package/dist/pacer/defaults.d.ts.map +1 -0
  71. package/dist/pacer/defaults.js +18 -0
  72. package/dist/pacer/defaults.js.map +1 -0
  73. package/dist/pacer/index.d.ts +10 -0
  74. package/dist/pacer/index.d.ts.map +1 -0
  75. package/dist/pacer/index.js +11 -0
  76. package/dist/pacer/index.js.map +1 -0
  77. package/dist/pacer/queue.d.ts +11 -0
  78. package/dist/pacer/queue.d.ts.map +1 -0
  79. package/dist/pacer/queue.js +15 -0
  80. package/dist/pacer/queue.js.map +1 -0
  81. package/dist/pacer/rate-limiter.d.ts +11 -0
  82. package/dist/pacer/rate-limiter.d.ts.map +1 -0
  83. package/dist/pacer/rate-limiter.js +15 -0
  84. package/dist/pacer/rate-limiter.js.map +1 -0
  85. package/dist/pacer/retry.d.ts +14 -0
  86. package/dist/pacer/retry.d.ts.map +1 -0
  87. package/dist/pacer/retry.js +50 -0
  88. package/dist/pacer/retry.js.map +1 -0
  89. package/dist/persistence/index.d.ts +3 -0
  90. package/dist/persistence/index.d.ts.map +1 -0
  91. package/dist/persistence/index.js +2 -0
  92. package/dist/persistence/index.js.map +1 -0
  93. package/dist/persistence/persistence.d.ts +52 -0
  94. package/dist/persistence/persistence.d.ts.map +1 -0
  95. package/dist/persistence/persistence.js +32 -0
  96. package/dist/persistence/persistence.js.map +1 -0
  97. package/dist/pool/errors.d.ts +13 -0
  98. package/dist/pool/errors.d.ts.map +1 -0
  99. package/dist/pool/errors.js +16 -0
  100. package/dist/pool/errors.js.map +1 -0
  101. package/dist/pool/index.d.ts +4 -0
  102. package/dist/pool/index.d.ts.map +1 -0
  103. package/dist/pool/index.js +3 -0
  104. package/dist/pool/index.js.map +1 -0
  105. package/dist/pool/pool.d.ts +63 -0
  106. package/dist/pool/pool.d.ts.map +1 -0
  107. package/dist/pool/pool.js +87 -0
  108. package/dist/pool/pool.js.map +1 -0
  109. package/dist/redirect/errors.d.ts +45 -0
  110. package/dist/redirect/errors.d.ts.map +1 -0
  111. package/dist/redirect/errors.js +52 -0
  112. package/dist/redirect/errors.js.map +1 -0
  113. package/dist/redirect/index.d.ts +4 -0
  114. package/dist/redirect/index.d.ts.map +1 -0
  115. package/dist/redirect/index.js +3 -0
  116. package/dist/redirect/index.js.map +1 -0
  117. package/dist/redirect/policy.d.ts +79 -0
  118. package/dist/redirect/policy.d.ts.map +1 -0
  119. package/dist/redirect/policy.js +69 -0
  120. package/dist/redirect/policy.js.map +1 -0
  121. package/dist/schema/call-args.d.ts +6 -0
  122. package/dist/schema/call-args.d.ts.map +1 -0
  123. package/dist/schema/call-args.js +21 -0
  124. package/dist/schema/call-args.js.map +1 -0
  125. package/dist/schema/class-proxy.d.ts +8 -0
  126. package/dist/schema/class-proxy.d.ts.map +1 -0
  127. package/dist/schema/class-proxy.js +38 -0
  128. package/dist/schema/class-proxy.js.map +1 -0
  129. package/dist/schema/extract.d.ts +9 -0
  130. package/dist/schema/extract.d.ts.map +1 -0
  131. package/dist/schema/extract.js +16 -0
  132. package/dist/schema/extract.js.map +1 -0
  133. package/dist/schema/from-entries.d.ts +12 -0
  134. package/dist/schema/from-entries.d.ts.map +1 -0
  135. package/dist/schema/from-entries.js +39 -0
  136. package/dist/schema/from-entries.js.map +1 -0
  137. package/dist/schema/index.d.ts +11 -0
  138. package/dist/schema/index.d.ts.map +1 -0
  139. package/dist/schema/index.js +9 -0
  140. package/dist/schema/index.js.map +1 -0
  141. package/dist/schema/node-proxy.d.ts +9 -0
  142. package/dist/schema/node-proxy.d.ts.map +1 -0
  143. package/dist/schema/node-proxy.js +32 -0
  144. package/dist/schema/node-proxy.js.map +1 -0
  145. package/dist/schema/proxy-types.d.ts +112 -0
  146. package/dist/schema/proxy-types.d.ts.map +1 -0
  147. package/dist/schema/proxy-types.js +2 -0
  148. package/dist/schema/proxy-types.js.map +1 -0
  149. package/dist/schema/registry.d.ts +51 -0
  150. package/dist/schema/registry.d.ts.map +1 -0
  151. package/dist/schema/registry.js +111 -0
  152. package/dist/schema/registry.js.map +1 -0
  153. package/dist/schema/resolve.d.ts +26 -0
  154. package/dist/schema/resolve.d.ts.map +1 -0
  155. package/dist/schema/resolve.js +27 -0
  156. package/dist/schema/resolve.js.map +1 -0
  157. package/dist/schema/typed-client.d.ts +10 -0
  158. package/dist/schema/typed-client.d.ts.map +1 -0
  159. package/dist/schema/typed-client.js +26 -0
  160. package/dist/schema/typed-client.js.map +1 -0
  161. package/dist/schema/types.d.ts +9 -0
  162. package/dist/schema/types.d.ts.map +1 -0
  163. package/dist/schema/types.js +2 -0
  164. package/dist/schema/types.js.map +1 -0
  165. package/dist/session/bound.d.ts +11 -0
  166. package/dist/session/bound.d.ts.map +1 -0
  167. package/dist/session/bound.js +24 -0
  168. package/dist/session/bound.js.map +1 -0
  169. package/dist/session/client-session.d.ts +61 -0
  170. package/dist/session/client-session.d.ts.map +1 -0
  171. package/dist/session/client-session.js +635 -0
  172. package/dist/session/client-session.js.map +1 -0
  173. package/dist/session/config.d.ts +157 -0
  174. package/dist/session/config.d.ts.map +1 -0
  175. package/dist/session/config.js +2 -0
  176. package/dist/session/config.js.map +1 -0
  177. package/dist/session/errors/index.d.ts +6 -0
  178. package/dist/session/errors/index.d.ts.map +1 -0
  179. package/dist/session/errors/index.js +8 -0
  180. package/dist/session/errors/index.js.map +1 -0
  181. package/dist/session/errors/session-errors.d.ts +52 -0
  182. package/dist/session/errors/session-errors.d.ts.map +1 -0
  183. package/dist/session/errors/session-errors.js +70 -0
  184. package/dist/session/errors/session-errors.js.map +1 -0
  185. package/dist/session/forward.d.ts +14 -0
  186. package/dist/session/forward.d.ts.map +1 -0
  187. package/dist/session/forward.js +44 -0
  188. package/dist/session/forward.js.map +1 -0
  189. package/dist/session/helpers.d.ts +23 -0
  190. package/dist/session/helpers.d.ts.map +1 -0
  191. package/dist/session/helpers.js +56 -0
  192. package/dist/session/helpers.js.map +1 -0
  193. package/dist/session/hooks.d.ts +140 -0
  194. package/dist/session/hooks.d.ts.map +1 -0
  195. package/dist/session/hooks.js +2 -0
  196. package/dist/session/hooks.js.map +1 -0
  197. package/dist/session/index.d.ts +6 -0
  198. package/dist/session/index.d.ts.map +1 -0
  199. package/dist/session/index.js +3 -0
  200. package/dist/session/index.js.map +1 -0
  201. package/dist/session/types.d.ts +38 -0
  202. package/dist/session/types.d.ts.map +1 -0
  203. package/dist/session/types.js +2 -0
  204. package/dist/session/types.js.map +1 -0
  205. package/dist/transport/binary.d.ts +15 -0
  206. package/dist/transport/binary.d.ts.map +1 -0
  207. package/dist/transport/binary.js +2 -0
  208. package/dist/transport/binary.js.map +1 -0
  209. package/dist/transport/contract.d.ts +58 -0
  210. package/dist/transport/contract.d.ts.map +1 -0
  211. package/dist/transport/contract.js +11 -0
  212. package/dist/transport/contract.js.map +1 -0
  213. package/dist/transport/fetch.d.ts +28 -0
  214. package/dist/transport/fetch.d.ts.map +1 -0
  215. package/dist/transport/fetch.js +74 -0
  216. package/dist/transport/fetch.js.map +1 -0
  217. package/dist/transport/http-envelope.d.ts +49 -0
  218. package/dist/transport/http-envelope.d.ts.map +1 -0
  219. package/dist/transport/http-envelope.js +405 -0
  220. package/dist/transport/http-envelope.js.map +1 -0
  221. package/dist/transport/http-routed.d.ts +61 -0
  222. package/dist/transport/http-routed.d.ts.map +1 -0
  223. package/dist/transport/http-routed.js +409 -0
  224. package/dist/transport/http-routed.js.map +1 -0
  225. package/dist/transport/index.d.ts +8 -0
  226. package/dist/transport/index.d.ts.map +1 -0
  227. package/dist/transport/index.js +6 -0
  228. package/dist/transport/index.js.map +1 -0
  229. package/dist/transport/request-id.d.ts +9 -0
  230. package/dist/transport/request-id.d.ts.map +1 -0
  231. package/dist/transport/request-id.js +12 -0
  232. package/dist/transport/request-id.js.map +1 -0
  233. package/dist/transport/ws-envelope.d.ts +74 -0
  234. package/dist/transport/ws-envelope.d.ts.map +1 -0
  235. package/dist/transport/ws-envelope.js +560 -0
  236. package/dist/transport/ws-envelope.js.map +1 -0
  237. package/dist/util/race-signal.d.ts +12 -0
  238. package/dist/util/race-signal.d.ts.map +1 -0
  239. package/dist/util/race-signal.js +34 -0
  240. package/dist/util/race-signal.js.map +1 -0
  241. package/dist/wire/controller.d.ts +38 -0
  242. package/dist/wire/controller.d.ts.map +1 -0
  243. package/dist/wire/controller.js +38 -0
  244. package/dist/wire/controller.js.map +1 -0
  245. package/dist/wire/index.d.ts +7 -0
  246. package/dist/wire/index.d.ts.map +1 -0
  247. package/dist/wire/index.js +4 -0
  248. package/dist/wire/index.js.map +1 -0
  249. package/dist/wire/meta.d.ts +81 -0
  250. package/dist/wire/meta.d.ts.map +1 -0
  251. package/dist/wire/meta.js +44 -0
  252. package/dist/wire/meta.js.map +1 -0
  253. package/dist/wire/sse.d.ts +67 -0
  254. package/dist/wire/sse.d.ts.map +1 -0
  255. package/dist/wire/sse.js +170 -0
  256. package/dist/wire/sse.js.map +1 -0
  257. package/package.json +104 -0
@@ -0,0 +1,258 @@
1
+ import { DelegationMintError } from './errors';
2
+ /**
3
+ * TTL-keyed credential cache with concurrent-mint deduplication. Spec §9.2.
4
+ *
5
+ * **Concurrent-mint invariant:** two concurrent `get(audience, delegation,
6
+ * attestation)` calls for the same structural triple share a single mint
7
+ * dispatch. On success, every waiter receives the same credential and the
8
+ * result is cached. On rejection, every waiter sees the rejection and the
9
+ * cache is NOT populated — subsequent calls re-attempt minting.
10
+ *
11
+ * **Structural key:** `delegation` and `attestation` are canonicalized
12
+ * by a deterministic JSON encoding (sorted keys; array order preserved —
13
+ * see `canonicalEncode` docstring). Policies returning fresh-but-equal
14
+ * objects on every call hit the same cache entry.
15
+ *
16
+ * **Attestation distinct from undefined:** `undefined` attestation and
17
+ * `{ kind: 'identity', self: true }` attestation are distinct keys (§9.2.1)
18
+ * even though the kernel adapter defaults the former to the latter at
19
+ * the wire — they produce distinguishable outer-envelope claims.
20
+ */
21
+ export class DelegationCache {
22
+ entries = new Map();
23
+ inFlight = new Map();
24
+ // Secondary index: audience → set of canonical cache keys. Maintained on
25
+ // every insert / evict / clear so `evictAudience` is O(entries-for-audience)
26
+ // instead of O(all-entries). Invariant: every entry in `entries` has its
27
+ // key in `byAudience.get(entry.audience)`, and vice versa.
28
+ byAudience = new Map();
29
+ mint;
30
+ defaultTtl;
31
+ maxSize;
32
+ now;
33
+ clock = 0;
34
+ // Bumped by `clear()`. Mints captured before the bump skip the post-resolve
35
+ // insert so an intentional reset actually clears — preserves the docstring
36
+ // invariant "results bypass the now-cleared cache."
37
+ clearEpoch = 0;
38
+ constructor(config) {
39
+ this.mint = config.mint;
40
+ this.defaultTtl = config.ttl ?? 300;
41
+ this.maxSize = config.maxSize ?? 64;
42
+ this.now = config.now ?? (() => Date.now() / 1000);
43
+ if (this.maxSize < 1) {
44
+ throw new Error(`DelegationCache.maxSize must be >= 1 (got ${this.maxSize})`);
45
+ }
46
+ }
47
+ /**
48
+ * Returns a delegated credential for `(audience, delegation, attestation)`.
49
+ * Cache hit returns synchronously-resolved; miss coalesces a single mint
50
+ * across concurrent callers on the same structural key.
51
+ *
52
+ * Throws `DelegationMintError` (wrapping the mint's cause) on mint
53
+ * failure. The failure is NOT cached.
54
+ */
55
+ async get(audience, delegation, attestation) {
56
+ const key = delegationCacheKey(audience, delegation, attestation);
57
+ const now = this.now();
58
+ const cached = this.entries.get(key);
59
+ if (cached && cached.expiresAt > now) {
60
+ cached.lastUsed = ++this.clock;
61
+ return cached.credential;
62
+ }
63
+ if (cached)
64
+ this.deleteEntry(key);
65
+ const pending = this.inFlight.get(key);
66
+ if (pending)
67
+ return (await pending.promise).credential;
68
+ const mintEpoch = this.clearEpoch;
69
+ const promise = this.mint(audience, delegation, attestation)
70
+ .then((result) => {
71
+ // undefined → use default; 0 → don't cache (explicit mint opt-out).
72
+ const ttl = result.ttl === undefined ? this.defaultTtl : result.ttl;
73
+ // Skip the insert if clear() ran after this mint was launched —
74
+ // repopulating would undo the consumer's explicit reset.
75
+ if (ttl > 0 && this.clearEpoch === mintEpoch) {
76
+ this.insert(key, {
77
+ credential: result.credential,
78
+ expiresAt: this.now() + ttl,
79
+ lastUsed: ++this.clock,
80
+ audience,
81
+ });
82
+ }
83
+ return result;
84
+ })
85
+ .catch((cause) => {
86
+ throw new DelegationMintError(audience, delegation, attestation, {
87
+ cause: cause,
88
+ });
89
+ })
90
+ .finally(() => {
91
+ // Evict the in-flight record regardless of outcome so the next call
92
+ // retries on failure ("don't cache rejection") and observes the
93
+ // fresh cache entry on success.
94
+ this.inFlight.delete(key);
95
+ });
96
+ this.inFlight.set(key, { promise });
97
+ const result = await promise;
98
+ return result.credential;
99
+ }
100
+ /**
101
+ * Manually insert an entry. Used by consumers minting through a
102
+ * custom path outside `get`'s default mint flow.
103
+ */
104
+ set(audience, credential, ttl, delegation, attestation) {
105
+ if (ttl <= 0)
106
+ return;
107
+ const key = delegationCacheKey(audience, delegation, attestation);
108
+ this.insert(key, {
109
+ credential,
110
+ expiresAt: this.now() + ttl,
111
+ lastUsed: ++this.clock,
112
+ audience,
113
+ });
114
+ }
115
+ /** Remove one entry. Idempotent — no-op for absent keys. */
116
+ evict(audience, delegation, attestation) {
117
+ const key = delegationCacheKey(audience, delegation, attestation);
118
+ this.deleteEntry(key);
119
+ }
120
+ /**
121
+ * Remove every entry for `audience`, regardless of delegation /
122
+ * attestation shape. Returns the number of entries removed.
123
+ * Idempotent — returns `0` for unknown audiences.
124
+ *
125
+ * Bulk invalidation primitive: a host whose user revokes consent for
126
+ * an audience calls this without enumerating every
127
+ * `(delegation, attestation)` combination it minted.
128
+ */
129
+ evictAudience(audience) {
130
+ const keys = this.byAudience.get(audience);
131
+ if (!keys)
132
+ return 0;
133
+ const removed = keys.size;
134
+ for (const key of keys)
135
+ this.entries.delete(key);
136
+ this.byAudience.delete(audience);
137
+ return removed;
138
+ }
139
+ /**
140
+ * Drop every cached entry. Does NOT abort in-flight mints — they
141
+ * resolve as usual but their results bypass the now-cleared cache.
142
+ */
143
+ clear() {
144
+ this.entries.clear();
145
+ this.byAudience.clear();
146
+ this.clearEpoch++;
147
+ }
148
+ /** Entry count. Useful for tests; not a contract for production flow. */
149
+ get size() {
150
+ return this.entries.size;
151
+ }
152
+ insert(key, entry) {
153
+ if (this.entries.has(key)) {
154
+ // Overwrite-in-place: byAudience already tracks this key under the
155
+ // same audience (cache key is a function of audience; audience can't
156
+ // change for a given key).
157
+ this.entries.set(key, entry);
158
+ return;
159
+ }
160
+ if (this.entries.size >= this.maxSize)
161
+ this.evictLeastRecentlyUsed();
162
+ this.entries.set(key, entry);
163
+ this.indexKey(entry.audience, key);
164
+ }
165
+ indexKey(audience, key) {
166
+ let set = this.byAudience.get(audience);
167
+ if (!set) {
168
+ set = new Set();
169
+ this.byAudience.set(audience, set);
170
+ }
171
+ set.add(key);
172
+ }
173
+ deleteEntry(key) {
174
+ const entry = this.entries.get(key);
175
+ if (!entry)
176
+ return;
177
+ this.entries.delete(key);
178
+ const set = this.byAudience.get(entry.audience);
179
+ if (set) {
180
+ set.delete(key);
181
+ if (set.size === 0)
182
+ this.byAudience.delete(entry.audience);
183
+ }
184
+ }
185
+ evictLeastRecentlyUsed() {
186
+ let oldestKey;
187
+ let oldestTs = Number.POSITIVE_INFINITY;
188
+ for (const [key, entry] of this.entries) {
189
+ if (entry.lastUsed < oldestTs) {
190
+ oldestTs = entry.lastUsed;
191
+ oldestKey = key;
192
+ }
193
+ }
194
+ if (oldestKey !== undefined)
195
+ this.deleteEntry(oldestKey);
196
+ }
197
+ }
198
+ /**
199
+ * Build a deterministic cache key from `(audience, delegation, attestation)`.
200
+ *
201
+ * The `delegation` and `attestation` trees are serialised through
202
+ * `canonicalEncode`, which:
203
+ * - Sorts object keys alphabetically at every node (so
204
+ * `{ kind, expr }` and `{ expr, kind }` collide).
205
+ * - Preserves array order (union/intersect/exclude operands are
206
+ * position-sensitive in the canonical form; `Scope[]` inside
207
+ * `{ kind: 'scope' }` is also position-preserving — scope sorting
208
+ * is the caller's choice, not the cache's, per spec §9.2.1).
209
+ * - Emits `#none` for absent attestation (distinct from any present
210
+ * `SelfIdentityExpr` including `{ kind: 'identity', self: true }`).
211
+ *
212
+ * Exported so ClientSession's `#pendingMints` table stays keyed identically
213
+ * without duplicating the key-shape logic.
214
+ */
215
+ export function delegationCacheKey(audience, delegation, attestation) {
216
+ const delPart = canonicalEncode(delegation);
217
+ const attPart = attestation === undefined ? '#none' : canonicalEncode(attestation);
218
+ return `${audience}|d=${delPart}|a=${attPart}`;
219
+ }
220
+ /**
221
+ * Canonical JSON-ish encoder: object keys sorted alphabetically, arrays
222
+ * preserve order, primitives stringified. Used only for cache keying;
223
+ * consumers never parse the output.
224
+ *
225
+ * We don't reach for `JSON.stringify` directly because its key order
226
+ * tracks insertion, not alphabetical. A custom walk is ~25 LOC and
227
+ * eliminates the whole class of "policy returned equal-but-reordered
228
+ * objects → cache miss" bugs.
229
+ *
230
+ * `Uint8Array` gets an explicit `#bytes:` prefix + hex encoding. Without
231
+ * it, a bytes credential (`CredentialInput = string | Uint8Array`) would
232
+ * fall through the generic object branch — `Object.keys(uint8)` returns
233
+ * stringified numeric indices — and collide with a literal
234
+ * `Record<string, number>` leaf. The prefix is a sentinel that cannot
235
+ * appear in a valid `JSON.stringify(object)` output.
236
+ */
237
+ function canonicalEncode(value) {
238
+ if (value === null)
239
+ return 'null';
240
+ if (typeof value !== 'object')
241
+ return JSON.stringify(value);
242
+ if (value instanceof Uint8Array)
243
+ return `#bytes:${bytesToHex(value)}`;
244
+ if (Array.isArray(value)) {
245
+ return `[${value.map(canonicalEncode).join(',')}]`;
246
+ }
247
+ const obj = value;
248
+ const keys = Object.keys(obj).sort();
249
+ const parts = keys.map((k) => `${JSON.stringify(k)}:${canonicalEncode(obj[k])}`);
250
+ return `{${parts.join(',')}}`;
251
+ }
252
+ function bytesToHex(bytes) {
253
+ let s = '';
254
+ for (let i = 0; i < bytes.length; i++)
255
+ s += bytes[i].toString(16).padStart(2, '0');
256
+ return s;
257
+ }
258
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/delegation/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { SelfIdentityExpr, UnresolvedIdentityExpr } from '@astrale-os/kernel-core/expression';
2
+ import { ClientSessionError } from '../errors/session-error';
3
+ /**
4
+ * Delegation policy refused to mint for an audience.
5
+ *
6
+ * Thrown by ClientSession's dispatch loop when `DelegationPolicy.decide` returns
7
+ * `{ allow: false, reason }`. The `audience`, `delegation`, and
8
+ * `attestation` the call was about to mint for are preserved so a UI can
9
+ * re-prompt with the full context of what was being proposed. The values
10
+ * are **pre-refine** — if the policy conceptually considered a refined
11
+ * shape before refusing, the pre-refine shape is what's reported here
12
+ * (that's what was proposed to `decide`). Spec §9.3.1 / §10.3.
13
+ */
14
+ export declare class DelegationRefusedError extends ClientSessionError {
15
+ readonly audience: string;
16
+ readonly delegation: UnresolvedIdentityExpr;
17
+ readonly attestation: SelfIdentityExpr | undefined;
18
+ readonly reason: string;
19
+ constructor(audience: string, delegation: UnresolvedIdentityExpr, attestation: SelfIdentityExpr | undefined, reason: string, options?: ErrorOptions);
20
+ }
21
+ /**
22
+ * `DelegationCache.get` dispatched a mint that threw.
23
+ *
24
+ * Wraps the underlying error (kernel auth error, network failure, etc.)
25
+ * as `cause`. The cache does NOT retain a failed mint — subsequent
26
+ * calls re-attempt minting. Spec §9.2 "don't cache rejection" + §10.3.
27
+ */
28
+ export declare class DelegationMintError extends ClientSessionError {
29
+ readonly audience: string;
30
+ readonly delegation: UnresolvedIdentityExpr;
31
+ readonly attestation: SelfIdentityExpr | undefined;
32
+ constructor(audience: string, delegation: UnresolvedIdentityExpr, attestation: SelfIdentityExpr | undefined, options?: ErrorOptions);
33
+ }
34
+ /**
35
+ * A `DelegationPolicy.decide` implementation itself threw.
36
+ *
37
+ * Programmer error at the policy layer. Wraps the thrown error as
38
+ * `cause`. Consumers treating this as "refused" is safe. Spec §10.3.
39
+ */
40
+ export declare class DelegationPolicyError extends ClientSessionError {
41
+ readonly audience: string | undefined;
42
+ constructor(audience: string | undefined, options?: ErrorOptions);
43
+ }
44
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/delegation/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAA;AAElG,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAE5D;;;;;;;;;;GAUG;AACH,qBAAa,sBAAuB,SAAQ,kBAAkB;aAE1C,QAAQ,EAAE,MAAM;aAChB,UAAU,EAAE,sBAAsB;aAClC,WAAW,EAAE,gBAAgB,GAAG,SAAS;aACzC,MAAM,EAAE,MAAM;IAJhC,YACkB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,sBAAsB,EAClC,WAAW,EAAE,gBAAgB,GAAG,SAAS,EACzC,MAAM,EAAE,MAAM,EAC9B,OAAO,CAAC,EAAE,YAAY,EAIvB;CACF;AAED;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,kBAAkB;aAEvC,QAAQ,EAAE,MAAM;aAChB,UAAU,EAAE,sBAAsB;aAClC,WAAW,EAAE,gBAAgB,GAAG,SAAS;IAH3D,YACkB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,sBAAsB,EAClC,WAAW,EAAE,gBAAgB,GAAG,SAAS,EACzD,OAAO,CAAC,EAAE,YAAY,EAIvB;CACF;AAED;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,kBAAkB;aAEzC,QAAQ,EAAE,MAAM,GAAG,SAAS;IAD9C,YACkB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5C,OAAO,CAAC,EAAE,YAAY,EAUvB;CACF"}
@@ -0,0 +1,62 @@
1
+ import { ClientSessionError } from '../errors/session-error';
2
+ /**
3
+ * Delegation policy refused to mint for an audience.
4
+ *
5
+ * Thrown by ClientSession's dispatch loop when `DelegationPolicy.decide` returns
6
+ * `{ allow: false, reason }`. The `audience`, `delegation`, and
7
+ * `attestation` the call was about to mint for are preserved so a UI can
8
+ * re-prompt with the full context of what was being proposed. The values
9
+ * are **pre-refine** — if the policy conceptually considered a refined
10
+ * shape before refusing, the pre-refine shape is what's reported here
11
+ * (that's what was proposed to `decide`). Spec §9.3.1 / §10.3.
12
+ */
13
+ export class DelegationRefusedError extends ClientSessionError {
14
+ audience;
15
+ delegation;
16
+ attestation;
17
+ reason;
18
+ constructor(audience, delegation, attestation, reason, options) {
19
+ super(`Delegation refused for "${audience}": ${reason}`, 'DELEGATION_REFUSED', options);
20
+ this.audience = audience;
21
+ this.delegation = delegation;
22
+ this.attestation = attestation;
23
+ this.reason = reason;
24
+ this.name = 'DelegationRefusedError';
25
+ }
26
+ }
27
+ /**
28
+ * `DelegationCache.get` dispatched a mint that threw.
29
+ *
30
+ * Wraps the underlying error (kernel auth error, network failure, etc.)
31
+ * as `cause`. The cache does NOT retain a failed mint — subsequent
32
+ * calls re-attempt minting. Spec §9.2 "don't cache rejection" + §10.3.
33
+ */
34
+ export class DelegationMintError extends ClientSessionError {
35
+ audience;
36
+ delegation;
37
+ attestation;
38
+ constructor(audience, delegation, attestation, options) {
39
+ super(`Delegation mint failed for "${audience}"`, 'DELEGATION_MINT_FAILED', options);
40
+ this.audience = audience;
41
+ this.delegation = delegation;
42
+ this.attestation = attestation;
43
+ this.name = 'DelegationMintError';
44
+ }
45
+ }
46
+ /**
47
+ * A `DelegationPolicy.decide` implementation itself threw.
48
+ *
49
+ * Programmer error at the policy layer. Wraps the thrown error as
50
+ * `cause`. Consumers treating this as "refused" is safe. Spec §10.3.
51
+ */
52
+ export class DelegationPolicyError extends ClientSessionError {
53
+ audience;
54
+ constructor(audience, options) {
55
+ super(audience === undefined
56
+ ? 'Delegation policy threw during audience derivation'
57
+ : `Delegation policy threw for audience "${audience}"`, 'DELEGATION_POLICY_THREW', options);
58
+ this.audience = audience;
59
+ this.name = 'DelegationPolicyError';
60
+ }
61
+ }
62
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/delegation/errors.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAE5D;;;;;;;;;;GAUG;AACH,MAAM,OAAO,sBAAuB,SAAQ,kBAAkB;IAE1C,QAAQ;IACR,UAAU;IACV,WAAW;IACX,MAAM;IAJxB,YACkB,QAAgB,EAChB,UAAkC,EAClC,WAAyC,EACzC,MAAc,EAC9B,OAAsB;QAEtB,KAAK,CAAC,2BAA2B,QAAQ,MAAM,MAAM,EAAE,EAAE,oBAAoB,EAAE,OAAO,CAAC,CAAA;wBANvE,QAAQ;0BACR,UAAU;2BACV,WAAW;sBACX,MAAM;QAItB,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAA;IACtC,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,mBAAoB,SAAQ,kBAAkB;IAEvC,QAAQ;IACR,UAAU;IACV,WAAW;IAH7B,YACkB,QAAgB,EAChB,UAAkC,EAClC,WAAyC,EACzD,OAAsB;QAEtB,KAAK,CAAC,+BAA+B,QAAQ,GAAG,EAAE,wBAAwB,EAAE,OAAO,CAAC,CAAA;wBALpE,QAAQ;0BACR,UAAU;2BACV,WAAW;QAI3B,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;IACnC,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,OAAO,qBAAsB,SAAQ,kBAAkB;IAEzC,QAAQ;IAD1B,YACkB,QAA4B,EAC5C,OAAsB;QAEtB,KAAK,CACH,QAAQ,KAAK,SAAS;YACpB,CAAC,CAAC,oDAAoD;YACtD,CAAC,CAAC,yCAAyC,QAAQ,GAAG,EACxD,yBAAyB,EACzB,OAAO,CACR,CAAA;wBATe,QAAQ;QAUxB,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAA;IACrC,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ export type { DelegationCacheConfig, DelegationMintFn, DelegationMintResult } from './cache';
2
+ export { DelegationCache } from './cache';
3
+ export type { DelegationContext, DelegationDecision, DelegationPolicy } from './policy';
4
+ export { allowAllDelegations, allowlistDelegations, promptDelegations, scopedSelf } from './policy';
5
+ export { DelegationMintError, DelegationPolicyError, DelegationRefusedError } from './errors';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/delegation/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAC5F,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AACvF,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACnG,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,4 @@
1
+ export { DelegationCache } from './cache';
2
+ export { allowAllDelegations, allowlistDelegations, promptDelegations, scopedSelf } from './policy';
3
+ export { DelegationMintError, DelegationPolicyError, DelegationRefusedError } from './errors';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/delegation/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAEzC,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACnG,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,175 @@
1
+ import type { Scope, SelfIdentityExpr, UnresolvedIdentityExpr } from '@astrale-os/kernel-core/expression';
2
+ /**
3
+ * Context passed to every `DelegationPolicy` method. Carries hop history
4
+ * and correlation id so policies can branch on "are we on a redirect
5
+ * hop?" or thread the correlation id into an external audit stream
6
+ * without closure tracking.
7
+ */
8
+ export type DelegationContext = {
9
+ /** URL the call is about to dispatch to. */
10
+ target: string;
11
+ /** Method name being dispatched. */
12
+ method: string;
13
+ /** Params the call carries. */
14
+ params: unknown;
15
+ /** Correlation id of the top-level `session.call`. */
16
+ correlationId: string;
17
+ /**
18
+ * Hop chain so far. `hops[0]` is the initial target; the last entry is
19
+ * the *current* hop being dispatched. On the first iteration of the
20
+ * dispatch loop the array has one element; each followed redirect
21
+ * appends one more before the next iteration re-runs the policy.
22
+ */
23
+ hops: ReadonlyArray<{
24
+ url: string;
25
+ at: number;
26
+ }>;
27
+ };
28
+ /**
29
+ * Consent decision for a single mint.
30
+ *
31
+ * - `{ allow: true }` — accept the pre-computed `(delegation, attestation)` as-is.
32
+ * - `{ allow: true, delegation?, attestation? }` — accept with refinement:
33
+ * substitute the provided field(s) before `DelegationCache.get`. The
34
+ * post-refine values become the cache key and what `onDelegationMint`
35
+ * reports. Either field is optional; the unrefined fields stay
36
+ * pre-computed. Refinement is one-shot — ClientSession does not re-invoke
37
+ * `decide` on the refined shape.
38
+ * - `{ allow: false, reason }` — causes ClientSession to throw
39
+ * `DelegationRefusedError`. The error carries the pre-refine shape.
40
+ *
41
+ * Spec §9.3 / §9.3.1. A single-branch `allow: true` with optional
42
+ * refinement fields is what TypeScript narrows cleanly — a two-branch
43
+ * union of `{ allow: true }` and `{ allow: true, delegation?, attestation? }`
44
+ * would be indistinguishable to the narrower and forces `as` casts at
45
+ * every read site.
46
+ */
47
+ export type DelegationDecision = {
48
+ allow: true;
49
+ delegation?: UnresolvedIdentityExpr;
50
+ attestation?: SelfIdentityExpr;
51
+ } | {
52
+ allow: false;
53
+ reason: string;
54
+ };
55
+ /**
56
+ * Audience + delegation + attestation + consent surface. Spec §9.3.
57
+ *
58
+ * Mirrors the server-side `MintDelegationParams` triple in
59
+ * `kernel/ports/authentication/authentication.port.ts` one-for-one. Owns
60
+ * ONLY decision-layer concerns. Does NOT own the cache (that's
61
+ * `DelegationCache`), the mint itself (injected via cache config),
62
+ * credential-chaining resolution (kernel-side at mint/auth time), or
63
+ * iterated negotiation (`decide` is one-shot).
64
+ */
65
+ export interface DelegationPolicy {
66
+ /**
67
+ * Map a target to an audience. Returning `undefined` skips delegation
68
+ * — the dispatch uses the base credential directly. Sync, cheap, pure
69
+ * (runs on every wire dispatch).
70
+ *
71
+ * `ctx` carries hop history and correlation id — the first three args
72
+ * are duplicated into `ctx` so a 3-arg policy still type-checks (the
73
+ * fourth arg is additive, not a breaking rename).
74
+ */
75
+ audienceFor(target: string, method: string, params: unknown, ctx: DelegationContext): string | undefined;
76
+ /**
77
+ * Derive the `UnresolvedIdentityExpr` this mint should carry. Return
78
+ * `undefined` to use ClientSession's default `{ kind: 'identity', self: true }`.
79
+ * Invoked only when `audienceFor` returned a non-undefined audience.
80
+ *
81
+ * The full expression tree the kernel accepts — `union`, `intersect`,
82
+ * `exclude`, `scope`-wrapped sub-expressions, `Cred(...)` leaves. For
83
+ * the common "narrow self-delegation by scope" case use `scopedSelf`.
84
+ *
85
+ * Sync — delegation shape is a rule, not an I/O lookup. Consumers who
86
+ * need async derivation compute outside and pass
87
+ * `ClientSessionCallOptions.delegation`; `decide` can also refine the shape
88
+ * asynchronously (§9.3.1).
89
+ */
90
+ delegationFor?(target: string, method: string, params: unknown, ctx: DelegationContext): UnresolvedIdentityExpr | undefined;
91
+ /**
92
+ * Derive the `SelfIdentityExpr` attestation for the mint's outer
93
+ * envelope. Return `undefined` to omit the attestation param (kernel
94
+ * adapter defaults it to `{ kind: 'identity', self: true }`). Invoked
95
+ * only when `audienceFor` returned a non-undefined audience.
96
+ *
97
+ * Attestation asserts something about the delegator; it is signed into
98
+ * the outer envelope as metadata and is NOT enforced at authentication.
99
+ * Most consumers leave this absent.
100
+ */
101
+ attestationFor?(target: string, method: string, params: unknown, ctx: DelegationContext): SelfIdentityExpr | undefined;
102
+ /**
103
+ * Consent hook. Called AFTER `audienceFor` / `delegationFor` /
104
+ * `attestationFor` resolved and BEFORE `DelegationCache.get` issues the
105
+ * mint. Default when absent: implicit allow.
106
+ *
107
+ * `signal` propagates from the caller's `ClientSessionCallOptions.signal` —
108
+ * implementations that await UI input MUST race against it.
109
+ *
110
+ * May refine the mint shape (§9.3.1): returning
111
+ * `{ allow: true, delegation?, attestation? }` substitutes the
112
+ * provided field(s) before cache lookup. Plain `{ allow: true }` uses
113
+ * the pre-computed shape unchanged.
114
+ */
115
+ decide?(audience: string, delegation: UnresolvedIdentityExpr, attestation: SelfIdentityExpr | undefined, ctx: DelegationContext, signal: AbortSignal | undefined): DelegationDecision | Promise<DelegationDecision>;
116
+ }
117
+ /**
118
+ * Default policy: mint for any target whose origin differs from the
119
+ * default kernel's origin, allow implicitly.
120
+ *
121
+ * `default` is the `ClientSessionConfig.default` URL; same-origin targets skip
122
+ * delegation (the base credential is valid). Used when `ClientSessionConfig`
123
+ * omits `delegationPolicy`.
124
+ */
125
+ export declare function allowAllDelegations(opts: {
126
+ default: string;
127
+ }): DelegationPolicy;
128
+ /**
129
+ * Allowlist: only mint for audiences `allow` returns true for. Matches
130
+ * `RedirectPolicy`'s `allowlistPolicy` shape for symmetry.
131
+ *
132
+ * `delegation`, when supplied, is the expression applied to every mint
133
+ * this policy produces; per-call overrides via `ClientSessionCallOptions.delegation`
134
+ * still win. Same for `attestation`.
135
+ */
136
+ export declare function allowlistDelegations(opts: {
137
+ default: string;
138
+ allow: (audience: string) => boolean;
139
+ delegation?: UnresolvedIdentityExpr;
140
+ attestation?: SelfIdentityExpr;
141
+ }): DelegationPolicy;
142
+ /**
143
+ * UI prompt: ask the consumer for each new audience. Races `onPrompt`
144
+ * against the caller's `signal` so aborting the top-level call cancels
145
+ * the prompt.
146
+ *
147
+ * `allow` is a fast-path predicate (pre-approved audiences). Returning
148
+ * `false` from `allow` falls through to `onPrompt`.
149
+ *
150
+ * `onPrompt` receives the full mint shape (audience + delegation +
151
+ * attestation + ctx) so the UI can render what's actually being minted,
152
+ * not just the audience. This matters when the policy narrows by method:
153
+ * the user should see "write to partner kernel as self with READ | WRITE
154
+ * on nodes [X, Y]," not just "trust partner kernel."
155
+ */
156
+ export declare function promptDelegations(opts: {
157
+ default: string;
158
+ onPrompt: (audience: string, delegation: UnresolvedIdentityExpr, attestation: SelfIdentityExpr | undefined, ctx: DelegationContext) => Promise<boolean>;
159
+ allow?: (audience: string) => boolean;
160
+ delegation?: UnresolvedIdentityExpr;
161
+ attestation?: SelfIdentityExpr;
162
+ }): DelegationPolicy;
163
+ /**
164
+ * Build `{ kind: 'scope', scopes, expr: { kind: 'identity', self: true } }`
165
+ * — a self-delegation narrowed to the given scope set. This is the shape
166
+ * that was the *entire* delegation surface in an earlier draft of the
167
+ * spec, now expressible as one case of the full `UnresolvedIdentityExpr`
168
+ * algebra.
169
+ *
170
+ * Use for the common "vary perms by method" pattern. Reach for the raw
171
+ * `UnresolvedIdentityExpr` constructors when composing identities
172
+ * (union/intersect/exclude) or chaining credentials.
173
+ */
174
+ export declare function scopedSelf(scopes: readonly Scope[]): UnresolvedIdentityExpr;
175
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/delegation/policy.ts"],"names":[],"mappings":"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"}