@caracalai/sdk 0.1.6-rc.3 → 0.2.0-rc.2
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/advanced.d.ts +11 -6
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +5 -5
- package/dist/advanced.js.map +1 -1
- package/dist/client.d.ts +460 -48
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1027 -156
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +45 -8
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +50 -18
- package/dist/context.js.map +1 -1
- package/dist/coordinator.d.ts +79 -11
- package/dist/coordinator.d.ts.map +1 -1
- package/dist/coordinator.js +181 -32
- package/dist/coordinator.js.map +1 -1
- package/dist/envelope.d.ts +14 -8
- package/dist/envelope.d.ts.map +1 -1
- package/dist/envelope.js +96 -54
- package/dist/envelope.js.map +1 -1
- package/dist/http.d.ts +14 -4
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +15 -8
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +10 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/primitives.d.ts +147 -42
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +426 -127
- package/dist/primitives.js.map +1 -1
- package/package.json +5 -5
package/dist/advanced.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export
|
|
1
|
+
export { HeaderAuthorization, HeaderTraceparent, HeaderTracestate, HeaderBaggage, BaggageAgentSession, BaggageDelegationEdge, BaggageParentEdge, BaggageSession, BaggageHop, MaxHop, formatTraceparent, parseTraceparent, encodeBaggage, parseBaggage, fromHeaders, decodeEnvelope, encodeEnvelope, toHeaders, } from './envelope.js';
|
|
2
|
+
export type { Envelope, HeaderGetter, HeaderSetter } from './envelope.js';
|
|
3
|
+
export { current, captureContext, bind, withOverrides, toEnvelope, fromEnvelope, fromVerifiedEnvelope, describeAuthority, } from './context.js';
|
|
4
|
+
export type { CaracalContext, AuthoritySummary, VerifiedClaims } from './context.js';
|
|
5
|
+
export { CoordinatorError, Lifecycle, startCoordinatorSession, terminateSession, acquireSessionLease, heartbeatSession, createDelegation, revokeDelegation, getInboundDelegation, listInboundDelegations, } from './coordinator.js';
|
|
6
|
+
export type { CoordinatorCallEvent, CoordinatorClient, SessionStatus, DelegationConstraints, StartSessionRequest, StartSessionResponse, DelegationRequest, DelegationResponse, HeartbeatResponse, InboundDelegation, } from './coordinator.js';
|
|
7
|
+
export { Authority, session, delegate, acceptDelegation, startSession, attachSession } from './primitives.js';
|
|
8
|
+
export type { AuthorityMode, SessionInput, DelegateInput, Delegation, StartSessionInput, SessionHandle, AttachSessionInput, } from './primitives.js';
|
|
9
|
+
export { caracalContextMiddleware, caracalFastifyHook } from './http.js';
|
|
10
|
+
export type { IncomingLike, FastifyRequestLike, ServerResponseLike, FastifyReplyLike, ConnectMiddleware } from './http.js';
|
|
6
11
|
export { Caracal } from './client.js';
|
|
7
|
-
export type { CaracalConfig,
|
|
12
|
+
export type { CaracalConfig, SessionOptions, DelegateOptions, LifecycleHook, CallOptions, TokenSource, ClientCredentials, CredentialsResolver, ClientSecretOptions, } from './client.js';
|
|
8
13
|
//# sourceMappingURL=advanced.d.ts.map
|
package/dist/advanced.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"advanced.d.ts","sourceRoot":"","sources":["../src/advanced.ts"],"names":[],"mappings":"AAUA,cAAc,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"advanced.d.ts","sourceRoot":"","sources":["../src/advanced.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,MAAM,EACN,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,WAAW,EACX,cAAc,EACd,cAAc,EACd,SAAS,GACV,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACzE,OAAO,EACL,OAAO,EACP,cAAc,EACd,IAAI,EACJ,aAAa,EACb,UAAU,EACV,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AACpF,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,uBAAuB,EACvB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC7G,YAAY,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,kBAAkB,GACnB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AACxE,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAC1H,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACrC,YAAY,EACV,aAAa,EACb,cAAc,EACd,eAAe,EACf,aAAa,EACb,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,aAAa,CAAA"}
|
package/dist/advanced.js
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* "@caracalai/sdk" entrypoint; reach for these when building a transport
|
|
8
8
|
* adapter or framework integration.
|
|
9
9
|
*/
|
|
10
|
-
export
|
|
11
|
-
export
|
|
12
|
-
export
|
|
13
|
-
export
|
|
14
|
-
export
|
|
10
|
+
export { HeaderAuthorization, HeaderTraceparent, HeaderTracestate, HeaderBaggage, BaggageAgentSession, BaggageDelegationEdge, BaggageParentEdge, BaggageSession, BaggageHop, MaxHop, formatTraceparent, parseTraceparent, encodeBaggage, parseBaggage, fromHeaders, decodeEnvelope, encodeEnvelope, toHeaders, } from './envelope.js';
|
|
11
|
+
export { current, captureContext, bind, withOverrides, toEnvelope, fromEnvelope, fromVerifiedEnvelope, describeAuthority, } from './context.js';
|
|
12
|
+
export { CoordinatorError, Lifecycle, startCoordinatorSession, terminateSession, acquireSessionLease, heartbeatSession, createDelegation, revokeDelegation, getInboundDelegation, listInboundDelegations, } from './coordinator.js';
|
|
13
|
+
export { Authority, session, delegate, acceptDelegation, startSession, attachSession } from './primitives.js';
|
|
14
|
+
export { caracalContextMiddleware, caracalFastifyHook } from './http.js';
|
|
15
15
|
export { Caracal } from './client.js';
|
|
16
16
|
//# sourceMappingURL=advanced.js.map
|
package/dist/advanced.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"advanced.js","sourceRoot":"","sources":["../src/advanced.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"advanced.js","sourceRoot":"","sources":["../src/advanced.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,UAAU,EACV,MAAM,EACN,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,WAAW,EACX,cAAc,EACd,cAAc,EACd,SAAS,GACV,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,OAAO,EACP,cAAc,EACd,IAAI,EACJ,aAAa,EACb,UAAU,EACV,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,gBAAgB,EAChB,SAAS,EACT,uBAAuB,EACvB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,kBAAkB,CAAA;AAazB,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAU7G,OAAO,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAExE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,64 +1,249 @@
|
|
|
1
|
-
import { type CaracalContext } from './context.js';
|
|
1
|
+
import { type CaracalContext, type VerifiedClaims } from './context.js';
|
|
2
2
|
import { type HeaderGetter } from './envelope.js';
|
|
3
|
-
import { type
|
|
4
|
-
import { type
|
|
5
|
-
import { type DelegationConstraints } from './coordinator.js';
|
|
3
|
+
import { type Authority, type Delegation, type SessionHandle } from './primitives.js';
|
|
4
|
+
import { type CoordinatorCallEvent, type CoordinatorClient, type DelegationConstraints } from './coordinator.js';
|
|
6
5
|
import type { JsonObject } from './json.js';
|
|
6
|
+
import { type ApprovalState, type OAuthEvent } from '@caracalai/oauth';
|
|
7
7
|
export interface ResourceBinding {
|
|
8
8
|
resourceId: string;
|
|
9
9
|
upstreamPrefix: string;
|
|
10
10
|
}
|
|
11
11
|
export type TokenSource = () => string | Promise<string>;
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
/** The credential triple a client-secret client acts as. */
|
|
13
|
+
export interface ClientCredentials {
|
|
14
14
|
zoneId: string;
|
|
15
15
|
applicationId: string;
|
|
16
|
+
clientSecret: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolves the current client credentials at each control-plane operation, so
|
|
20
|
+
* a secret rotated by a secrets manager or a credential provisioned after the
|
|
21
|
+
* process started is simply presented on the next exchange - the client is
|
|
22
|
+
* never rebuilt. Returning null or undefined means no usable credential exists
|
|
23
|
+
* yet; the operation fails closed with CredentialsUnavailableError.
|
|
24
|
+
*/
|
|
25
|
+
export type CredentialsResolver = () => ClientCredentials | null | undefined | Promise<ClientCredentials | null | undefined>;
|
|
26
|
+
/** A control-plane operation ran while the credentials resolver had no usable credential; the operation fails closed. */
|
|
27
|
+
export declare class CredentialsUnavailableError extends Error {
|
|
28
|
+
constructor();
|
|
29
|
+
}
|
|
30
|
+
/** A minted resource mandate and the lifetime the STS granted it. */
|
|
31
|
+
export interface MintedMandate {
|
|
32
|
+
token: string;
|
|
33
|
+
expiresInSeconds: number;
|
|
34
|
+
}
|
|
35
|
+
/** A federated Subject and the mandate proving it. */
|
|
36
|
+
export interface FederatedSubject {
|
|
37
|
+
/** Anchors Coordinator attribution when passed as subjectAuthorityRecordId; it does not alone propagate the user sub to later mints. */
|
|
38
|
+
subjectAuthorityRecordId: string;
|
|
39
|
+
/** The Subject mandate used for user-facing flows; it carries no resource authority. */
|
|
40
|
+
token: string;
|
|
41
|
+
expiresInSeconds: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Client-secret credential surface behind a configured Caracal client:
|
|
45
|
+
* resolves the acting identity, invalidates the cached lifecycle token after
|
|
46
|
+
* a server-side rejection, and mints scoped resource mandates for gateway
|
|
47
|
+
* calls. Integrators never construct one: `fromClientSecret` (and profile or
|
|
48
|
+
* environment detection) wires it into the client; the interface exists so
|
|
49
|
+
* the credential surface can be observed and faked in tests.
|
|
50
|
+
*/
|
|
51
|
+
export interface ClientSecretExchanger {
|
|
52
|
+
invalidate(): void;
|
|
53
|
+
/** The zone and application the credentials currently resolve to; fails closed when unresolved. */
|
|
54
|
+
identity(): Promise<{
|
|
55
|
+
zoneId: string;
|
|
56
|
+
applicationId: string;
|
|
57
|
+
credentialGeneration?: string;
|
|
58
|
+
}>;
|
|
59
|
+
mintMandate(resourceId: string, scopes: string[], opts?: {
|
|
60
|
+
sessionId?: string;
|
|
61
|
+
delegationId?: string;
|
|
62
|
+
ttlSeconds?: number;
|
|
63
|
+
approvalId?: string;
|
|
64
|
+
signal?: AbortSignal;
|
|
65
|
+
cache?: boolean;
|
|
66
|
+
}): Promise<MintedMandate>;
|
|
67
|
+
/** Exchanges an end user's external identity token for a Subject mandate; never cached. */
|
|
68
|
+
federateSubject(idToken: string, opts?: {
|
|
69
|
+
ttlSeconds?: number;
|
|
70
|
+
timeoutMs?: number;
|
|
71
|
+
signal?: AbortSignal;
|
|
72
|
+
}): Promise<MintedMandate>;
|
|
73
|
+
waitForApproval(approvalId: string, opts?: {
|
|
74
|
+
timeoutMs?: number;
|
|
75
|
+
signal?: AbortSignal;
|
|
76
|
+
}): Promise<ApprovalState>;
|
|
77
|
+
/** Attaches the observability sink token-exchange events report to. */
|
|
78
|
+
onEvent(cb: (event: OAuthEvent) => void): void;
|
|
79
|
+
}
|
|
80
|
+
export interface CaracalConfig {
|
|
81
|
+
coordinator: CoordinatorClient;
|
|
82
|
+
/** Static identity; omitted for a credentials-resolver client, which resolves it through the exchanger per operation. */
|
|
83
|
+
zoneId?: string;
|
|
84
|
+
applicationId?: string;
|
|
16
85
|
subjectToken?: string;
|
|
17
86
|
tokenSource?: TokenSource;
|
|
87
|
+
exchanger?: ClientSecretExchanger;
|
|
18
88
|
gatewayUrl?: string;
|
|
19
89
|
resources?: ResourceBinding[];
|
|
90
|
+
/** Default TTL for sessions run with session(); a session started with startSession lives by its heartbeat lease instead. */
|
|
20
91
|
defaultTtlSeconds?: number;
|
|
92
|
+
/** Receives SDK operational warnings (lease loss, cleanup failures, boundary misconfiguration); defaults to console.warn. */
|
|
93
|
+
logger?: (message: string, err?: unknown) => void;
|
|
21
94
|
}
|
|
22
|
-
export interface
|
|
23
|
-
|
|
95
|
+
export interface SessionOptions {
|
|
96
|
+
authority?: Authority;
|
|
24
97
|
ttlSeconds?: number;
|
|
25
|
-
|
|
26
|
-
|
|
98
|
+
/** Authority record attached to the Session for Coordinator attribution; it does not alone propagate the user sub to later mints. */
|
|
99
|
+
subjectAuthorityRecordId?: string;
|
|
100
|
+
/** Federated Subject mandate proving control of subjectAuthorityRecordId. */
|
|
101
|
+
subjectAuthorityRecordToken?: string;
|
|
102
|
+
/** Session to parent under; defaults to the session bound on the calling context. */
|
|
103
|
+
parentSessionId?: string;
|
|
104
|
+
/** What this session is for, in operator terms; recorded as metadata.task and shown wherever the session is inspected. */
|
|
105
|
+
task?: string;
|
|
27
106
|
metadata?: JsonObject;
|
|
107
|
+
/** Role labels the zone's grant policy matches (`input.principal.labels`); descriptive for policy and audit, never grants. */
|
|
28
108
|
labels?: string[];
|
|
109
|
+
/** W3C trace id (32 lowercase hex characters) to correlate the session under; generated when absent. */
|
|
29
110
|
traceId?: string;
|
|
111
|
+
/**
|
|
112
|
+
* Optional stable operation identifier from a queue, webhook, workflow, or scheduler.
|
|
113
|
+
* Reusing it with the same inputs replays the Coordinator's session-creation result;
|
|
114
|
+
* changed inputs fail with a conflict. It does not suppress this callback or make
|
|
115
|
+
* downstream side effects exactly once. Ordinary code should omit it.
|
|
116
|
+
*/
|
|
117
|
+
idempotencyKey?: string;
|
|
118
|
+
signal?: AbortSignal;
|
|
30
119
|
}
|
|
31
|
-
export interface
|
|
32
|
-
|
|
120
|
+
export interface StartSessionOptions {
|
|
121
|
+
authority?: Authority;
|
|
33
122
|
ttlSeconds?: number;
|
|
34
|
-
|
|
35
|
-
|
|
123
|
+
/** Authority record attached to the Session for Coordinator attribution; it does not alone propagate the user sub to later mints. */
|
|
124
|
+
subjectAuthorityRecordId?: string;
|
|
125
|
+
/** Federated Subject mandate proving control of subjectAuthorityRecordId. */
|
|
126
|
+
subjectAuthorityRecordToken?: string;
|
|
127
|
+
/** Session to parent under; defaults to the session bound on the calling context. */
|
|
128
|
+
parentSessionId?: string;
|
|
129
|
+
/** What this session is for, in operator terms; recorded as metadata.task and shown wherever the session is inspected. */
|
|
130
|
+
task?: string;
|
|
36
131
|
metadata?: JsonObject;
|
|
132
|
+
/** Role labels the zone's grant policy matches (`input.principal.labels`); descriptive for policy and audit, never grants. */
|
|
37
133
|
labels?: string[];
|
|
134
|
+
/** W3C trace id (32 lowercase hex characters) to correlate the session under; generated when absent. */
|
|
38
135
|
traceId?: string;
|
|
136
|
+
/** Optional stable operation identifier; see SessionOptions.idempotencyKey for its creation-only guarantee. */
|
|
137
|
+
idempotencyKey?: string;
|
|
138
|
+
/**
|
|
139
|
+
* Auto-heartbeat cadence. Leave unset to derive it from the server lease;
|
|
140
|
+
* a positive value fixes the interval; zero or a negative value disables
|
|
141
|
+
* the background timer, leaving the lease to manual heartbeat calls.
|
|
142
|
+
*/
|
|
39
143
|
heartbeatIntervalMs?: number;
|
|
144
|
+
/** Called once if the coordinator reports the session permanently gone. */
|
|
145
|
+
onLeaseLost?: (err: unknown) => void;
|
|
146
|
+
/** Called when the coordinator reports a different service-session state. */
|
|
147
|
+
onStateChange?: (status: string) => void;
|
|
148
|
+
signal?: AbortSignal;
|
|
40
149
|
}
|
|
41
150
|
export interface DelegateOptions {
|
|
42
|
-
|
|
43
|
-
|
|
151
|
+
toSessionId: string;
|
|
152
|
+
/** Receiving application; defaults to the caller's own application for same-app delegation. */
|
|
153
|
+
toApplicationId?: string;
|
|
44
154
|
resourceId?: string;
|
|
45
155
|
scopes: string[];
|
|
46
156
|
constraints?: DelegationConstraints;
|
|
47
|
-
ttlSeconds
|
|
157
|
+
ttlSeconds: number;
|
|
158
|
+
signal?: AbortSignal;
|
|
48
159
|
}
|
|
49
160
|
export type LifecycleHook = (ctx: CaracalContext) => void | Promise<void>;
|
|
50
|
-
|
|
51
|
-
|
|
161
|
+
/**
|
|
162
|
+
* One delegation presentation by this client. Emitted whenever
|
|
163
|
+
* acceptDelegation binds a delegation - and with `ok: false` when an
|
|
164
|
+
* up-front validation rejects it - so a forensic consumer can correlate
|
|
165
|
+
* which workload presented which delegation on which session.
|
|
166
|
+
*/
|
|
167
|
+
export interface DelegationAcceptEvent {
|
|
168
|
+
type: 'delegation.accept';
|
|
169
|
+
delegationId: string;
|
|
170
|
+
sessionId?: string;
|
|
171
|
+
validated: boolean;
|
|
172
|
+
ok: boolean;
|
|
173
|
+
durationMs: number;
|
|
174
|
+
}
|
|
175
|
+
/** Control-plane operation reported to onEvent subscribers: a token exchange, an approval wait, a coordinator call, or a delegation acceptance. */
|
|
176
|
+
export type CaracalEvent = OAuthEvent | CoordinatorCallEvent | DelegationAcceptEvent;
|
|
177
|
+
export type EventHook = (event: CaracalEvent) => void;
|
|
178
|
+
/** Identity selection for calls made outside a bound session context. */
|
|
179
|
+
export interface CallOptions {
|
|
180
|
+
/** Run the call as the application's own identity instead of a bound session; explicit opt-in. */
|
|
181
|
+
asApplication?: boolean;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Transport behavior options. `scopes` switches gateway-routed requests from
|
|
185
|
+
* the raw subject token to a scoped resource mandate minted for the routed
|
|
186
|
+
* resource and the bound session identity; requires a client-secret
|
|
187
|
+
* configuration. `approvalId` retries an approval-gated mint so the request
|
|
188
|
+
* proceeds once an authenticated approver has satisfied the hold. `timeoutMs`
|
|
189
|
+
* bounds every request the transport sends when the caller supplies no signal
|
|
190
|
+
* of its own (combined when both are present). `propagation` controls where
|
|
191
|
+
* the context envelope (traceparent, baggage) is written: 'always' (the
|
|
192
|
+
* default) propagates to every host so downstream Caracal-aware services can
|
|
193
|
+
* rebind context; 'gateway-only' keeps the caracal.* correlation ids off
|
|
194
|
+
* third-party hosts.
|
|
195
|
+
*/
|
|
196
|
+
export interface TransportOptions extends CallOptions {
|
|
197
|
+
scopes?: string[];
|
|
198
|
+
approvalId?: string;
|
|
199
|
+
timeoutMs?: number;
|
|
200
|
+
propagation?: 'always' | 'gateway-only';
|
|
201
|
+
}
|
|
202
|
+
/** Optional mint inputs: a TTL override, the approval id for retrying an approval-gated mint, an explicit context, and an abort signal. */
|
|
203
|
+
export interface MandateOptions {
|
|
204
|
+
ttlSeconds?: number;
|
|
205
|
+
approvalId?: string;
|
|
206
|
+
ctx?: CaracalContext;
|
|
207
|
+
signal?: AbortSignal;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Application transport behavior. `scopes` is the authority every request
|
|
211
|
+
* presents; `labels` mark the sessions each mint cycle starts and are the
|
|
212
|
+
* role labels the zone's grant policy matches - unset, they default to the
|
|
213
|
+
* application id, the same default role `ensureGrants` authors, so a grant
|
|
214
|
+
* and its transport align without either naming a role; `mandateTtlSeconds`
|
|
215
|
+
* bounds each minted mandate (the backing sessions outlive it by a small
|
|
216
|
+
* buffer).
|
|
217
|
+
*/
|
|
218
|
+
export interface ApplicationTransportOptions {
|
|
219
|
+
scopes: string[];
|
|
220
|
+
labels?: string[];
|
|
221
|
+
mandateTtlSeconds?: number;
|
|
222
|
+
approvalId?: string;
|
|
223
|
+
timeoutMs?: number;
|
|
224
|
+
}
|
|
225
|
+
export interface BindOptions extends CallOptions {
|
|
226
|
+
verify?: (token: string) => void | VerifiedClaims | Promise<void | VerifiedClaims>;
|
|
52
227
|
}
|
|
53
228
|
export interface ClientSecretOptions {
|
|
54
229
|
coordinatorUrl: string;
|
|
55
230
|
stsUrl: string;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
231
|
+
/** Static credential triple; pass `credentials` instead when the credential is rotated or provisioned at runtime. */
|
|
232
|
+
zoneId?: string;
|
|
233
|
+
applicationId?: string;
|
|
234
|
+
clientSecret?: string;
|
|
235
|
+
/** Dynamic credential source, resolved per operation; exactly one of this or the static triple is required. */
|
|
236
|
+
credentials?: CredentialsResolver;
|
|
237
|
+
/**
|
|
238
|
+
* Resources this client mints lifecycle tokens for and routes through the
|
|
239
|
+
* Gateway. Optional for a client that only runs application transports,
|
|
240
|
+
* which mint per resource; session and lifecycle paths require at least one.
|
|
241
|
+
*/
|
|
242
|
+
resources?: Array<string | ResourceBinding>;
|
|
60
243
|
gatewayUrl?: string;
|
|
61
244
|
scope?: string;
|
|
245
|
+
/** Seeds CaracalConfig.defaultTtlSeconds for sessions run with session(). */
|
|
246
|
+
defaultTtlSeconds?: number;
|
|
62
247
|
fetchImpl?: typeof fetch;
|
|
63
248
|
}
|
|
64
249
|
export interface CaracalOptions {
|
|
@@ -66,14 +251,21 @@ export interface CaracalOptions {
|
|
|
66
251
|
configPath?: string;
|
|
67
252
|
clientSecret?: Partial<ClientSecretOptions>;
|
|
68
253
|
}
|
|
69
|
-
export interface
|
|
254
|
+
export interface GatewayTarget {
|
|
70
255
|
url: string;
|
|
71
256
|
headers: Record<string, string>;
|
|
72
257
|
}
|
|
73
258
|
export declare class Caracal {
|
|
74
259
|
readonly config: CaracalConfig;
|
|
75
|
-
private
|
|
76
|
-
private
|
|
260
|
+
private sessionStartHooks;
|
|
261
|
+
private sessionEndHooks;
|
|
262
|
+
private eventHooks;
|
|
263
|
+
private appAuthorities;
|
|
264
|
+
private appInflight;
|
|
265
|
+
private appGeneration;
|
|
266
|
+
private appProvisionTail;
|
|
267
|
+
private closed;
|
|
268
|
+
private unverifiedBoundaryWarned;
|
|
77
269
|
/**
|
|
78
270
|
* Creates a Caracal client. With no arguments, credentials are
|
|
79
271
|
* auto-detected from `CARACAL_CONFIG`, the default generated profile, or
|
|
@@ -84,41 +276,261 @@ export declare class Caracal {
|
|
|
84
276
|
static fromEnv(env?: NodeJS.ProcessEnv): Caracal;
|
|
85
277
|
static fromClientSecret(opts: ClientSecretOptions): Caracal;
|
|
86
278
|
static fromConfig(path?: string, env?: NodeJS.ProcessEnv): Caracal;
|
|
279
|
+
/**
|
|
280
|
+
* Releases client-held state: cached application mandates and their
|
|
281
|
+
* in-flight mint cycles are dropped, the credential exchanger's cached
|
|
282
|
+
* lifecycle token is invalidated, and the sessions backing released
|
|
283
|
+
* application transports are terminated best-effort - any that termination
|
|
284
|
+
* misses retire on their own TTL. The client stays usable; the next call
|
|
285
|
+
* simply mints fresh state.
|
|
286
|
+
*/
|
|
87
287
|
close(): Promise<void>;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
288
|
+
private warnLog;
|
|
289
|
+
private ensureOpen;
|
|
290
|
+
/**
|
|
291
|
+
* The zone and application this client acts as: the static configuration
|
|
292
|
+
* when present, otherwise resolved through the credentials source, which
|
|
293
|
+
* fails closed while no usable credential exists. Useful for logging and
|
|
294
|
+
* metric labels.
|
|
295
|
+
*/
|
|
296
|
+
identity(): Promise<{
|
|
297
|
+
zoneId: string;
|
|
298
|
+
applicationId: string;
|
|
299
|
+
}>;
|
|
300
|
+
/**
|
|
301
|
+
* Run fn inside a governed session: a bounded identity Caracal establishes
|
|
302
|
+
* around whatever fn executes - an AI agent step, a job, a tool call, any
|
|
303
|
+
* code. The session binds delegated authority, records audit attribution,
|
|
304
|
+
* and is retired when fn returns. Pass `authority: Authority.narrow([...])`
|
|
305
|
+
* to bound the session to a subset of the caller's scopes.
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```ts
|
|
309
|
+
* const summary = await caracal.session(async (ctx) => {
|
|
310
|
+
* log.info({ sessionId: ctx.sessionId }, 'triage run started')
|
|
311
|
+
* const res = await caracal.fetch('resource://pipernet', '/tickets')
|
|
312
|
+
* return res.json()
|
|
313
|
+
* }, { labels: ['ticket-triage'] })
|
|
314
|
+
* ```
|
|
315
|
+
*
|
|
316
|
+
* @example Narrowed child session
|
|
317
|
+
* ```ts
|
|
318
|
+
* await caracal.session(work, {
|
|
319
|
+
* authority: Authority.narrow(['tickets:read'], { resourceId: 'resource://pipernet' }),
|
|
320
|
+
* })
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
session<T>(fn: (ctx: CaracalContext) => Promise<T>, opts?: SessionOptions): Promise<T>;
|
|
324
|
+
/**
|
|
325
|
+
* Start a governed session that outlives a block: the returned handle keeps
|
|
326
|
+
* the session's lease renewed and the holder retires it with close.
|
|
327
|
+
*/
|
|
328
|
+
startSession(opts?: StartSessionOptions): Promise<SessionHandle>;
|
|
329
|
+
/**
|
|
330
|
+
* Delegate a slice of the current session's authority to an existing peer
|
|
331
|
+
* session and return the created delegation. The receiver defaults to the
|
|
332
|
+
* caller's own application; name toApplicationId only for cross-application
|
|
333
|
+
* delegation. The receiving session accepts it with acceptDelegation.
|
|
334
|
+
*/
|
|
335
|
+
delegate(opts: DelegateOptions): Promise<Delegation>;
|
|
336
|
+
/**
|
|
337
|
+
* Run fn under a delegation received from a peer: the current session
|
|
338
|
+
* context is rebound so token exchanges inside fn present the delegation.
|
|
339
|
+
* Acceptance is local; the STS re-validates the delegation on every mint,
|
|
340
|
+
* so a wrong or revoked id fails at first use. Pass `{ validate: true }`
|
|
341
|
+
* to check the delegation up front instead: the coordinator's inbound
|
|
342
|
+
* listing for this session must hold it live, so a mistyped or already
|
|
343
|
+
* revoked id fails here with a targeted error.
|
|
344
|
+
*/
|
|
345
|
+
acceptDelegation<T>(delegationId: string, fn: () => Promise<T>, opts?: {
|
|
346
|
+
validate?: boolean;
|
|
347
|
+
}): Promise<T>;
|
|
348
|
+
/**
|
|
349
|
+
* Re-attach to a service session that already exists - typically after a
|
|
350
|
+
* process restart, using a session id the previous holder persisted (for
|
|
351
|
+
* example from SessionHandle.sessionId). The session is validated with an
|
|
352
|
+
* immediate lease renewal, and the returned handle renews and retires it
|
|
353
|
+
* exactly like one from startSession.
|
|
354
|
+
*/
|
|
355
|
+
attachSession(sessionId: string, opts?: {
|
|
356
|
+
heartbeatIntervalMs?: number;
|
|
357
|
+
onLeaseLost?: (err: unknown) => void;
|
|
358
|
+
}): Promise<SessionHandle>;
|
|
91
359
|
bind<T>(ctx: CaracalContext, fn: () => Promise<T>): Promise<T>;
|
|
92
|
-
|
|
93
|
-
|
|
360
|
+
onSessionStart(cb: LifecycleHook): void;
|
|
361
|
+
onSessionEnd(cb: LifecycleHook): void;
|
|
362
|
+
/**
|
|
363
|
+
* Subscribes to control-plane operation events: token exchanges (with cache
|
|
364
|
+
* outcome), approval waits, and coordinator calls, each carrying outcome and
|
|
365
|
+
* duration. Bridge them to any metrics or tracing system; a hook that throws
|
|
366
|
+
* is ignored and never disturbs the operation that emitted the event.
|
|
367
|
+
* Returns a disposer that unsubscribes the hook.
|
|
368
|
+
*/
|
|
369
|
+
onEvent(cb: EventHook): () => void;
|
|
370
|
+
private emitEvent;
|
|
94
371
|
private fire;
|
|
95
372
|
current(): CaracalContext | undefined;
|
|
96
|
-
headers(opts?: RootOptions): Record<string, string>;
|
|
97
|
-
headersAsync(opts?: RootOptions): Promise<Record<string, string>>;
|
|
98
|
-
bindFromHeaders<T>(headers: Record<string, string | string[] | undefined> | HeaderGetter, fn: () => Promise<T>, opts?: RootOptions): Promise<T>;
|
|
99
373
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
374
|
+
* Envelope headers plus the bearer credential for the current context.
|
|
375
|
+
* The context token is returned as bound; long-lived holders needing a
|
|
376
|
+
* refreshed own-credential token use headersAsync. Calling as the
|
|
377
|
+
* application's own identity requires `{ asApplication: true }`.
|
|
378
|
+
*/
|
|
379
|
+
headers(opts?: CallOptions): Record<string, string>;
|
|
380
|
+
/**
|
|
381
|
+
* Async variant of headers. For contexts this process established from its
|
|
382
|
+
* own credentials, the bearer is resolved fresh through the token source so
|
|
383
|
+
* long-lived holders never present an expired token; inbound contexts stay
|
|
384
|
+
* pinned to the caller's token.
|
|
385
|
+
*/
|
|
386
|
+
headersAsync(opts?: CallOptions): Promise<Record<string, string>>;
|
|
387
|
+
bindFromHeaders<T>(headers: Headers | Record<string, string | string[] | undefined> | HeaderGetter, fn: () => Promise<T>, opts?: BindOptions): Promise<T>;
|
|
388
|
+
/**
|
|
389
|
+
* Returns a fetch-shaped function that injects the Caracal context envelope
|
|
390
|
+
* (traceparent, tracestate, baggage) onto outbound requests, merging with any
|
|
391
|
+
* headers the caller or an OpenTelemetry SDK already set. The bearer is
|
|
392
|
+
* attached only to gateway-routed calls, where the Gateway terminates it at
|
|
393
|
+
* the trust boundary: a scoped mandate when `scopes` is set, otherwise the
|
|
394
|
+
* context's subject token. No default timeout is applied unless `timeoutMs`
|
|
395
|
+
* is set; a caller-supplied `init.signal` still applies (combined when both
|
|
396
|
+
* are present). Pass to any provider SDK that accepts a custom fetch.
|
|
397
|
+
*
|
|
398
|
+
* @example Hand the transport to a provider SDK
|
|
399
|
+
* ```ts
|
|
400
|
+
* const openai = new OpenAI({
|
|
401
|
+
* baseURL: 'https://api.pipernet.example/v1',
|
|
402
|
+
* apiKey: 'unused-gateway-injects-credentials',
|
|
403
|
+
* fetch: caracal.transport({ scopes: ['chat:complete'] }),
|
|
404
|
+
* })
|
|
405
|
+
* ```
|
|
406
|
+
*/
|
|
407
|
+
transport(opts?: TransportOptions): typeof fetch;
|
|
408
|
+
/**
|
|
409
|
+
* Resolves the bearer for a gateway-bound request: a scoped mandate when
|
|
410
|
+
* scopes are set and the routed resource is known, a fresh token from the
|
|
411
|
+
* token source for contexts this process established from its own
|
|
412
|
+
* credentials, the pinned context token for inbound-bound contexts, or the
|
|
413
|
+
* application token when running as the application itself.
|
|
414
|
+
*/
|
|
415
|
+
private gatewayToken;
|
|
416
|
+
/**
|
|
417
|
+
* Mints a resource mandate for the current session: a short-lived token
|
|
418
|
+
* audienced to `resourceId` and narrowed to `scopes`, carrying the session
|
|
419
|
+
* and delegation of the bound context. The STS evaluates policy against
|
|
420
|
+
* that session's authority, so a narrowed child can mint only what its
|
|
421
|
+
* delegation allows. Results are cached per resource, scope set, and
|
|
422
|
+
* session identity, and refreshed before expiry.
|
|
423
|
+
*
|
|
424
|
+
* When a scope is approval-gated this throws ApprovalRequiredError; retry
|
|
425
|
+
* with `approvalId` set to the returned approval id once an authenticated
|
|
426
|
+
* approver has satisfied it. Requires a client-secret configuration.
|
|
104
427
|
*/
|
|
105
|
-
|
|
106
|
-
|
|
428
|
+
mintMandate(resourceId: string, scopes: string[], opts?: MandateOptions): Promise<MintedMandate>;
|
|
429
|
+
/**
|
|
430
|
+
* Exchange an end user's identity token from a zone-trusted external issuer
|
|
431
|
+
* for the Subject's Caracal Authority record. The returned subjectAuthorityRecordId
|
|
432
|
+
* anchors governed work to that user (`session(fn, { subjectAuthorityRecordId })`),
|
|
433
|
+
* and the returned token is the user's own mandate for user-facing flows
|
|
434
|
+
* such as approval decisions. Never cached: each federation is an explicit
|
|
435
|
+
* identity event, recorded in the audit stream. Requires a client-secret
|
|
436
|
+
* configuration and a subject issuer registered on the zone.
|
|
437
|
+
*/
|
|
438
|
+
federateSubject(idToken: string, opts?: {
|
|
439
|
+
ttlSeconds?: number;
|
|
440
|
+
timeoutMs?: number;
|
|
441
|
+
signal?: AbortSignal;
|
|
442
|
+
}): Promise<FederatedSubject>;
|
|
443
|
+
/**
|
|
444
|
+
* Long-polls an approval until an approver decides it, it
|
|
445
|
+
* expires, or the timeout elapses. Returns the final lifecycle state:
|
|
446
|
+
* 'approved' means retrying the mint with `approvalId` set will succeed;
|
|
447
|
+
* 'rejected', 'expired', and 'consumed' are terminal; 'pending' means the
|
|
448
|
+
* timeout elapsed with no decision and waiting again is safe. Pass `signal`
|
|
449
|
+
* to abort the wait early.
|
|
450
|
+
*/
|
|
451
|
+
waitForApproval(approvalId: string, opts?: {
|
|
452
|
+
timeoutMs?: number;
|
|
453
|
+
signal?: AbortSignal;
|
|
454
|
+
}): Promise<ApprovalState>;
|
|
455
|
+
/**
|
|
456
|
+
* Runs an approval-gated operation end to end. fn is invoked once; when it
|
|
457
|
+
* throws ApprovalRequiredError the client long-polls the approval and, on
|
|
458
|
+
* approval, invokes fn again with the approval id so the retried mint
|
|
459
|
+
* consumes the decision. Any other outcome (rejected, expired, consumed, or
|
|
460
|
+
* the wait timing out) rethrows the original ApprovalRequiredError, whose
|
|
461
|
+
* approvalId lets the caller resume the wait later.
|
|
462
|
+
*
|
|
463
|
+
* @example
|
|
464
|
+
* ```ts
|
|
465
|
+
* const mandate = await caracal.withApproval((approvalId) =>
|
|
466
|
+
* caracal.mintMandate('resource://pipernet', ['funds:transfer'], { approvalId }),
|
|
467
|
+
* )
|
|
468
|
+
* ```
|
|
469
|
+
*/
|
|
470
|
+
withApproval<T>(fn: (approvalId?: string) => Promise<T>, opts?: {
|
|
471
|
+
timeoutMs?: number;
|
|
472
|
+
signal?: AbortSignal;
|
|
473
|
+
}): Promise<T>;
|
|
474
|
+
gatewayRequest(resourceId: string, path?: string): GatewayTarget;
|
|
107
475
|
/**
|
|
108
476
|
* One-call happy path: sends `init` to `path` on the given resource through the
|
|
109
|
-
* Gateway with Caracal context and authority injected.
|
|
110
|
-
* `
|
|
111
|
-
*
|
|
477
|
+
* Gateway with Caracal context and authority injected. Accepts the transport
|
|
478
|
+
* options inline: pass `scopes` to authorize with a scoped resource mandate,
|
|
479
|
+
* `approvalId` to retry an approval-gated call once its hold is satisfied, and
|
|
480
|
+
* `asApplication` to call as the application's own identity. The resource header
|
|
481
|
+
* always wins over any caller-supplied `X-Caracal-Resource`. No default timeout
|
|
482
|
+
* is applied; pass `timeoutMs` or `signal` (e.g. AbortSignal.timeout) to bound a call.
|
|
483
|
+
*/
|
|
484
|
+
fetch(resourceId: string, path?: string, init?: RequestInit & TransportOptions): Promise<Response>;
|
|
485
|
+
/**
|
|
486
|
+
* A fetch pinned to one resource, running as the application's own
|
|
487
|
+
* identity rather than a bound session context: each mint cycle bootstraps
|
|
488
|
+
* a lifecycle token, starts a source/target session pair, narrows the
|
|
489
|
+
* target to `scopes` on the resource over a delegation, and mints a fresh
|
|
490
|
+
* single-use mandate for every request. Authority cycles are cached per
|
|
491
|
+
* resolved identity, resource, scope set, effective labels, and mandate
|
|
492
|
+
* TTL, then re-provisioned on a fresh session pair before expiry; sessions from
|
|
493
|
+
* a failed cycle are terminated best-effort. Requests already addressed to
|
|
494
|
+
* the Gateway pass through unchanged; other absolute URLs are rewritten
|
|
495
|
+
* onto it. When a scope is approval-gated the request rejects with
|
|
496
|
+
* ApprovalRequiredError. Requires a client-secret configuration.
|
|
497
|
+
*
|
|
498
|
+
* Provisioning costs four control-plane calls (bootstrap mint, two session
|
|
499
|
+
* spawns, one delegation) roughly once per TTL. Every Gateway request then
|
|
500
|
+
* performs one uncached final mint so its replay-protected JTI is unique.
|
|
501
|
+
*
|
|
502
|
+
* @example Application-identity LLM transport
|
|
503
|
+
* ```ts
|
|
504
|
+
* const llm = new OpenAI({
|
|
505
|
+
* baseURL: 'https://api.pipernet.example/v1',
|
|
506
|
+
* apiKey: 'unused-gateway-injects-credentials',
|
|
507
|
+
* fetch: caracal.applicationTransport('resource://pipernet', { scopes: ['chat:complete'] }),
|
|
508
|
+
* })
|
|
509
|
+
* ```
|
|
112
510
|
*/
|
|
113
|
-
|
|
511
|
+
applicationTransport(resourceId: string, opts: ApplicationTransportOptions): typeof fetch;
|
|
512
|
+
private appAuthority;
|
|
513
|
+
private withAppProvisioning;
|
|
514
|
+
private appAuthorityCycle;
|
|
515
|
+
private retireAppAuthorities;
|
|
114
516
|
private routeThroughGateway;
|
|
517
|
+
/** Reports whether the request is already addressed to the Gateway origin, where the subject token terminates. */
|
|
518
|
+
private targetsGateway;
|
|
115
519
|
/**
|
|
116
|
-
* Binds Caracal context
|
|
117
|
-
*
|
|
520
|
+
* Binds Caracal context at the inbound request boundary. Pass `verify` to
|
|
521
|
+
* enforce the bearer token before binding; without it this middleware only
|
|
522
|
+
* propagates context and must sit behind a verifier boundary. Boundary
|
|
523
|
+
* failures (missing token, verify rejection) answer 401 directly; errors
|
|
524
|
+
* thrown by downstream handlers still flow to the framework error path.
|
|
118
525
|
*/
|
|
119
|
-
contextMiddleware(opts?:
|
|
526
|
+
contextMiddleware(opts?: BindOptions): (req: {
|
|
120
527
|
headers: Record<string, string | string[] | undefined>;
|
|
121
|
-
},
|
|
528
|
+
}, res: {
|
|
529
|
+
statusCode: number;
|
|
530
|
+
setHeader(name: string, value: string): void;
|
|
531
|
+
end(body?: string): void;
|
|
532
|
+
}, next: (err?: unknown) => void) => void;
|
|
533
|
+
private invalidate;
|
|
122
534
|
private rootTokenSync;
|
|
123
535
|
private rootToken;
|
|
124
536
|
}
|