@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.
- package/dist/client/batch.d.ts +26 -0
- package/dist/client/batch.d.ts.map +1 -0
- package/dist/client/batch.js +8 -0
- package/dist/client/batch.js.map +1 -0
- package/dist/client/bound.d.ts +27 -0
- package/dist/client/bound.d.ts.map +1 -0
- package/dist/client/bound.js +14 -0
- package/dist/client/bound.js.map +1 -0
- package/dist/client/client.d.ts +93 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +393 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/config.d.ts +112 -0
- package/dist/client/config.d.ts.map +1 -0
- package/dist/client/config.js +10 -0
- package/dist/client/config.js.map +1 -0
- package/dist/client/fn-map.d.ts +61 -0
- package/dist/client/fn-map.d.ts.map +1 -0
- package/dist/client/fn-map.js +2 -0
- package/dist/client/fn-map.js.map +1 -0
- package/dist/client/freeze.d.ts +32 -0
- package/dist/client/freeze.d.ts.map +1 -0
- package/dist/client/freeze.js +42 -0
- package/dist/client/freeze.js.map +1 -0
- package/dist/client/hooks.d.ts +49 -0
- package/dist/client/hooks.d.ts.map +1 -0
- package/dist/client/hooks.js +2 -0
- package/dist/client/hooks.js.map +1 -0
- package/dist/client/index.d.ts +7 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -0
- package/dist/delegation/cache.d.ts +133 -0
- package/dist/delegation/cache.d.ts.map +1 -0
- package/dist/delegation/cache.js +258 -0
- package/dist/delegation/cache.js.map +1 -0
- package/dist/delegation/errors.d.ts +44 -0
- package/dist/delegation/errors.d.ts.map +1 -0
- package/dist/delegation/errors.js +62 -0
- package/dist/delegation/errors.js.map +1 -0
- package/dist/delegation/index.d.ts +6 -0
- package/dist/delegation/index.d.ts.map +1 -0
- package/dist/delegation/index.js +4 -0
- package/dist/delegation/index.js.map +1 -0
- package/dist/delegation/policy.d.ts +175 -0
- package/dist/delegation/policy.d.ts.map +1 -0
- package/dist/delegation/policy.js +109 -0
- package/dist/delegation/policy.js.map +1 -0
- package/dist/errors/errors.d.ts +103 -0
- package/dist/errors/errors.d.ts.map +1 -0
- package/dist/errors/errors.js +170 -0
- package/dist/errors/errors.js.map +1 -0
- package/dist/errors/index.d.ts +4 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +3 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/session-error.d.ts +21 -0
- package/dist/errors/session-error.d.ts.map +1 -0
- package/dist/errors/session-error.js +25 -0
- package/dist/errors/session-error.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/pacer/batcher.d.ts +11 -0
- package/dist/pacer/batcher.d.ts.map +1 -0
- package/dist/pacer/batcher.js +15 -0
- package/dist/pacer/batcher.js.map +1 -0
- package/dist/pacer/defaults.d.ts +18 -0
- package/dist/pacer/defaults.d.ts.map +1 -0
- package/dist/pacer/defaults.js +18 -0
- package/dist/pacer/defaults.js.map +1 -0
- package/dist/pacer/index.d.ts +10 -0
- package/dist/pacer/index.d.ts.map +1 -0
- package/dist/pacer/index.js +11 -0
- package/dist/pacer/index.js.map +1 -0
- package/dist/pacer/queue.d.ts +11 -0
- package/dist/pacer/queue.d.ts.map +1 -0
- package/dist/pacer/queue.js +15 -0
- package/dist/pacer/queue.js.map +1 -0
- package/dist/pacer/rate-limiter.d.ts +11 -0
- package/dist/pacer/rate-limiter.d.ts.map +1 -0
- package/dist/pacer/rate-limiter.js +15 -0
- package/dist/pacer/rate-limiter.js.map +1 -0
- package/dist/pacer/retry.d.ts +14 -0
- package/dist/pacer/retry.d.ts.map +1 -0
- package/dist/pacer/retry.js +50 -0
- package/dist/pacer/retry.js.map +1 -0
- package/dist/persistence/index.d.ts +3 -0
- package/dist/persistence/index.d.ts.map +1 -0
- package/dist/persistence/index.js +2 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/persistence.d.ts +52 -0
- package/dist/persistence/persistence.d.ts.map +1 -0
- package/dist/persistence/persistence.js +32 -0
- package/dist/persistence/persistence.js.map +1 -0
- package/dist/pool/errors.d.ts +13 -0
- package/dist/pool/errors.d.ts.map +1 -0
- package/dist/pool/errors.js +16 -0
- package/dist/pool/errors.js.map +1 -0
- package/dist/pool/index.d.ts +4 -0
- package/dist/pool/index.d.ts.map +1 -0
- package/dist/pool/index.js +3 -0
- package/dist/pool/index.js.map +1 -0
- package/dist/pool/pool.d.ts +63 -0
- package/dist/pool/pool.d.ts.map +1 -0
- package/dist/pool/pool.js +87 -0
- package/dist/pool/pool.js.map +1 -0
- package/dist/redirect/errors.d.ts +45 -0
- package/dist/redirect/errors.d.ts.map +1 -0
- package/dist/redirect/errors.js +52 -0
- package/dist/redirect/errors.js.map +1 -0
- package/dist/redirect/index.d.ts +4 -0
- package/dist/redirect/index.d.ts.map +1 -0
- package/dist/redirect/index.js +3 -0
- package/dist/redirect/index.js.map +1 -0
- package/dist/redirect/policy.d.ts +79 -0
- package/dist/redirect/policy.d.ts.map +1 -0
- package/dist/redirect/policy.js +69 -0
- package/dist/redirect/policy.js.map +1 -0
- package/dist/schema/call-args.d.ts +6 -0
- package/dist/schema/call-args.d.ts.map +1 -0
- package/dist/schema/call-args.js +21 -0
- package/dist/schema/call-args.js.map +1 -0
- package/dist/schema/class-proxy.d.ts +8 -0
- package/dist/schema/class-proxy.d.ts.map +1 -0
- package/dist/schema/class-proxy.js +38 -0
- package/dist/schema/class-proxy.js.map +1 -0
- package/dist/schema/extract.d.ts +9 -0
- package/dist/schema/extract.d.ts.map +1 -0
- package/dist/schema/extract.js +16 -0
- package/dist/schema/extract.js.map +1 -0
- package/dist/schema/from-entries.d.ts +12 -0
- package/dist/schema/from-entries.d.ts.map +1 -0
- package/dist/schema/from-entries.js +39 -0
- package/dist/schema/from-entries.js.map +1 -0
- package/dist/schema/index.d.ts +11 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +9 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/node-proxy.d.ts +9 -0
- package/dist/schema/node-proxy.d.ts.map +1 -0
- package/dist/schema/node-proxy.js +32 -0
- package/dist/schema/node-proxy.js.map +1 -0
- package/dist/schema/proxy-types.d.ts +112 -0
- package/dist/schema/proxy-types.d.ts.map +1 -0
- package/dist/schema/proxy-types.js +2 -0
- package/dist/schema/proxy-types.js.map +1 -0
- package/dist/schema/registry.d.ts +51 -0
- package/dist/schema/registry.d.ts.map +1 -0
- package/dist/schema/registry.js +111 -0
- package/dist/schema/registry.js.map +1 -0
- package/dist/schema/resolve.d.ts +26 -0
- package/dist/schema/resolve.d.ts.map +1 -0
- package/dist/schema/resolve.js +27 -0
- package/dist/schema/resolve.js.map +1 -0
- package/dist/schema/typed-client.d.ts +10 -0
- package/dist/schema/typed-client.d.ts.map +1 -0
- package/dist/schema/typed-client.js +26 -0
- package/dist/schema/typed-client.js.map +1 -0
- package/dist/schema/types.d.ts +9 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +2 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/session/bound.d.ts +11 -0
- package/dist/session/bound.d.ts.map +1 -0
- package/dist/session/bound.js +24 -0
- package/dist/session/bound.js.map +1 -0
- package/dist/session/client-session.d.ts +61 -0
- package/dist/session/client-session.d.ts.map +1 -0
- package/dist/session/client-session.js +635 -0
- package/dist/session/client-session.js.map +1 -0
- package/dist/session/config.d.ts +157 -0
- package/dist/session/config.d.ts.map +1 -0
- package/dist/session/config.js +2 -0
- package/dist/session/config.js.map +1 -0
- package/dist/session/errors/index.d.ts +6 -0
- package/dist/session/errors/index.d.ts.map +1 -0
- package/dist/session/errors/index.js +8 -0
- package/dist/session/errors/index.js.map +1 -0
- package/dist/session/errors/session-errors.d.ts +52 -0
- package/dist/session/errors/session-errors.d.ts.map +1 -0
- package/dist/session/errors/session-errors.js +70 -0
- package/dist/session/errors/session-errors.js.map +1 -0
- package/dist/session/forward.d.ts +14 -0
- package/dist/session/forward.d.ts.map +1 -0
- package/dist/session/forward.js +44 -0
- package/dist/session/forward.js.map +1 -0
- package/dist/session/helpers.d.ts +23 -0
- package/dist/session/helpers.d.ts.map +1 -0
- package/dist/session/helpers.js +56 -0
- package/dist/session/helpers.js.map +1 -0
- package/dist/session/hooks.d.ts +140 -0
- package/dist/session/hooks.d.ts.map +1 -0
- package/dist/session/hooks.js +2 -0
- package/dist/session/hooks.js.map +1 -0
- package/dist/session/index.d.ts +6 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +3 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/types.d.ts +38 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +2 -0
- package/dist/session/types.js.map +1 -0
- package/dist/transport/binary.d.ts +15 -0
- package/dist/transport/binary.d.ts.map +1 -0
- package/dist/transport/binary.js +2 -0
- package/dist/transport/binary.js.map +1 -0
- package/dist/transport/contract.d.ts +58 -0
- package/dist/transport/contract.d.ts.map +1 -0
- package/dist/transport/contract.js +11 -0
- package/dist/transport/contract.js.map +1 -0
- package/dist/transport/fetch.d.ts +28 -0
- package/dist/transport/fetch.d.ts.map +1 -0
- package/dist/transport/fetch.js +74 -0
- package/dist/transport/fetch.js.map +1 -0
- package/dist/transport/http-envelope.d.ts +49 -0
- package/dist/transport/http-envelope.d.ts.map +1 -0
- package/dist/transport/http-envelope.js +405 -0
- package/dist/transport/http-envelope.js.map +1 -0
- package/dist/transport/http-routed.d.ts +61 -0
- package/dist/transport/http-routed.d.ts.map +1 -0
- package/dist/transport/http-routed.js +409 -0
- package/dist/transport/http-routed.js.map +1 -0
- package/dist/transport/index.d.ts +8 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +6 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/request-id.d.ts +9 -0
- package/dist/transport/request-id.d.ts.map +1 -0
- package/dist/transport/request-id.js +12 -0
- package/dist/transport/request-id.js.map +1 -0
- package/dist/transport/ws-envelope.d.ts +74 -0
- package/dist/transport/ws-envelope.d.ts.map +1 -0
- package/dist/transport/ws-envelope.js +560 -0
- package/dist/transport/ws-envelope.js.map +1 -0
- package/dist/util/race-signal.d.ts +12 -0
- package/dist/util/race-signal.d.ts.map +1 -0
- package/dist/util/race-signal.js +34 -0
- package/dist/util/race-signal.js.map +1 -0
- package/dist/wire/controller.d.ts +38 -0
- package/dist/wire/controller.d.ts.map +1 -0
- package/dist/wire/controller.js +38 -0
- package/dist/wire/controller.js.map +1 -0
- package/dist/wire/index.d.ts +7 -0
- package/dist/wire/index.d.ts.map +1 -0
- package/dist/wire/index.js +4 -0
- package/dist/wire/index.js.map +1 -0
- package/dist/wire/meta.d.ts +81 -0
- package/dist/wire/meta.d.ts.map +1 -0
- package/dist/wire/meta.js +44 -0
- package/dist/wire/meta.js.map +1 -0
- package/dist/wire/sse.d.ts +67 -0
- package/dist/wire/sse.d.ts.map +1 -0
- package/dist/wire/sse.js +170 -0
- package/dist/wire/sse.js.map +1 -0
- 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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"}
|