@clearance/management 0.1.4
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/LICENSE +29 -0
- package/LICENSE.md +20 -0
- package/dist/index.d.mts +2815 -0
- package/dist/index.mjs +57439 -0
- package/package.json +41 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,2815 @@
|
|
|
1
|
+
import { ClearanceAuthBundle } from "@clearance/auth";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
|
|
4
|
+
//#region src/types/resources.d.ts
|
|
5
|
+
type ResourceId = string;
|
|
6
|
+
interface Project {
|
|
7
|
+
id: ResourceId;
|
|
8
|
+
name: string;
|
|
9
|
+
slug: string;
|
|
10
|
+
createdAt: string;
|
|
11
|
+
updatedAt: string;
|
|
12
|
+
}
|
|
13
|
+
interface Environment {
|
|
14
|
+
id: ResourceId;
|
|
15
|
+
projectId: ResourceId;
|
|
16
|
+
name: string;
|
|
17
|
+
slug: string;
|
|
18
|
+
kind: "development" | "preview" | "production";
|
|
19
|
+
createdAt: string;
|
|
20
|
+
updatedAt: string;
|
|
21
|
+
}
|
|
22
|
+
interface Principal {
|
|
23
|
+
id: ResourceId;
|
|
24
|
+
projectId: ResourceId;
|
|
25
|
+
environmentId: ResourceId;
|
|
26
|
+
email: string;
|
|
27
|
+
name: string;
|
|
28
|
+
status: "active" | "disabled" | "deleted";
|
|
29
|
+
externalId?: string;
|
|
30
|
+
createdAt: string;
|
|
31
|
+
updatedAt: string;
|
|
32
|
+
}
|
|
33
|
+
interface Organization {
|
|
34
|
+
id: ResourceId;
|
|
35
|
+
projectId: ResourceId;
|
|
36
|
+
environmentId: ResourceId;
|
|
37
|
+
name: string;
|
|
38
|
+
slug: string;
|
|
39
|
+
status: "active" | "archived";
|
|
40
|
+
externalId?: string;
|
|
41
|
+
createdAt: string;
|
|
42
|
+
updatedAt: string;
|
|
43
|
+
}
|
|
44
|
+
interface Membership {
|
|
45
|
+
id: ResourceId;
|
|
46
|
+
organizationId: ResourceId;
|
|
47
|
+
principalId: ResourceId;
|
|
48
|
+
role: string;
|
|
49
|
+
status: "active" | "invited" | "removed";
|
|
50
|
+
source: "manual" | "scim" | "sso" | "import";
|
|
51
|
+
createdAt: string;
|
|
52
|
+
updatedAt: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Authorization role definition (project/environment scoped).
|
|
56
|
+
* Built-in roles are virtual system definitions; custom roles are persisted.
|
|
57
|
+
* Optional organizationId binds a custom role to one organization.
|
|
58
|
+
* Optional status defaults to active when omitted (legacy snapshots).
|
|
59
|
+
*/
|
|
60
|
+
interface CustomRole {
|
|
61
|
+
id: ResourceId;
|
|
62
|
+
projectId: ResourceId;
|
|
63
|
+
environmentId: ResourceId;
|
|
64
|
+
name: string;
|
|
65
|
+
/** Lowercase unique key within project+environment */
|
|
66
|
+
slug: string;
|
|
67
|
+
description?: string;
|
|
68
|
+
/** Normalized, deduplicated, stably ordered permission strings (resource:action) */
|
|
69
|
+
permissions: string[];
|
|
70
|
+
kind: "built_in" | "custom";
|
|
71
|
+
/** When set, role may only be assigned within this organization */
|
|
72
|
+
organizationId?: ResourceId;
|
|
73
|
+
/** active (default) | disabled | archived — only active roles are assignable */
|
|
74
|
+
status?: "active" | "disabled" | "archived";
|
|
75
|
+
createdAt: string;
|
|
76
|
+
updatedAt: string;
|
|
77
|
+
}
|
|
78
|
+
type IdentityProtocol = "saml" | "oidc" | "password" | "passkey" | "social";
|
|
79
|
+
interface IdentityConnection {
|
|
80
|
+
id: ResourceId;
|
|
81
|
+
organizationId: ResourceId;
|
|
82
|
+
protocol: IdentityProtocol;
|
|
83
|
+
provider: string;
|
|
84
|
+
status: "draft" | "testing" | "active" | "disabled";
|
|
85
|
+
domains: string[];
|
|
86
|
+
issuer?: string;
|
|
87
|
+
audience?: string;
|
|
88
|
+
metadataUrl?: string;
|
|
89
|
+
clientId?: string;
|
|
90
|
+
/** Write-only secret fingerprint after create */
|
|
91
|
+
clientSecretFingerprint?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Versioned AEAD envelope for client secret. Write-only — never expose on
|
|
94
|
+
* public/domain list responses (use publicIdentityConnection).
|
|
95
|
+
*/
|
|
96
|
+
clientSecretEncrypted?: string;
|
|
97
|
+
/** Key id used for clientSecretEncrypted (rotation metadata). */
|
|
98
|
+
clientSecretKeyId?: string;
|
|
99
|
+
/** Public IdP SAML endpoint and signing certificate used by the runtime. */
|
|
100
|
+
samlEntryPoint?: string;
|
|
101
|
+
samlCertificate?: string;
|
|
102
|
+
samlCertificateFingerprint?: string;
|
|
103
|
+
certificateFingerprint?: string;
|
|
104
|
+
attributeMapping: Record<string, string>;
|
|
105
|
+
createdAt: string;
|
|
106
|
+
updatedAt: string;
|
|
107
|
+
}
|
|
108
|
+
interface DirectoryConnection {
|
|
109
|
+
id: ResourceId;
|
|
110
|
+
organizationId: ResourceId;
|
|
111
|
+
provider: string;
|
|
112
|
+
status: "draft" | "testing" | "active" | "disabled";
|
|
113
|
+
endpoint: string;
|
|
114
|
+
bearerTokenFingerprint?: string;
|
|
115
|
+
/**
|
|
116
|
+
* Versioned AEAD envelope for SCIM bearer. Write-only — strip from public responses.
|
|
117
|
+
*/
|
|
118
|
+
bearerTokenEncrypted?: string;
|
|
119
|
+
bearerTokenKeyId?: string;
|
|
120
|
+
deprovisioningPolicy: "disable" | "delete" | "suspend";
|
|
121
|
+
createdAt: string;
|
|
122
|
+
updatedAt: string;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Persisted setup capability: digest only (never raw token).
|
|
126
|
+
* Single-use, scoped to project/environment/organization + action.
|
|
127
|
+
*
|
|
128
|
+
* Completion state machine (durable fields only):
|
|
129
|
+
* - available: useCount < maxUses, !redeemedAt, !revokedAt, no active reservation
|
|
130
|
+
* - reserved: reservedAt + reservationId + reservationExpiresAt (in-progress lease)
|
|
131
|
+
* - redeemed: useCount >= maxUses and redeemedAt set (terminal; never reopen)
|
|
132
|
+
* - revoked: revokedAt set
|
|
133
|
+
*
|
|
134
|
+
* Raw capability tokens are never stored. Reservation ids are opaque leases,
|
|
135
|
+
* not the customer token.
|
|
136
|
+
*/
|
|
137
|
+
interface SetupCapability {
|
|
138
|
+
id: ResourceId;
|
|
139
|
+
/** SHA-256 hex digest of the capability token */
|
|
140
|
+
digest: string;
|
|
141
|
+
kind: "sso" | "scim";
|
|
142
|
+
action: "setup";
|
|
143
|
+
resourceType: "organization";
|
|
144
|
+
resourceId: ResourceId;
|
|
145
|
+
organizationId: ResourceId;
|
|
146
|
+
projectId: ResourceId;
|
|
147
|
+
environmentId: ResourceId;
|
|
148
|
+
expiresAt: string;
|
|
149
|
+
maxUses: number;
|
|
150
|
+
useCount: number;
|
|
151
|
+
revokedAt?: string;
|
|
152
|
+
redeemedAt?: string;
|
|
153
|
+
/**
|
|
154
|
+
* In-progress completion lease. Bound with reservationExpiresAt so a crashed
|
|
155
|
+
* holder does not permanently burn the capability. Cleared on commit/release.
|
|
156
|
+
*/
|
|
157
|
+
reservedAt?: string;
|
|
158
|
+
/**
|
|
159
|
+
* Opaque lease / setup-attempt id. Derived from the capability digest so the
|
|
160
|
+
* same attempt identity is reused after lease expiry (durable provision
|
|
161
|
+
* reconcile). Never the raw capability token.
|
|
162
|
+
*/
|
|
163
|
+
reservationId?: string;
|
|
164
|
+
/** When the reservation lease ends and another attempt may re-reserve. */
|
|
165
|
+
reservationExpiresAt?: string;
|
|
166
|
+
createdAt: string;
|
|
167
|
+
}
|
|
168
|
+
interface AuditEvent {
|
|
169
|
+
id: ResourceId;
|
|
170
|
+
correlationId: string;
|
|
171
|
+
projectId?: ResourceId;
|
|
172
|
+
environmentId?: ResourceId;
|
|
173
|
+
organizationId?: ResourceId;
|
|
174
|
+
actor: string;
|
|
175
|
+
action: string;
|
|
176
|
+
subjectType: string;
|
|
177
|
+
subjectId?: string;
|
|
178
|
+
outcome: "success" | "failure" | "pending";
|
|
179
|
+
source: "cli" | "console" | "api" | "system" | "migration" | "sso" | "scim";
|
|
180
|
+
message: string;
|
|
181
|
+
metadata?: Record<string, unknown>;
|
|
182
|
+
createdAt: string;
|
|
183
|
+
}
|
|
184
|
+
type ConformanceMode = "simulation" | "live";
|
|
185
|
+
interface DiagnosticTrace {
|
|
186
|
+
id: ResourceId;
|
|
187
|
+
correlationId: string;
|
|
188
|
+
projectId?: ResourceId;
|
|
189
|
+
environmentId?: ResourceId;
|
|
190
|
+
organizationId?: ResourceId;
|
|
191
|
+
connectionId?: ResourceId;
|
|
192
|
+
subsystem: "sso" | "scim" | "email" | "webhook" | "session" | "migration" | "deploy" | "doctor";
|
|
193
|
+
stage: string;
|
|
194
|
+
outcome: "pass" | "fail" | "warn";
|
|
195
|
+
/**
|
|
196
|
+
* simulation = fixture/lab path (not live IdP/directory conformance)
|
|
197
|
+
* live = exercised against a real external system
|
|
198
|
+
*/
|
|
199
|
+
mode?: ConformanceMode;
|
|
200
|
+
cause?: string;
|
|
201
|
+
causeConfidence?: number;
|
|
202
|
+
owner?: "customer" | "application" | "clearance";
|
|
203
|
+
remediation?: string;
|
|
204
|
+
redactedRequest?: Record<string, unknown>;
|
|
205
|
+
redactedResponse?: Record<string, unknown>;
|
|
206
|
+
checks?: Array<{
|
|
207
|
+
name: string;
|
|
208
|
+
pass: boolean;
|
|
209
|
+
detail?: string;
|
|
210
|
+
}>;
|
|
211
|
+
createdAt: string;
|
|
212
|
+
}
|
|
213
|
+
interface ReadinessCheck {
|
|
214
|
+
id: string;
|
|
215
|
+
name: string;
|
|
216
|
+
status: "pass" | "fail" | "warn" | "skip";
|
|
217
|
+
detail: string;
|
|
218
|
+
fingerprint?: string;
|
|
219
|
+
/** When true, a pass does not imply live production conformance */
|
|
220
|
+
simulation?: boolean;
|
|
221
|
+
}
|
|
222
|
+
interface ReadinessReport {
|
|
223
|
+
id: ResourceId;
|
|
224
|
+
organizationId: ResourceId;
|
|
225
|
+
generatedAt: string;
|
|
226
|
+
checks: ReadinessCheck[];
|
|
227
|
+
overall: "ready" | "blocked" | "attention";
|
|
228
|
+
/** Explicit: overall ready never means live IdP/SCIM certification from synthetic checks */
|
|
229
|
+
conformance: {
|
|
230
|
+
mode: ConformanceMode;
|
|
231
|
+
liveCertified: false | true;
|
|
232
|
+
note: string;
|
|
233
|
+
};
|
|
234
|
+
remainingCustomerActions: string[];
|
|
235
|
+
signature: string;
|
|
236
|
+
}
|
|
237
|
+
interface MigrationPlan {
|
|
238
|
+
id: ResourceId;
|
|
239
|
+
source: "legacy";
|
|
240
|
+
/** Immutable operator scope that owns this migration plan. */
|
|
241
|
+
readonly projectId: ResourceId;
|
|
242
|
+
readonly environmentId: ResourceId;
|
|
243
|
+
status: "planned" | "running" | "verified" | "rolled_back" | "failed";
|
|
244
|
+
counts: Record<string, number>;
|
|
245
|
+
/** Stable fixture identity and last durable import checkpoint for safe resume/audit. */
|
|
246
|
+
fixtureChecksum: string;
|
|
247
|
+
checkpoint: {
|
|
248
|
+
phase: "planned" | "dry_run" | "imported" | "verified" | "failed" | "rolled_back";
|
|
249
|
+
source: "legacy";
|
|
250
|
+
fixtureChecksum: string;
|
|
251
|
+
counts: Record<string, number>;
|
|
252
|
+
wouldCreate: Record<string, number>;
|
|
253
|
+
idempotent: Record<string, number>;
|
|
254
|
+
};
|
|
255
|
+
createdResourceIds?: {
|
|
256
|
+
users: ResourceId[];
|
|
257
|
+
organizations: ResourceId[];
|
|
258
|
+
memberships: ResourceId[];
|
|
259
|
+
};
|
|
260
|
+
/** Exact runtime rows created by the coordinated Postgres importer. */
|
|
261
|
+
createdRuntimeResourceIds?: {
|
|
262
|
+
users: ResourceId[];
|
|
263
|
+
organizations: ResourceId[];
|
|
264
|
+
memberships: ResourceId[];
|
|
265
|
+
};
|
|
266
|
+
/** Identity and relationship fields that must still match before exact rollback. */
|
|
267
|
+
rollbackResourceState?: {
|
|
268
|
+
management: {
|
|
269
|
+
users: Array<{
|
|
270
|
+
id: ResourceId;
|
|
271
|
+
projectId: ResourceId;
|
|
272
|
+
environmentId: ResourceId;
|
|
273
|
+
email: string;
|
|
274
|
+
name: string;
|
|
275
|
+
status: Principal["status"];
|
|
276
|
+
externalId?: string;
|
|
277
|
+
updatedAt: string;
|
|
278
|
+
}>;
|
|
279
|
+
organizations: Array<{
|
|
280
|
+
id: ResourceId;
|
|
281
|
+
projectId: ResourceId;
|
|
282
|
+
environmentId: ResourceId;
|
|
283
|
+
name: string;
|
|
284
|
+
slug: string;
|
|
285
|
+
status: Organization["status"];
|
|
286
|
+
externalId?: string;
|
|
287
|
+
updatedAt: string;
|
|
288
|
+
}>;
|
|
289
|
+
memberships: Array<{
|
|
290
|
+
id: ResourceId;
|
|
291
|
+
organizationId: ResourceId;
|
|
292
|
+
principalId: ResourceId;
|
|
293
|
+
role: string;
|
|
294
|
+
status: Membership["status"];
|
|
295
|
+
source: Membership["source"];
|
|
296
|
+
updatedAt: string;
|
|
297
|
+
}>;
|
|
298
|
+
};
|
|
299
|
+
runtime: {
|
|
300
|
+
users: Array<{
|
|
301
|
+
id: ResourceId;
|
|
302
|
+
email: string;
|
|
303
|
+
name: string;
|
|
304
|
+
emailVerified: boolean;
|
|
305
|
+
image: string | null;
|
|
306
|
+
banned: boolean;
|
|
307
|
+
banReason: string | null;
|
|
308
|
+
updatedAt: string;
|
|
309
|
+
}>;
|
|
310
|
+
organizations: Array<{
|
|
311
|
+
id: ResourceId;
|
|
312
|
+
name: string;
|
|
313
|
+
slug: string;
|
|
314
|
+
logo: string | null;
|
|
315
|
+
metadata: string | null;
|
|
316
|
+
}>;
|
|
317
|
+
memberships: Array<{
|
|
318
|
+
id: ResourceId;
|
|
319
|
+
organizationId: ResourceId;
|
|
320
|
+
principalId: ResourceId;
|
|
321
|
+
role: string;
|
|
322
|
+
}>;
|
|
323
|
+
};
|
|
324
|
+
};
|
|
325
|
+
steps: Array<{
|
|
326
|
+
name: string;
|
|
327
|
+
status: "pending" | "done" | "failed" | "skipped";
|
|
328
|
+
detail?: string;
|
|
329
|
+
}>;
|
|
330
|
+
createdAt: string;
|
|
331
|
+
updatedAt: string;
|
|
332
|
+
}
|
|
333
|
+
interface BackupRecord {
|
|
334
|
+
id: ResourceId;
|
|
335
|
+
path: string;
|
|
336
|
+
createdAt: string;
|
|
337
|
+
checksum: string;
|
|
338
|
+
resourceCounts: Record<string, number>;
|
|
339
|
+
verified: boolean;
|
|
340
|
+
}
|
|
341
|
+
interface SessionRecord {
|
|
342
|
+
id: ResourceId;
|
|
343
|
+
principalId: ResourceId;
|
|
344
|
+
environmentId: ResourceId;
|
|
345
|
+
status: "active" | "revoked";
|
|
346
|
+
createdAt: string;
|
|
347
|
+
revokedAt?: string;
|
|
348
|
+
}
|
|
349
|
+
/** Digest-only API key record. The raw secret is returned once at creation only. */
|
|
350
|
+
interface ApiKey {
|
|
351
|
+
id: ResourceId;
|
|
352
|
+
projectId: ResourceId;
|
|
353
|
+
environmentId: ResourceId;
|
|
354
|
+
name: string;
|
|
355
|
+
/** Normalized, sorted authorization scopes. */
|
|
356
|
+
scopes: string[];
|
|
357
|
+
/** SHA-256 hex digest of the raw API key; never expose in public views. */
|
|
358
|
+
digest: string;
|
|
359
|
+
/** Safe non-secret identifier derived from the key material. */
|
|
360
|
+
prefix: string;
|
|
361
|
+
/** Short digest fingerprint for operator correlation. */
|
|
362
|
+
fingerprint: string;
|
|
363
|
+
status: "active" | "revoked";
|
|
364
|
+
createdAt: string;
|
|
365
|
+
updatedAt: string;
|
|
366
|
+
revokedAt?: string;
|
|
367
|
+
replacedById?: string;
|
|
368
|
+
}
|
|
369
|
+
interface DoctorCheck {
|
|
370
|
+
id: string;
|
|
371
|
+
name: string;
|
|
372
|
+
status: "pass" | "fail" | "warn";
|
|
373
|
+
detail: string;
|
|
374
|
+
remediation?: string;
|
|
375
|
+
}
|
|
376
|
+
interface DataStoreSnapshot {
|
|
377
|
+
version: number;
|
|
378
|
+
releaseVersion: string;
|
|
379
|
+
projects: Project[];
|
|
380
|
+
environments: Environment[];
|
|
381
|
+
principals: Principal[];
|
|
382
|
+
organizations: Organization[];
|
|
383
|
+
memberships: Membership[];
|
|
384
|
+
identityConnections: IdentityConnection[];
|
|
385
|
+
directoryConnections: DirectoryConnection[];
|
|
386
|
+
/** Custom (operator-defined) roles only — built-ins are virtual */
|
|
387
|
+
roles: CustomRole[];
|
|
388
|
+
events: AuditEvent[];
|
|
389
|
+
traces: DiagnosticTrace[];
|
|
390
|
+
readinessReports: ReadinessReport[];
|
|
391
|
+
migrations: MigrationPlan[];
|
|
392
|
+
backups: BackupRecord[];
|
|
393
|
+
sessions: SessionRecord[];
|
|
394
|
+
/** API-key digests only; raw secrets are never snapshotted. */
|
|
395
|
+
apiKeys: ApiKey[];
|
|
396
|
+
/** Capability digests for SSO/SCIM setup links (no raw tokens) */
|
|
397
|
+
setupLinks: SetupCapability[];
|
|
398
|
+
meta: {
|
|
399
|
+
initializedAt?: string;
|
|
400
|
+
schemaVersion: number;
|
|
401
|
+
config: Record<string, string>;
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region src/store/types.d.ts
|
|
406
|
+
/**
|
|
407
|
+
* Management control-plane store.
|
|
408
|
+
* Json backend is for local dev without DATABASE_URL.
|
|
409
|
+
* Postgres backend is the single transactional source of truth when DATABASE_URL is set.
|
|
410
|
+
*
|
|
411
|
+
* Cross-process safety (Postgres): mutations are transactionally replayed against a
|
|
412
|
+
* row-locked snapshot with a monotonically increasing revision. Long-lived processes
|
|
413
|
+
* must call refresh() before reads so CLI writes are visible to a running API.
|
|
414
|
+
*/
|
|
415
|
+
interface ManagementStore {
|
|
416
|
+
/** Local path used for file-backed stores and backup directory resolution */
|
|
417
|
+
readonly path: string;
|
|
418
|
+
readonly backend: "json" | "postgres";
|
|
419
|
+
get snapshot(): DataStoreSnapshot;
|
|
420
|
+
load(): DataStoreSnapshot;
|
|
421
|
+
save(): void;
|
|
422
|
+
/** Flush pending durable writes (no-op for json; await for postgres) */
|
|
423
|
+
ready(): Promise<void>;
|
|
424
|
+
/**
|
|
425
|
+
* Reload from durable backend when another process may have written.
|
|
426
|
+
* Json re-reads the file; Postgres compares revision and replaces local cache.
|
|
427
|
+
*/
|
|
428
|
+
refresh(): Promise<void>;
|
|
429
|
+
replace(snapshot: DataStoreSnapshot): void;
|
|
430
|
+
/**
|
|
431
|
+
* Queue a mutation. For Postgres the function is replayed inside
|
|
432
|
+
* BEGIN…SELECT FOR UPDATE…COMMIT so concurrent writers merge by replaying
|
|
433
|
+
* ops rather than last-write-wins full snapshot overwrite.
|
|
434
|
+
* Await ready() before relying on snapshot for subsequent reads.
|
|
435
|
+
*/
|
|
436
|
+
mutate(fn: (data: DataStoreSnapshot) => void): DataStoreSnapshot;
|
|
437
|
+
/** Execute against the latest durable draft and resolve only after commit. */
|
|
438
|
+
mutateDurable<T>(fn: (data: DataStoreSnapshot) => T): Promise<T>;
|
|
439
|
+
/**
|
|
440
|
+
* Postgres only: one transaction covering management snapshot (+ uniqueness +
|
|
441
|
+
* audit via the mutator) and arbitrary runtime SQL on the same connection.
|
|
442
|
+
* JsonStore does not implement this — callers must use management-only paths.
|
|
443
|
+
*/
|
|
444
|
+
mutateCoordinated?<T>(fn: (ctx: {
|
|
445
|
+
data: DataStoreSnapshot;
|
|
446
|
+
query: (sql: string, params?: unknown[]) => Promise<{
|
|
447
|
+
rows: Record<string, unknown>[];
|
|
448
|
+
rowCount: number | null;
|
|
449
|
+
}>;
|
|
450
|
+
}) => Promise<T> | T): Promise<T>;
|
|
451
|
+
checksum(): string;
|
|
452
|
+
resourceCounts(): Record<string, number>;
|
|
453
|
+
}
|
|
454
|
+
declare function isManagementStore(value: unknown): value is ManagementStore;
|
|
455
|
+
//#endregion
|
|
456
|
+
//#region src/store/json-store.d.ts
|
|
457
|
+
declare const STORE_SCHEMA_VERSION = 1;
|
|
458
|
+
declare const CLEARANCE_RELEASE_VERSION = "0.1.4";
|
|
459
|
+
declare function emptySnapshot(config?: Record<string, string>): DataStoreSnapshot;
|
|
460
|
+
/** Normalize older snapshots missing newer collections. */
|
|
461
|
+
declare function normalizeSnapshot(data: DataStoreSnapshot): DataStoreSnapshot;
|
|
462
|
+
declare function defaultDataPath(): string;
|
|
463
|
+
/** File-backed store for local development without DATABASE_URL. */
|
|
464
|
+
declare class JsonStore implements ManagementStore {
|
|
465
|
+
readonly backend: "json";
|
|
466
|
+
readonly path: string;
|
|
467
|
+
private data;
|
|
468
|
+
constructor(path?: string);
|
|
469
|
+
load(): DataStoreSnapshot;
|
|
470
|
+
save(): void;
|
|
471
|
+
ready(): Promise<void>;
|
|
472
|
+
/** Re-read file so another process's writes are visible (local multi-process). */
|
|
473
|
+
refresh(): Promise<void>;
|
|
474
|
+
get snapshot(): DataStoreSnapshot;
|
|
475
|
+
replace(snapshot: DataStoreSnapshot): void;
|
|
476
|
+
/**
|
|
477
|
+
* Apply mutation on a draft so a thrown validation error does not leave
|
|
478
|
+
* a half-applied in-memory snapshot (atomic validation+mutation+audit).
|
|
479
|
+
*/
|
|
480
|
+
mutate(fn: (data: DataStoreSnapshot) => void): DataStoreSnapshot;
|
|
481
|
+
mutateDurable<T>(fn: (data: DataStoreSnapshot) => T): Promise<T>;
|
|
482
|
+
checksum(): string;
|
|
483
|
+
resourceCounts(): Record<string, number>;
|
|
484
|
+
}
|
|
485
|
+
declare function newId(prefix: string): string;
|
|
486
|
+
declare function nowIso(): string;
|
|
487
|
+
declare function fingerprint(value: string): string;
|
|
488
|
+
declare function correlationId(): string;
|
|
489
|
+
//#endregion
|
|
490
|
+
//#region src/store/pg-store.d.ts
|
|
491
|
+
declare class PgStore implements ManagementStore {
|
|
492
|
+
readonly backend: "postgres";
|
|
493
|
+
readonly path: string;
|
|
494
|
+
private data;
|
|
495
|
+
private revision;
|
|
496
|
+
private pool;
|
|
497
|
+
private table;
|
|
498
|
+
private emailUniqueTable;
|
|
499
|
+
private slugUniqueTable;
|
|
500
|
+
private idempotencyTable;
|
|
501
|
+
private pending;
|
|
502
|
+
/** Set when a queued write fails; rethrown from ready() so the chain never rejects. */
|
|
503
|
+
private writeError;
|
|
504
|
+
private initialized;
|
|
505
|
+
constructor(databaseUrl: string, opts?: {
|
|
506
|
+
backupDir?: string;
|
|
507
|
+
tableName?: string;
|
|
508
|
+
});
|
|
509
|
+
/** Ensure schema + load snapshot. Call before first use. */
|
|
510
|
+
init(): Promise<this>;
|
|
511
|
+
load(): DataStoreSnapshot;
|
|
512
|
+
get snapshot(): DataStoreSnapshot;
|
|
513
|
+
/** Monotonic revision of the last known durable snapshot (test/debug). */
|
|
514
|
+
get currentRevision(): number;
|
|
515
|
+
save(): void;
|
|
516
|
+
ready(): Promise<void>;
|
|
517
|
+
/**
|
|
518
|
+
* Pull latest durable snapshot if another process advanced the revision.
|
|
519
|
+
* Safe to call on every API request; no-op when revision is current.
|
|
520
|
+
*/
|
|
521
|
+
refresh(): Promise<void>;
|
|
522
|
+
replace(snapshot: DataStoreSnapshot): void;
|
|
523
|
+
mutate(fn: (data: DataStoreSnapshot) => void): DataStoreSnapshot;
|
|
524
|
+
mutateDurable<T>(fn: (data: DataStoreSnapshot) => T): Promise<T>;
|
|
525
|
+
/**
|
|
526
|
+
* Single Postgres transaction: lock management snapshot, run caller SQL
|
|
527
|
+
* (runtime user/session/account tables) + snapshot mutator, enforce
|
|
528
|
+
* uniqueness indexes, commit. Full ROLLBACK on any throw — never returns
|
|
529
|
+
* success when runtime and management diverge.
|
|
530
|
+
*/
|
|
531
|
+
mutateCoordinated<T>(fn: (ctx: {
|
|
532
|
+
data: DataStoreSnapshot;
|
|
533
|
+
query: (sql: string, params?: unknown[]) => Promise<{
|
|
534
|
+
rows: Record<string, unknown>[];
|
|
535
|
+
rowCount: number | null;
|
|
536
|
+
}>;
|
|
537
|
+
}) => Promise<T> | T): Promise<T>;
|
|
538
|
+
checksum(): string;
|
|
539
|
+
resourceCounts(): Record<string, number>;
|
|
540
|
+
destroy(): Promise<void>;
|
|
541
|
+
/**
|
|
542
|
+
* Read a stored Idempotency-Key replay record. Expired rows are treated as
|
|
543
|
+
* absent (TTL is enforced on read as well as by opportunistic cleanup).
|
|
544
|
+
*/
|
|
545
|
+
getIdempotencyRecord(scopeKey: string, key: string): Promise<{
|
|
546
|
+
fingerprint: string;
|
|
547
|
+
status: number;
|
|
548
|
+
contentType: string;
|
|
549
|
+
body: string;
|
|
550
|
+
} | null>;
|
|
551
|
+
/**
|
|
552
|
+
* Store an Idempotency-Key replay record with a TTL. Opportunistically
|
|
553
|
+
* deletes expired rows first (the table stays small; expiry never touches
|
|
554
|
+
* the snapshot). ON CONFLICT DO NOTHING: the first committed responder wins
|
|
555
|
+
* under a same-key race.
|
|
556
|
+
*/
|
|
557
|
+
putIdempotencyRecord(record: {
|
|
558
|
+
scopeKey: string;
|
|
559
|
+
key: string;
|
|
560
|
+
fingerprint: string;
|
|
561
|
+
status: number;
|
|
562
|
+
contentType: string;
|
|
563
|
+
body: string;
|
|
564
|
+
ttlMs: number;
|
|
565
|
+
}): Promise<void>;
|
|
566
|
+
/**
|
|
567
|
+
* Enqueue a mutation that will be transactionally replayed against the
|
|
568
|
+
* row-locked latest snapshot (not applied only to a stale process-local copy).
|
|
569
|
+
* The chain never rejects (errors are stashed and rethrown from ready()) so
|
|
570
|
+
* concurrent writers cannot strand later ops or emit unhandledRejection.
|
|
571
|
+
*/
|
|
572
|
+
private queueWrite;
|
|
573
|
+
private transactReplay;
|
|
574
|
+
private transactMutation;
|
|
575
|
+
private transactCoordinated;
|
|
576
|
+
/**
|
|
577
|
+
* Rebuild uniqueness tables from snapshot inside the open transaction.
|
|
578
|
+
* Primary keys enforce same-email / same-slug within project+environment.
|
|
579
|
+
* Concurrent writers serialize on snapshot FOR UPDATE; app checks catch
|
|
580
|
+
* duplicates first, and these constraints fail closed as a second layer.
|
|
581
|
+
*/
|
|
582
|
+
private syncUniqueness;
|
|
583
|
+
/** Initial insert path used only from init when the row is missing. */
|
|
584
|
+
private persistLocked;
|
|
585
|
+
}
|
|
586
|
+
declare function createPgStore(databaseUrl: string, opts?: {
|
|
587
|
+
backupDir?: string;
|
|
588
|
+
tableName?: string;
|
|
589
|
+
}): Promise<PgStore>;
|
|
590
|
+
declare function pgStoreId(): string;
|
|
591
|
+
//#endregion
|
|
592
|
+
//#region src/store/create-store.d.ts
|
|
593
|
+
type CreateStoreOptions = {
|
|
594
|
+
/** Explicit file path for JSON backend */dataPath?: string; /** Force backend; default chooses postgres when DATABASE_URL is set */
|
|
595
|
+
backend?: "json" | "postgres" | "auto";
|
|
596
|
+
databaseUrl?: string;
|
|
597
|
+
};
|
|
598
|
+
/**
|
|
599
|
+
* Open the management store.
|
|
600
|
+
* - DATABASE_URL set → Postgres is the single transactional source of truth
|
|
601
|
+
* - otherwise → local JSON file (developer quick path)
|
|
602
|
+
*/
|
|
603
|
+
declare function createManagementStore(opts?: CreateStoreOptions): Promise<ManagementStore>;
|
|
604
|
+
//#endregion
|
|
605
|
+
//#region src/services/errors.d.ts
|
|
606
|
+
declare class ClearanceError extends Error {
|
|
607
|
+
readonly code: string;
|
|
608
|
+
readonly stage: string;
|
|
609
|
+
readonly retryable: boolean;
|
|
610
|
+
readonly remediation: string;
|
|
611
|
+
readonly status: number;
|
|
612
|
+
constructor(opts: {
|
|
613
|
+
code: string;
|
|
614
|
+
message: string;
|
|
615
|
+
stage: string;
|
|
616
|
+
retryable?: boolean;
|
|
617
|
+
remediation?: string;
|
|
618
|
+
status?: number;
|
|
619
|
+
});
|
|
620
|
+
toJSON(): {
|
|
621
|
+
error: {
|
|
622
|
+
code: string;
|
|
623
|
+
message: string;
|
|
624
|
+
stage: string;
|
|
625
|
+
retryable: boolean;
|
|
626
|
+
remediation: string;
|
|
627
|
+
};
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
declare function isClearanceError(err: unknown): err is ClearanceError;
|
|
631
|
+
//#endregion
|
|
632
|
+
//#region src/services/config.d.ts
|
|
633
|
+
type ConfigRecord = Record<string, string>;
|
|
634
|
+
declare function isSecretLikeConfigKey(key: string): boolean;
|
|
635
|
+
declare function isSecretLikeConfigEntry(key: string, value: string): boolean;
|
|
636
|
+
/** Parse the deliberately small config-file grammar while detecting duplicate keys. */
|
|
637
|
+
declare function parseConfigJson(input: string): ConfigRecord;
|
|
638
|
+
declare function validateConfig(store: ManagementStore, config: ConfigRecord): ConfigRecord;
|
|
639
|
+
declare function validateCurrentConfig(store: ManagementStore): ConfigRecord;
|
|
640
|
+
declare function publicConfig(config: ConfigRecord, key?: string): {
|
|
641
|
+
config: ConfigRecord;
|
|
642
|
+
redactedKeys: string[];
|
|
643
|
+
};
|
|
644
|
+
declare function setConfig(store: ManagementStore, key: string, value: string): {
|
|
645
|
+
changed: boolean;
|
|
646
|
+
config: ConfigRecord;
|
|
647
|
+
};
|
|
648
|
+
declare function diffConfig(current: ConfigRecord, candidate: ConfigRecord): {
|
|
649
|
+
added: string[];
|
|
650
|
+
changed: string[];
|
|
651
|
+
removed: string[];
|
|
652
|
+
};
|
|
653
|
+
//#endregion
|
|
654
|
+
//#region src/services/redact.d.ts
|
|
655
|
+
declare function redactValue(value: unknown, keyHint?: string): unknown;
|
|
656
|
+
declare function redactRecord(input: Record<string, unknown> | undefined | null): Record<string, unknown> | undefined;
|
|
657
|
+
/** Keys that must never appear on connection/audit records */
|
|
658
|
+
declare const WRITE_ONLY_SECRET_FIELDS: readonly ["clientSecret", "bearerToken", "password", "token", "_token", "scimToken", "clientSecretEncrypted", "bearerTokenEncrypted"];
|
|
659
|
+
/** Public domain view of SSO connection — encrypted material stripped. */
|
|
660
|
+
declare function publicIdentityConnection(conn: IdentityConnection): Omit<IdentityConnection, "clientSecretEncrypted" | "clientSecretKeyId"> & {
|
|
661
|
+
hasClientSecret: boolean;
|
|
662
|
+
};
|
|
663
|
+
/** Public domain view of SCIM connection — encrypted material stripped. */
|
|
664
|
+
declare function publicDirectoryConnection(conn: DirectoryConnection): Omit<DirectoryConnection, "bearerTokenEncrypted" | "bearerTokenKeyId"> & {
|
|
665
|
+
hasBearerToken: boolean;
|
|
666
|
+
};
|
|
667
|
+
//#endregion
|
|
668
|
+
//#region src/services/secrets.d.ts
|
|
669
|
+
/**
|
|
670
|
+
* Production secret policy for control plane and auth runtime.
|
|
671
|
+
*/
|
|
672
|
+
/** Known-insecure defaults that production must refuse. */
|
|
673
|
+
declare const FORBIDDEN_DEFAULT_SECRETS: readonly ["dev-secret-change-me", "dev-secret-change-me-please-32chars!!", "secret", "local-compose-secret-change-me-32", "test-secret-value-32-characters", "test-secret-value-that-is-long-enough", "test-secret-value-that-is-long-enough-32", "change-me", "password", "clearance", "clearance-secret"];
|
|
674
|
+
declare function isForbiddenDefaultSecret(secret: string | undefined | null): boolean;
|
|
675
|
+
declare function assertProductionSecret(secret: string | undefined, label?: string): void;
|
|
676
|
+
declare function requireOperatorToken(): string;
|
|
677
|
+
/**
|
|
678
|
+
* Production startup guard for credential encryption key material.
|
|
679
|
+
* Call from management API / console when NODE_ENV=production.
|
|
680
|
+
*/
|
|
681
|
+
declare function assertProductionCredentialKey(env?: NodeJS.ProcessEnv): void;
|
|
682
|
+
declare function parseCorsOrigins(): string[];
|
|
683
|
+
//#endregion
|
|
684
|
+
//#region src/services/credentials.d.ts
|
|
685
|
+
type CredentialKeyring = {
|
|
686
|
+
currentKeyId: string; /** keyId → 32-byte key material */
|
|
687
|
+
keys: Map<string, Buffer>;
|
|
688
|
+
};
|
|
689
|
+
type EncryptedCredential = {
|
|
690
|
+
/** Versioned AEAD envelope (never the plaintext) */ciphertext: string;
|
|
691
|
+
keyId: string; /** Short fingerprint of plaintext for comparison without disclosure */
|
|
692
|
+
fingerprint: string;
|
|
693
|
+
};
|
|
694
|
+
declare function fingerprintCredential(value: string): string;
|
|
695
|
+
/**
|
|
696
|
+
* Resolve keyring from environment.
|
|
697
|
+
* - CLEARANCE_CREDENTIAL_KEY + CLEARANCE_CREDENTIAL_KEY_ID (required outside dev/test)
|
|
698
|
+
* - Optional CLEARANCE_CREDENTIAL_PREVIOUS_KEY / CLEARANCE_CREDENTIAL_PREVIOUS_KEY_ID for rotation
|
|
699
|
+
* - CLEARANCE_CREDENTIAL_KEYS_JSON: {"kid":"secret",...} with CLEARANCE_CREDENTIAL_KEY_ID as current
|
|
700
|
+
*/
|
|
701
|
+
declare function resolveCredentialKeyring(env?: NodeJS.ProcessEnv): CredentialKeyring | null;
|
|
702
|
+
declare function isDevelopmentLike(env?: NodeJS.ProcessEnv): boolean;
|
|
703
|
+
/**
|
|
704
|
+
* Fail closed outside development/test when credential key is missing or weak.
|
|
705
|
+
*/
|
|
706
|
+
declare function assertCredentialKeyConfigured(env?: NodeJS.ProcessEnv): CredentialKeyring;
|
|
707
|
+
declare function getCredentialKeyring(env?: NodeJS.ProcessEnv): CredentialKeyring;
|
|
708
|
+
declare function encryptCredential(plaintext: string, ring?: CredentialKeyring): EncryptedCredential;
|
|
709
|
+
declare function parseCredentialEnvelope(envelope: string): {
|
|
710
|
+
version: 1;
|
|
711
|
+
keyId: string;
|
|
712
|
+
iv: Buffer;
|
|
713
|
+
tag: Buffer;
|
|
714
|
+
ct: Buffer;
|
|
715
|
+
};
|
|
716
|
+
declare function decryptCredential(envelope: string, ring?: CredentialKeyring): string;
|
|
717
|
+
/** Re-encrypt under the current key id (rotation). */
|
|
718
|
+
declare function rotateCredential(envelope: string, ring?: CredentialKeyring): EncryptedCredential;
|
|
719
|
+
/** True when value looks like our envelope (not plaintext). */
|
|
720
|
+
declare function isCredentialEnvelope(value: string | undefined | null): boolean;
|
|
721
|
+
//#endregion
|
|
722
|
+
//#region src/services/audit.d.ts
|
|
723
|
+
declare const AUDIT_MAX_EVENTS_DEFAULT = 5000;
|
|
724
|
+
declare const AUDIT_MAX_EVENTS_MIN = 10;
|
|
725
|
+
declare const AUDIT_MAX_EVENTS_MAX = 1000000;
|
|
726
|
+
declare const AUDIT_PRUNED_ACTION = "system.audit.pruned";
|
|
727
|
+
declare function auditMaxEvents(env?: Record<string, string | undefined>): number;
|
|
728
|
+
type AuditEventInput = Omit<AuditEvent, "id" | "createdAt" | "correlationId"> & {
|
|
729
|
+
correlationId?: string;
|
|
730
|
+
};
|
|
731
|
+
/** Build a redacted audit event (no secrets/tokens in metadata). */
|
|
732
|
+
declare function buildAuditEvent(input: AuditEventInput): AuditEvent;
|
|
733
|
+
/**
|
|
734
|
+
* Enforce the retention cap on a snapshot draft (events are newest-first).
|
|
735
|
+
* Truncation maintains exactly ONE rolling system.audit.pruned marker with a
|
|
736
|
+
* cumulative droppedCount. A per-prune marker is itself an event that
|
|
737
|
+
* re-overflows the cap, so the naive version reached ~50% marker density at
|
|
738
|
+
* steady state and halved effective retention (adversarial finding M3).
|
|
739
|
+
* Prior markers are extracted (counts carried forward), overflow real events
|
|
740
|
+
* drop from the tail, and one cumulative marker is reinserted at the head —
|
|
741
|
+
* never re-triggering itself (no recursion, no cap overshoot).
|
|
742
|
+
*/
|
|
743
|
+
declare function enforceAuditRetention(data: DataStoreSnapshot): void;
|
|
744
|
+
/**
|
|
745
|
+
* Append an audit event onto a snapshot draft. Use inside the same store.mutate
|
|
746
|
+
* as the resource mutation so Postgres commits validation+write+audit atomically.
|
|
747
|
+
*/
|
|
748
|
+
declare function appendAuditEvent(data: DataStoreSnapshot, input: AuditEventInput): AuditEvent;
|
|
749
|
+
declare function recordEvent(store: ManagementStore, input: AuditEventInput): AuditEvent;
|
|
750
|
+
//#endregion
|
|
751
|
+
//#region src/services/scope.d.ts
|
|
752
|
+
type ResourceScope = {
|
|
753
|
+
projectId: string;
|
|
754
|
+
environmentId: string;
|
|
755
|
+
};
|
|
756
|
+
type ScopedResource = {
|
|
757
|
+
projectId: string;
|
|
758
|
+
environmentId: string;
|
|
759
|
+
};
|
|
760
|
+
/**
|
|
761
|
+
* Resolve the authenticated operator's scope from server configuration + store.
|
|
762
|
+
* Order:
|
|
763
|
+
* 1. Explicit opts (tests / internal callers)
|
|
764
|
+
* 2. CLEARANCE_PROJECT_ID + CLEARANCE_ENV_ID (secure server-side mapping)
|
|
765
|
+
* 3. store.meta.config after init
|
|
766
|
+
* 4. Sole project/environment pair in the store (local single-project profile)
|
|
767
|
+
*
|
|
768
|
+
* Rejects when scope cannot be determined — never invents foreign scope.
|
|
769
|
+
*/
|
|
770
|
+
declare function resolveOperatorScope(store: ManagementStore, opts?: Partial<ResourceScope>): ResourceScope;
|
|
771
|
+
/**
|
|
772
|
+
* Optional client header consistency check. Headers never select scope —
|
|
773
|
+
* if present they must equal the principal-derived scope.
|
|
774
|
+
*/
|
|
775
|
+
declare function assertClientScopeHeaders(principal: ResourceScope, headerProject?: string | null, headerEnv?: string | null): void;
|
|
776
|
+
/**
|
|
777
|
+
* Fail closed for cross-scope resource access — same error as missing so
|
|
778
|
+
* foreign resources are not revealed.
|
|
779
|
+
*/
|
|
780
|
+
declare function assertResourceInScope(resource: ScopedResource | null | undefined, scope: ResourceScope, opts: {
|
|
781
|
+
code: string;
|
|
782
|
+
stage: string;
|
|
783
|
+
label?: string;
|
|
784
|
+
}): asserts resource is ScopedResource;
|
|
785
|
+
declare function scopeFilter(scope: ResourceScope): (r: ScopedResource) => boolean;
|
|
786
|
+
//#endregion
|
|
787
|
+
//#region src/services/pagination.d.ts
|
|
788
|
+
type PageSurface = "users" | "organizations" | "events" | "sessions";
|
|
789
|
+
type PageOrder = "asc" | "desc";
|
|
790
|
+
type PageCursorKey = {
|
|
791
|
+
createdAt: string;
|
|
792
|
+
id: string;
|
|
793
|
+
};
|
|
794
|
+
declare function encodePageCursor(surface: PageSurface, key: PageCursorKey): string;
|
|
795
|
+
/**
|
|
796
|
+
* Decode + validate an opaque cursor. Fail-closed: anything that is not a
|
|
797
|
+
* well-formed cursor for this surface throws CURSOR_INVALID (garbage base64,
|
|
798
|
+
* non-JSON, wrong shape, wrong version, or a cursor minted for another
|
|
799
|
+
* surface). Returns undefined when no cursor was supplied.
|
|
800
|
+
*/
|
|
801
|
+
declare function decodePageCursor(raw: string | undefined | null, surface: PageSurface, stage: string): PageCursorKey | undefined;
|
|
802
|
+
type ResourcePage<T> = {
|
|
803
|
+
items: T[]; /** Opaque cursor for the next page; null when this page is the last. */
|
|
804
|
+
nextCursor: string | null;
|
|
805
|
+
};
|
|
806
|
+
/**
|
|
807
|
+
* Keyset-paginate an already-filtered snapshot array. Items are sorted into
|
|
808
|
+
* the documented order for the surface, the page starts strictly after the
|
|
809
|
+
* cursor key (when present), and nextCursor is emitted only when more items
|
|
810
|
+
* remain.
|
|
811
|
+
*/
|
|
812
|
+
declare function paginateByCreatedAt<T extends {
|
|
813
|
+
id: string;
|
|
814
|
+
createdAt: string;
|
|
815
|
+
}>(items: T[], opts: {
|
|
816
|
+
surface: PageSurface;
|
|
817
|
+
order: PageOrder;
|
|
818
|
+
limit: number;
|
|
819
|
+
cursor?: PageCursorKey;
|
|
820
|
+
}): ResourcePage<T>;
|
|
821
|
+
/**
|
|
822
|
+
* Fail-closed page-size validation shared by paginated list services.
|
|
823
|
+
* NaN / non-integers / out-of-range values raise the caller's stable code.
|
|
824
|
+
*/
|
|
825
|
+
declare function normalizePageLimit(limit: number | undefined, spec: {
|
|
826
|
+
stage: string;
|
|
827
|
+
code: string;
|
|
828
|
+
defaultValue: number;
|
|
829
|
+
maximum: number;
|
|
830
|
+
}): number;
|
|
831
|
+
//#endregion
|
|
832
|
+
//#region src/services/idempotency.d.ts
|
|
833
|
+
declare const IDEMPOTENCY_DEFAULT_TTL_MS: number;
|
|
834
|
+
type IdempotencyRecord = {
|
|
835
|
+
/** Route+method scope, e.g. "POST /v1/users" */scopeKey: string; /** Client-supplied Idempotency-Key header value */
|
|
836
|
+
key: string; /** sha256 of scopeKey + raw request body — detects key reuse conflicts */
|
|
837
|
+
fingerprint: string;
|
|
838
|
+
status: number;
|
|
839
|
+
contentType: string;
|
|
840
|
+
body: string;
|
|
841
|
+
};
|
|
842
|
+
interface IdempotencyBackend {
|
|
843
|
+
readonly kind: "postgres" | "memory";
|
|
844
|
+
get(scopeKey: string, key: string): Promise<IdempotencyRecord | null>;
|
|
845
|
+
put(record: IdempotencyRecord): Promise<void>;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Resolve the replay TTL. CLEARANCE_IDEMPOTENCY_TTL_MS overrides the 24h
|
|
849
|
+
* default (fail-closed on garbage — an unparseable TTL must not silently
|
|
850
|
+
* become "keys never expire" or "keys never match").
|
|
851
|
+
*/
|
|
852
|
+
declare function resolveIdempotencyTtlMs(env?: Record<string, string | undefined>): number;
|
|
853
|
+
/** Fail-closed Idempotency-Key header validation (visible ASCII, 1–200 chars). */
|
|
854
|
+
declare function assertIdempotencyKeyValid(key: string): void;
|
|
855
|
+
/** Deterministic request fingerprint: same route+method+body ⇒ same digest. */
|
|
856
|
+
declare function fingerprintIdempotentRequest(scopeKey: string, rawBody: string): string;
|
|
857
|
+
/** Structured 409 for Idempotency-Key reuse with a different payload. */
|
|
858
|
+
declare function idempotencyConflictError(scopeKey: string): ClearanceError;
|
|
859
|
+
/**
|
|
860
|
+
* Pick the idempotency backend for a management store: the Postgres companion
|
|
861
|
+
* table when the store is PgStore, otherwise the process-local in-memory map.
|
|
862
|
+
*/
|
|
863
|
+
declare function createIdempotencyBackend(store: ManagementStore, opts?: {
|
|
864
|
+
ttlMs?: number;
|
|
865
|
+
now?: () => number;
|
|
866
|
+
}): IdempotencyBackend;
|
|
867
|
+
//#endregion
|
|
868
|
+
//#region src/services/members.d.ts
|
|
869
|
+
type MembershipSource = Membership["source"];
|
|
870
|
+
type MembershipActorSource = "cli" | "console" | "api" | "import" | "scim" | "system";
|
|
871
|
+
/**
|
|
872
|
+
* Fail closed when the target is the sole active owner and the mutation would
|
|
873
|
+
* remove ownership (demote or remove). At least one owner is required.
|
|
874
|
+
*/
|
|
875
|
+
declare function assertOwnerInvariant(data: {
|
|
876
|
+
memberships: Membership[];
|
|
877
|
+
}, opts: {
|
|
878
|
+
organizationId: string;
|
|
879
|
+
membership: Membership; /** Next role after update; omit for remove */
|
|
880
|
+
nextRole?: string;
|
|
881
|
+
stage: string;
|
|
882
|
+
}): void;
|
|
883
|
+
declare function listMembers(store: ManagementStore, organizationId: string, opts?: {
|
|
884
|
+
scope?: ResourceScope;
|
|
885
|
+
includeRemoved?: boolean;
|
|
886
|
+
}): Membership[];
|
|
887
|
+
declare function inspectMembership(store: ManagementStore, id: string, scope?: ResourceScope): Membership;
|
|
888
|
+
/**
|
|
889
|
+
* Find active membership by org + principal. Cross-scope ids fail as not found
|
|
890
|
+
* when scope is provided.
|
|
891
|
+
*/
|
|
892
|
+
declare function findActiveMembership(store: ManagementStore, organizationId: string, principalId: string, scope?: ResourceScope): Membership | undefined;
|
|
893
|
+
/**
|
|
894
|
+
* Management-only add. Prefer addMemberInAuth when DATABASE_URL + PgStore.
|
|
895
|
+
* Idempotent: returns existing active membership without a second audit.
|
|
896
|
+
* Invalid roles fail closed with no write.
|
|
897
|
+
*/
|
|
898
|
+
declare function addMember(store: ManagementStore, input: {
|
|
899
|
+
organizationId: string;
|
|
900
|
+
principalId: string;
|
|
901
|
+
role?: string;
|
|
902
|
+
source?: MembershipSource;
|
|
903
|
+
actor?: string;
|
|
904
|
+
auditSource?: MembershipActorSource;
|
|
905
|
+
scope?: ResourceScope; /** Force a specific membership id (runtime id preservation) */
|
|
906
|
+
id?: string;
|
|
907
|
+
}): Membership;
|
|
908
|
+
/**
|
|
909
|
+
* Update membership role (management-only). Prefer updateMemberInAuth with
|
|
910
|
+
* DATABASE_URL + PgStore.
|
|
911
|
+
*/
|
|
912
|
+
declare function updateMember(store: ManagementStore, id: string, input: {
|
|
913
|
+
role: string;
|
|
914
|
+
actor?: string;
|
|
915
|
+
auditSource?: MembershipActorSource;
|
|
916
|
+
scope?: ResourceScope;
|
|
917
|
+
}): Membership;
|
|
918
|
+
/**
|
|
919
|
+
* Soft-remove membership (management-only). Prefer removeMemberInAuth with
|
|
920
|
+
* DATABASE_URL + PgStore. Destructive; CLI requires --yes.
|
|
921
|
+
*/
|
|
922
|
+
declare function removeMember(store: ManagementStore, id: string, input?: {
|
|
923
|
+
actor?: string;
|
|
924
|
+
auditSource?: MembershipActorSource;
|
|
925
|
+
scope?: ResourceScope;
|
|
926
|
+
}): Membership;
|
|
927
|
+
/**
|
|
928
|
+
* Update or remove by organization + principal (CLI convenience).
|
|
929
|
+
* Fails closed when membership missing (same as inspect).
|
|
930
|
+
*/
|
|
931
|
+
declare function resolveMembershipId(store: ManagementStore, input: {
|
|
932
|
+
organizationId: string;
|
|
933
|
+
principalId?: string;
|
|
934
|
+
membershipId?: string;
|
|
935
|
+
scope?: ResourceScope;
|
|
936
|
+
}, stage: string): string;
|
|
937
|
+
//#endregion
|
|
938
|
+
//#region src/services/core.d.ts
|
|
939
|
+
/** Validate and normalize a project name without mutating the store. */
|
|
940
|
+
declare function planProjectCreate(input: {
|
|
941
|
+
name: string;
|
|
942
|
+
}, existingProjects?: Project[]): Pick<Project, "name" | "slug">;
|
|
943
|
+
/** Create an additional project without changing the operator's active scope. */
|
|
944
|
+
declare function createProject(store: ManagementStore, input: {
|
|
945
|
+
name: string;
|
|
946
|
+
actor?: string;
|
|
947
|
+
source?: "cli" | "console" | "api";
|
|
948
|
+
}): Project;
|
|
949
|
+
declare function initProject(store: ManagementStore, input: {
|
|
950
|
+
name: string;
|
|
951
|
+
environment?: string;
|
|
952
|
+
actor?: string;
|
|
953
|
+
source?: "cli" | "console" | "api";
|
|
954
|
+
}): {
|
|
955
|
+
project: Project;
|
|
956
|
+
environment: Environment;
|
|
957
|
+
};
|
|
958
|
+
declare function listProjects(store: ManagementStore): Project[];
|
|
959
|
+
declare function createEnvironment(store: ManagementStore, input: {
|
|
960
|
+
projectId: string;
|
|
961
|
+
name: string;
|
|
962
|
+
kind?: Environment["kind"];
|
|
963
|
+
actor?: string;
|
|
964
|
+
}): Environment;
|
|
965
|
+
/** Validate an environment creation request without mutating the store. */
|
|
966
|
+
declare function planEnvironmentCreate(store: ManagementStore, input: {
|
|
967
|
+
projectId?: string;
|
|
968
|
+
name: string;
|
|
969
|
+
kind?: Environment["kind"];
|
|
970
|
+
}): Pick<Environment, "projectId" | "name" | "slug" | "kind">;
|
|
971
|
+
/**
|
|
972
|
+
* List environments for the operator's project (project-scoped).
|
|
973
|
+
* Environment rows are not dual-scoped; projectId must match principal project.
|
|
974
|
+
*/
|
|
975
|
+
declare function listEnvironments(store: ManagementStore, filter?: {
|
|
976
|
+
scope?: ResourceScope;
|
|
977
|
+
}): Environment[];
|
|
978
|
+
type EnvironmentLocalStatus = {
|
|
979
|
+
/** Whether this environment is the operator's active principal environment */active: boolean;
|
|
980
|
+
storeBackend: ManagementStore["backend"];
|
|
981
|
+
storePathPresent: boolean;
|
|
982
|
+
schemaVersion: number;
|
|
983
|
+
expectedSchemaVersion: number;
|
|
984
|
+
releaseVersion: string;
|
|
985
|
+
initialized: boolean; /** Configuration presence flags only — never secret values */
|
|
986
|
+
config: {
|
|
987
|
+
hasClearanceSecret: boolean;
|
|
988
|
+
hasDatabaseUrl: boolean;
|
|
989
|
+
hasOperatorToken: boolean;
|
|
990
|
+
hasCredentialKey: boolean;
|
|
991
|
+
nodeEnv: string;
|
|
992
|
+
operatorProjectIdConfigured: boolean;
|
|
993
|
+
operatorEnvironmentIdConfigured: boolean;
|
|
994
|
+
};
|
|
995
|
+
resourceCounts: {
|
|
996
|
+
principals: number;
|
|
997
|
+
organizations: number;
|
|
998
|
+
memberships: number;
|
|
999
|
+
identityConnections: number;
|
|
1000
|
+
directoryConnections: number;
|
|
1001
|
+
roles: number;
|
|
1002
|
+
sessions: number;
|
|
1003
|
+
events: number;
|
|
1004
|
+
};
|
|
1005
|
+
};
|
|
1006
|
+
type EnvironmentInspectResult = {
|
|
1007
|
+
environment: Environment;
|
|
1008
|
+
project: Project | null;
|
|
1009
|
+
scope: ResourceScope;
|
|
1010
|
+
local: EnvironmentLocalStatus;
|
|
1011
|
+
correlationId: string;
|
|
1012
|
+
};
|
|
1013
|
+
/**
|
|
1014
|
+
* Inspect a canonical environment plus truthful local status (no secrets).
|
|
1015
|
+
* Default id is the operator principal environment. Cross-project ids fail closed.
|
|
1016
|
+
*/
|
|
1017
|
+
declare function inspectEnvironment(store: ManagementStore, id?: string, opts?: {
|
|
1018
|
+
scope?: ResourceScope;
|
|
1019
|
+
}): EnvironmentInspectResult;
|
|
1020
|
+
type EnvironmentPromoteBlocker = {
|
|
1021
|
+
code: string;
|
|
1022
|
+
message: string;
|
|
1023
|
+
remediation: string;
|
|
1024
|
+
};
|
|
1025
|
+
type EnvironmentPromotePlanStep = {
|
|
1026
|
+
name: string;
|
|
1027
|
+
status: "planned" | "blocked" | "skipped" | "done";
|
|
1028
|
+
detail?: string;
|
|
1029
|
+
};
|
|
1030
|
+
type EnvironmentPromoteResult = {
|
|
1031
|
+
dryRun: boolean;
|
|
1032
|
+
applied: boolean;
|
|
1033
|
+
blocked: boolean;
|
|
1034
|
+
idempotent: boolean;
|
|
1035
|
+
wouldChange: boolean;
|
|
1036
|
+
source: Environment;
|
|
1037
|
+
target: Environment;
|
|
1038
|
+
scope: ResourceScope;
|
|
1039
|
+
plan: {
|
|
1040
|
+
action: "env.promote";
|
|
1041
|
+
description: string;
|
|
1042
|
+
resourceCounts: EnvironmentLocalStatus["resourceCounts"];
|
|
1043
|
+
steps: EnvironmentPromotePlanStep[];
|
|
1044
|
+
};
|
|
1045
|
+
blockers: EnvironmentPromoteBlocker[];
|
|
1046
|
+
correlationId: string;
|
|
1047
|
+
auditAction?: "env.promote";
|
|
1048
|
+
};
|
|
1049
|
+
/**
|
|
1050
|
+
* Plan (and optionally attempt) environment promotion.
|
|
1051
|
+
*
|
|
1052
|
+
* Grounded in existing Environment + scoped resource counts only. The snapshot
|
|
1053
|
+
* has no Deployment resource, so mutating apply always surfaces an explicit
|
|
1054
|
+
* structured blocker rather than inventing deployment state. Dry-run and
|
|
1055
|
+
* confirmed apply both validate inputs, return the same plan shape, and audit
|
|
1056
|
+
* confirmed attempts (including blocked/idempotent outcomes).
|
|
1057
|
+
*/
|
|
1058
|
+
declare function promoteEnvironment(store: ManagementStore, input: {
|
|
1059
|
+
/** Target environment id or slug (required) */to: string; /** Source environment id or slug; defaults to operator principal environment */
|
|
1060
|
+
from?: string; /** Preview only — default when confirm is not true */
|
|
1061
|
+
dryRun?: boolean; /** Required for a confirmed attempt (CLI --yes). Never invents deploy apply. */
|
|
1062
|
+
confirm?: boolean;
|
|
1063
|
+
scope?: ResourceScope;
|
|
1064
|
+
actor?: string;
|
|
1065
|
+
source?: "cli" | "console" | "api" | "system";
|
|
1066
|
+
}): EnvironmentPromoteResult;
|
|
1067
|
+
declare function createUser(store: ManagementStore, input: {
|
|
1068
|
+
email: string;
|
|
1069
|
+
name: string; /** Optional stable id (e.g. Clearance runtime user id) */
|
|
1070
|
+
id?: string;
|
|
1071
|
+
projectId?: string;
|
|
1072
|
+
environmentId?: string;
|
|
1073
|
+
externalId?: string;
|
|
1074
|
+
actor?: string;
|
|
1075
|
+
source?: "cli" | "console" | "api" | "import" | "scim";
|
|
1076
|
+
}): Principal;
|
|
1077
|
+
declare function listUsers(store: ManagementStore, filter?: {
|
|
1078
|
+
environmentId?: string;
|
|
1079
|
+
projectId?: string;
|
|
1080
|
+
status?: string; /** When true (default for scoped callers), require full scope filter */
|
|
1081
|
+
scope?: ResourceScope;
|
|
1082
|
+
}): Principal[];
|
|
1083
|
+
declare const USERS_LIST_DEFAULT_PAGE_LIMIT = 100;
|
|
1084
|
+
declare const USERS_LIST_MAX_PAGE_LIMIT = 1000;
|
|
1085
|
+
/**
|
|
1086
|
+
* Cursor-paginated users listing (FOLLOW.md P2.3.1), shared by CLI and API.
|
|
1087
|
+
* Ordering: createdAt ascending, then id ascending (documented keyset — see
|
|
1088
|
+
* pagination.ts for why keyset beats index cursors on the snapshot arrays).
|
|
1089
|
+
* Callers that need the full unpaginated legacy behavior keep using listUsers.
|
|
1090
|
+
*/
|
|
1091
|
+
declare function listUsersPage(store: ManagementStore, opts?: {
|
|
1092
|
+
scope?: ResourceScope;
|
|
1093
|
+
status?: string;
|
|
1094
|
+
limit?: number; /** Opaque cursor from a previous page's nextCursor (fail-closed). */
|
|
1095
|
+
cursor?: string;
|
|
1096
|
+
}): {
|
|
1097
|
+
users: Principal[];
|
|
1098
|
+
nextCursor: string | null;
|
|
1099
|
+
};
|
|
1100
|
+
/**
|
|
1101
|
+
* Lookup by id. When scope is provided, cross-scope ids fail closed as NOT_FOUND
|
|
1102
|
+
* without revealing that the foreign resource exists.
|
|
1103
|
+
*/
|
|
1104
|
+
declare function inspectUser(store: ManagementStore, id: string, scope?: ResourceScope): Principal;
|
|
1105
|
+
/** Fail-closed status validation shared by management + runtime lifecycle paths. */
|
|
1106
|
+
declare function parseUserStatusInput(status: unknown, stage?: string): "active" | "disabled" | undefined;
|
|
1107
|
+
/**
|
|
1108
|
+
* Update name, email, and/or status for a principal (management snapshot).
|
|
1109
|
+
* Soft-deleted users are fail-closed as NOT_FOUND. Status may be active|disabled
|
|
1110
|
+
* (not deleted — use deleteUser). Cross-scope ids fail closed as NOT_FOUND.
|
|
1111
|
+
* With DATABASE_URL prefer updateUserInAuth for runtime parity.
|
|
1112
|
+
*/
|
|
1113
|
+
declare function updateUser(store: ManagementStore, id: string, input: {
|
|
1114
|
+
name?: string;
|
|
1115
|
+
email?: string; /** Re-enable or set disabled without soft-delete. */
|
|
1116
|
+
status?: "active" | "disabled" | string;
|
|
1117
|
+
actor?: string;
|
|
1118
|
+
source?: "cli" | "console" | "api" | "import" | "scim" | "system";
|
|
1119
|
+
scope?: ResourceScope;
|
|
1120
|
+
}): Principal;
|
|
1121
|
+
/**
|
|
1122
|
+
* Disable a principal (status=disabled). Soft-deleted and cross-scope ids fail
|
|
1123
|
+
* closed as NOT_FOUND. Active management sessions for the user are revoked in
|
|
1124
|
+
* the same audited mutation.
|
|
1125
|
+
*/
|
|
1126
|
+
declare function disableUser(store: ManagementStore, id: string, input?: {
|
|
1127
|
+
actor?: string;
|
|
1128
|
+
source?: "cli" | "console" | "api" | "import" | "scim" | "system";
|
|
1129
|
+
scope?: ResourceScope;
|
|
1130
|
+
}): Principal;
|
|
1131
|
+
/**
|
|
1132
|
+
* Soft-delete a principal (status=deleted). Removed from list/inspect thereafter.
|
|
1133
|
+
* Cross-scope ids fail closed as NOT_FOUND. Active sessions are revoked atomically.
|
|
1134
|
+
*/
|
|
1135
|
+
declare function deleteUser(store: ManagementStore, id: string, input?: {
|
|
1136
|
+
actor?: string;
|
|
1137
|
+
source?: "cli" | "console" | "api" | "import" | "scim" | "system";
|
|
1138
|
+
scope?: ResourceScope;
|
|
1139
|
+
}): Principal;
|
|
1140
|
+
declare function createOrganization(store: ManagementStore, input: {
|
|
1141
|
+
name: string;
|
|
1142
|
+
slug?: string;
|
|
1143
|
+
id?: string;
|
|
1144
|
+
projectId?: string;
|
|
1145
|
+
environmentId?: string;
|
|
1146
|
+
externalId?: string;
|
|
1147
|
+
actor?: string;
|
|
1148
|
+
source?: "cli" | "console" | "api" | "import";
|
|
1149
|
+
}): Organization;
|
|
1150
|
+
declare function listOrganizations(store: ManagementStore, filter?: {
|
|
1151
|
+
environmentId?: string;
|
|
1152
|
+
projectId?: string;
|
|
1153
|
+
scope?: ResourceScope;
|
|
1154
|
+
}): Organization[];
|
|
1155
|
+
declare const ORGS_LIST_DEFAULT_PAGE_LIMIT = 100;
|
|
1156
|
+
declare const ORGS_LIST_MAX_PAGE_LIMIT = 1000;
|
|
1157
|
+
/**
|
|
1158
|
+
* Cursor-paginated organizations listing (FOLLOW.md P2.3.1).
|
|
1159
|
+
* Ordering: createdAt ascending, then id ascending (documented keyset).
|
|
1160
|
+
*/
|
|
1161
|
+
declare function listOrganizationsPage(store: ManagementStore, opts?: {
|
|
1162
|
+
scope?: ResourceScope;
|
|
1163
|
+
limit?: number; /** Opaque cursor from a previous page's nextCursor (fail-closed). */
|
|
1164
|
+
cursor?: string;
|
|
1165
|
+
}): {
|
|
1166
|
+
organizations: Organization[];
|
|
1167
|
+
nextCursor: string | null;
|
|
1168
|
+
};
|
|
1169
|
+
declare function inspectOrganization(store: ManagementStore, id: string, scope?: ResourceScope): Organization;
|
|
1170
|
+
/**
|
|
1171
|
+
* Update legitimate mutable organization fields: name and/or slug.
|
|
1172
|
+
* Status is not mutable here — use archiveOrganization. Soft-archived and
|
|
1173
|
+
* cross-scope ids fail closed as ORG_NOT_FOUND. Idempotent when values match.
|
|
1174
|
+
* Audits only when at least one field actually changes.
|
|
1175
|
+
*/
|
|
1176
|
+
declare function updateOrganization(store: ManagementStore, id: string, input: {
|
|
1177
|
+
name?: string;
|
|
1178
|
+
slug?: string;
|
|
1179
|
+
actor?: string;
|
|
1180
|
+
source?: "cli" | "console" | "api" | "import" | "system";
|
|
1181
|
+
scope?: ResourceScope;
|
|
1182
|
+
}): Organization;
|
|
1183
|
+
type ArchiveOrganizationResult = {
|
|
1184
|
+
organization: Organization;
|
|
1185
|
+
dryRun: boolean;
|
|
1186
|
+
idempotent: boolean;
|
|
1187
|
+
wouldChange: boolean;
|
|
1188
|
+
};
|
|
1189
|
+
/**
|
|
1190
|
+
* Archive an organization (status=archived). Requires confirm=true for mutation
|
|
1191
|
+
* (CLI --yes). Dry-run previews without audit. Idempotent re-archive succeeds
|
|
1192
|
+
* without a second audit when already archived. Membership list/add/update/remove
|
|
1193
|
+
* and org inspect remain fail-closed for archived orgs (recovery via audit + row).
|
|
1194
|
+
*/
|
|
1195
|
+
declare function archiveOrganization(store: ManagementStore, id: string, input?: {
|
|
1196
|
+
dryRun?: boolean; /** Required for mutation. CLI maps --yes → confirm=true. */
|
|
1197
|
+
confirm?: boolean;
|
|
1198
|
+
actor?: string;
|
|
1199
|
+
source?: "cli" | "console" | "api" | "import" | "system";
|
|
1200
|
+
scope?: ResourceScope;
|
|
1201
|
+
}): ArchiveOrganizationResult;
|
|
1202
|
+
declare const USERS_EXPORT_DEFAULT_LIMIT = 100;
|
|
1203
|
+
declare const USERS_EXPORT_MAX_LIMIT = 1000;
|
|
1204
|
+
declare const USERS_EXPORT_FORMATS: readonly ["json", "jsonl"];
|
|
1205
|
+
type UsersExportFormat = (typeof USERS_EXPORT_FORMATS)[number];
|
|
1206
|
+
type UsersExportOptions = {
|
|
1207
|
+
limit?: number; /** Filter by principal status (active|disabled). Deleted never exported. */
|
|
1208
|
+
status?: "active" | "disabled" | string;
|
|
1209
|
+
format?: UsersExportFormat | string; /** Absolute or relative path; when set, artifact is written atomically (CLI only) */
|
|
1210
|
+
outputPath?: string;
|
|
1211
|
+
force?: boolean;
|
|
1212
|
+
scope?: ResourceScope;
|
|
1213
|
+
actor?: string;
|
|
1214
|
+
source?: "cli" | "console" | "api" | "system";
|
|
1215
|
+
skipAudit?: boolean;
|
|
1216
|
+
};
|
|
1217
|
+
type UsersExportEnvelope = {
|
|
1218
|
+
schemaVersion: 1;
|
|
1219
|
+
kind: "users.export";
|
|
1220
|
+
exportedAt: string;
|
|
1221
|
+
format: UsersExportFormat;
|
|
1222
|
+
scope: ResourceScope;
|
|
1223
|
+
limit: number;
|
|
1224
|
+
count: number;
|
|
1225
|
+
truncated: boolean;
|
|
1226
|
+
filters: {
|
|
1227
|
+
status?: "active" | "disabled";
|
|
1228
|
+
};
|
|
1229
|
+
users: Principal[];
|
|
1230
|
+
outputPath?: string;
|
|
1231
|
+
correlationId: string;
|
|
1232
|
+
};
|
|
1233
|
+
declare function normalizeUsersExportLimit(limit: number | undefined): number;
|
|
1234
|
+
declare function normalizeUsersExportFormat(format: string | undefined): UsersExportFormat;
|
|
1235
|
+
declare function normalizeUsersExportStatus(status: string | undefined): "active" | "disabled" | undefined;
|
|
1236
|
+
/** Stable sort: email asc, then id asc. */
|
|
1237
|
+
declare function sortUsersDeterministic(users: Principal[]): Principal[];
|
|
1238
|
+
/** Public export view of a principal — no write-only secrets (none stored). */
|
|
1239
|
+
declare function sanitizePrincipalForExport(user: Principal): Principal;
|
|
1240
|
+
declare function selectUsersForExport(store: ManagementStore, filter: {
|
|
1241
|
+
limit: number;
|
|
1242
|
+
status?: "active" | "disabled";
|
|
1243
|
+
scope: ResourceScope;
|
|
1244
|
+
}): {
|
|
1245
|
+
users: Principal[];
|
|
1246
|
+
truncated: boolean;
|
|
1247
|
+
};
|
|
1248
|
+
/**
|
|
1249
|
+
* Export principals: scoped, bounded, redacted, deterministic.
|
|
1250
|
+
* Optional file write is atomic and refuse-overwrite by default (CLI).
|
|
1251
|
+
* Audit never persists local filesystem paths — only wroteFile boolean.
|
|
1252
|
+
*/
|
|
1253
|
+
declare function exportUsers(store: ManagementStore, opts?: UsersExportOptions): UsersExportEnvelope;
|
|
1254
|
+
declare function listEvents(store: ManagementStore, filter?: {
|
|
1255
|
+
limit?: number;
|
|
1256
|
+
organizationId?: string;
|
|
1257
|
+
action?: string;
|
|
1258
|
+
scope?: ResourceScope;
|
|
1259
|
+
}): AuditEvent[];
|
|
1260
|
+
declare const EVENTS_LIST_DEFAULT_PAGE_LIMIT = 50;
|
|
1261
|
+
declare const EVENTS_LIST_MAX_PAGE_LIMIT = 1000;
|
|
1262
|
+
/**
|
|
1263
|
+
* Cursor-paginated audit events listing (FOLLOW.md P2.3.1).
|
|
1264
|
+
* Ordering: createdAt descending, then id descending (newest first — matches
|
|
1265
|
+
* the deterministic export order in events.ts). Keyset cursors survive the
|
|
1266
|
+
* prepend-heavy events array where index cursors would duplicate rows.
|
|
1267
|
+
*/
|
|
1268
|
+
declare function listEventsPage(store: ManagementStore, filter?: {
|
|
1269
|
+
limit?: number;
|
|
1270
|
+
organizationId?: string;
|
|
1271
|
+
action?: string;
|
|
1272
|
+
scope?: ResourceScope; /** Opaque cursor from a previous page's nextCursor (fail-closed). */
|
|
1273
|
+
cursor?: string;
|
|
1274
|
+
}): {
|
|
1275
|
+
events: AuditEvent[];
|
|
1276
|
+
nextCursor: string | null;
|
|
1277
|
+
};
|
|
1278
|
+
declare function createSession(store: ManagementStore, input: {
|
|
1279
|
+
principalId: string;
|
|
1280
|
+
environmentId: string;
|
|
1281
|
+
scope?: ResourceScope;
|
|
1282
|
+
}): SessionRecord;
|
|
1283
|
+
declare function overviewStats(store: ManagementStore, scope?: ResourceScope): {
|
|
1284
|
+
totalUsers: number;
|
|
1285
|
+
activeUsers: number;
|
|
1286
|
+
organizations: number;
|
|
1287
|
+
activeSessions: number;
|
|
1288
|
+
recentEvents: AuditEvent[];
|
|
1289
|
+
releaseVersion: string;
|
|
1290
|
+
schemaVersion: number;
|
|
1291
|
+
resourceCounts: Record<string, number>;
|
|
1292
|
+
};
|
|
1293
|
+
//#endregion
|
|
1294
|
+
//#region src/services/export-artifact.d.ts
|
|
1295
|
+
type WriteExportArtifactCodes = {
|
|
1296
|
+
/** Audit/export stage string (e.g. events.export) */stage?: string; /** Error code when destination exists and force is false */
|
|
1297
|
+
existsCode?: string; /** Error code when write fails for other reasons */
|
|
1298
|
+
writeFailedCode?: string;
|
|
1299
|
+
};
|
|
1300
|
+
/**
|
|
1301
|
+
* Write export body atomically. Refuses to overwrite unless force.
|
|
1302
|
+
* On failure leaves no partial final artifact at outputPath.
|
|
1303
|
+
* Mode 0o600 — owner read/write only.
|
|
1304
|
+
*/
|
|
1305
|
+
declare function writeExportArtifact(outputPath: string, body: string, force: boolean, codes?: WriteExportArtifactCodes): string;
|
|
1306
|
+
//#endregion
|
|
1307
|
+
//#region src/services/events.d.ts
|
|
1308
|
+
declare const EVENTS_EXPORT_DEFAULT_LIMIT = 100;
|
|
1309
|
+
declare const EVENTS_EXPORT_MAX_LIMIT = 1000;
|
|
1310
|
+
declare const EVENTS_TAIL_DEFAULT_LIMIT = 20;
|
|
1311
|
+
declare const EVENTS_TAIL_MAX_LIMIT = 1000;
|
|
1312
|
+
declare const EVENTS_EXPORT_FORMATS: readonly ["json", "jsonl"];
|
|
1313
|
+
type EventsExportFormat = (typeof EVENTS_EXPORT_FORMATS)[number];
|
|
1314
|
+
type EventsTailFilter = {
|
|
1315
|
+
organizationId?: string;
|
|
1316
|
+
action?: string;
|
|
1317
|
+
scope?: ResourceScope;
|
|
1318
|
+
};
|
|
1319
|
+
/**
|
|
1320
|
+
* Tail cursor state is intentionally local to one CLI invocation. It records
|
|
1321
|
+
* every event visible at startup so a later poll never backfills old history.
|
|
1322
|
+
*/
|
|
1323
|
+
type EventsTailCursor = {
|
|
1324
|
+
readonly scope: ResourceScope;
|
|
1325
|
+
readonly organizationId?: string;
|
|
1326
|
+
readonly action?: string;
|
|
1327
|
+
readonly seenIds: Set<string>;
|
|
1328
|
+
};
|
|
1329
|
+
/**
|
|
1330
|
+
* Subsystems with a defined safe diagnostic re-record path.
|
|
1331
|
+
* Only SCIM has a repository replay service today (no domain state mutation).
|
|
1332
|
+
*/
|
|
1333
|
+
declare const REPLAYABLE_TRACE_SUBSYSTEMS: readonly ["scim"];
|
|
1334
|
+
type ReplayableTraceSubsystem = (typeof REPLAYABLE_TRACE_SUBSYSTEMS)[number];
|
|
1335
|
+
type EventsExportOptions = {
|
|
1336
|
+
limit?: number;
|
|
1337
|
+
organizationId?: string;
|
|
1338
|
+
action?: string;
|
|
1339
|
+
/**
|
|
1340
|
+
* Export only events created strictly BEFORE this ISO-8601 timestamp.
|
|
1341
|
+
* Archival bound (see audit.ts header): schedule exports with --before so
|
|
1342
|
+
* events are captured before the retention cap prunes them.
|
|
1343
|
+
*/
|
|
1344
|
+
before?: string; /** json (envelope) or jsonl (one event object per line) */
|
|
1345
|
+
format?: EventsExportFormat | string; /** Absolute or relative path; when set, artifact is written atomically */
|
|
1346
|
+
outputPath?: string; /** Allow replacing an existing file at outputPath */
|
|
1347
|
+
force?: boolean;
|
|
1348
|
+
scope?: ResourceScope;
|
|
1349
|
+
actor?: string;
|
|
1350
|
+
source?: AuditEvent["source"]; /** When true, skip privileged-read audit (tests / nested callers) */
|
|
1351
|
+
skipAudit?: boolean;
|
|
1352
|
+
};
|
|
1353
|
+
type EventsExportEnvelope = {
|
|
1354
|
+
schemaVersion: 1;
|
|
1355
|
+
kind: "events.export";
|
|
1356
|
+
exportedAt: string;
|
|
1357
|
+
format: EventsExportFormat;
|
|
1358
|
+
scope: ResourceScope;
|
|
1359
|
+
limit: number;
|
|
1360
|
+
count: number;
|
|
1361
|
+
truncated: boolean;
|
|
1362
|
+
filters: {
|
|
1363
|
+
organizationId?: string;
|
|
1364
|
+
action?: string;
|
|
1365
|
+
before?: string;
|
|
1366
|
+
};
|
|
1367
|
+
events: AuditEvent[];
|
|
1368
|
+
outputPath?: string;
|
|
1369
|
+
correlationId: string;
|
|
1370
|
+
};
|
|
1371
|
+
type EventInspectResult = {
|
|
1372
|
+
event?: AuditEvent;
|
|
1373
|
+
trace?: DiagnosticTrace;
|
|
1374
|
+
scope: ResourceScope;
|
|
1375
|
+
replayable: boolean;
|
|
1376
|
+
replayBlocker?: string;
|
|
1377
|
+
};
|
|
1378
|
+
type ReplayDiagnosticOptions = {
|
|
1379
|
+
/** Preview only — default safe path when confirm is not true */dryRun?: boolean;
|
|
1380
|
+
/**
|
|
1381
|
+
* Required for mutating apply. CLI maps --yes → confirm=true.
|
|
1382
|
+
* When confirm is not true, the service always dry-runs.
|
|
1383
|
+
*/
|
|
1384
|
+
confirm?: boolean;
|
|
1385
|
+
scope?: ResourceScope;
|
|
1386
|
+
actor?: string;
|
|
1387
|
+
source?: AuditEvent["source"];
|
|
1388
|
+
};
|
|
1389
|
+
type ReplayDiagnosticResult = {
|
|
1390
|
+
dryRun: boolean;
|
|
1391
|
+
idempotent: boolean;
|
|
1392
|
+
wouldChange: boolean;
|
|
1393
|
+
replayable: true;
|
|
1394
|
+
original: DiagnosticTrace;
|
|
1395
|
+
trace: DiagnosticTrace;
|
|
1396
|
+
scope: ResourceScope;
|
|
1397
|
+
auditAction?: "events.replay" | "scim.replay";
|
|
1398
|
+
};
|
|
1399
|
+
declare function normalizeEventsExportLimit(limit: number | undefined): number;
|
|
1400
|
+
declare function normalizeEventsTailLimit(limit: number | undefined): number;
|
|
1401
|
+
/** Fail-closed --before parsing: ISO-8601, normalized for lexical compare. */
|
|
1402
|
+
declare function normalizeEventsExportBefore(before: string | undefined): string | undefined;
|
|
1403
|
+
declare function normalizeEventsExportFormat(format: string | undefined): EventsExportFormat;
|
|
1404
|
+
/** Stable sort: newest first, then id descending for total order. */
|
|
1405
|
+
declare function sortEventsDeterministic(events: AuditEvent[]): AuditEvent[];
|
|
1406
|
+
/** Defense-in-depth redaction of an already-stored audit event. */
|
|
1407
|
+
declare function sanitizeAuditEvent(event: AuditEvent): AuditEvent;
|
|
1408
|
+
declare function selectEventsForExport(store: ManagementStore, filter: {
|
|
1409
|
+
limit: number;
|
|
1410
|
+
organizationId?: string;
|
|
1411
|
+
action?: string; /** Normalized ISO bound — only events with createdAt strictly before */
|
|
1412
|
+
before?: string;
|
|
1413
|
+
scope: ResourceScope;
|
|
1414
|
+
}): {
|
|
1415
|
+
events: AuditEvent[];
|
|
1416
|
+
truncated: boolean;
|
|
1417
|
+
};
|
|
1418
|
+
/**
|
|
1419
|
+
* Select the newest N matching events, then emit that initial history oldest
|
|
1420
|
+
* first. All currently visible ids become the cursor baseline; later polls
|
|
1421
|
+
* therefore only emit events that appeared after tailing began.
|
|
1422
|
+
*/
|
|
1423
|
+
declare function beginEventsTail(store: ManagementStore, filter?: EventsTailFilter & {
|
|
1424
|
+
limit?: number;
|
|
1425
|
+
}): {
|
|
1426
|
+
cursor: EventsTailCursor;
|
|
1427
|
+
events: AuditEvent[];
|
|
1428
|
+
};
|
|
1429
|
+
/**
|
|
1430
|
+
* Return newly observed scoped events in chronological order. Callers refresh
|
|
1431
|
+
* their store before every poll; this works for both shared Postgres and a
|
|
1432
|
+
* JsonStore modified by another process.
|
|
1433
|
+
*/
|
|
1434
|
+
declare function pollEventsTail(store: ManagementStore, cursor: EventsTailCursor): AuditEvent[];
|
|
1435
|
+
/**
|
|
1436
|
+
* Export audit events: scoped, bounded, redacted, deterministic.
|
|
1437
|
+
* Optional file write is atomic and refuse-overwrite by default.
|
|
1438
|
+
*/
|
|
1439
|
+
declare function exportEvents(store: ManagementStore, opts?: EventsExportOptions): EventsExportEnvelope;
|
|
1440
|
+
declare function isReplayableTraceSubsystem(subsystem: DiagnosticTrace["subsystem"]): subsystem is ReplayableTraceSubsystem;
|
|
1441
|
+
/**
|
|
1442
|
+
* Inspect an audit event and/or diagnostic trace by id / correlation id.
|
|
1443
|
+
* Fail closed for wrong-scope resources (same as missing).
|
|
1444
|
+
*/
|
|
1445
|
+
declare function inspectEvent(store: ManagementStore, id: string, opts?: {
|
|
1446
|
+
scope?: ResourceScope;
|
|
1447
|
+
}): EventInspectResult;
|
|
1448
|
+
/**
|
|
1449
|
+
* Replay a diagnostic trace via the shared SCIM replay service.
|
|
1450
|
+
*
|
|
1451
|
+
* - Defaults to dry-run when dryRun is true or confirm is not set.
|
|
1452
|
+
* - Mutating apply requires confirm=true (CLI --yes) and only works for SCIM.
|
|
1453
|
+
* - Idempotent: a prior scim.replay/events.replay of the same original returns
|
|
1454
|
+
* the existing artifact without writing another.
|
|
1455
|
+
* - Never re-executes directory mutations or arbitrary audit actions.
|
|
1456
|
+
*/
|
|
1457
|
+
declare function replayDiagnosticTrace(store: ManagementStore, id: string, opts?: ReplayDiagnosticOptions): ReplayDiagnosticResult;
|
|
1458
|
+
//#endregion
|
|
1459
|
+
//#region src/services/sessions.d.ts
|
|
1460
|
+
/** Safe operator-facing session view — never includes token material. */
|
|
1461
|
+
type SessionView = {
|
|
1462
|
+
id: string;
|
|
1463
|
+
principalId: string;
|
|
1464
|
+
projectId: string;
|
|
1465
|
+
environmentId: string;
|
|
1466
|
+
status: "active" | "revoked";
|
|
1467
|
+
createdAt: string;
|
|
1468
|
+
expiresAt?: string;
|
|
1469
|
+
revokedAt?: string;
|
|
1470
|
+
ipAddress?: string;
|
|
1471
|
+
userAgent?: string;
|
|
1472
|
+
};
|
|
1473
|
+
type SessionSource = "cli" | "console" | "api" | "system";
|
|
1474
|
+
type RevokeSessionResult = {
|
|
1475
|
+
session: SessionView; /** True when session was already revoked / absent under authorized contract */
|
|
1476
|
+
idempotent: boolean;
|
|
1477
|
+
};
|
|
1478
|
+
declare function normalizeSessionLimit(limit: number | undefined): number;
|
|
1479
|
+
/**
|
|
1480
|
+
* Strip any credential-like fields from a session-shaped object before return.
|
|
1481
|
+
* Defense in depth: list/revoke must never surface raw tokens.
|
|
1482
|
+
*/
|
|
1483
|
+
declare function sanitizeSessionView(input: Record<string, unknown>): SessionView;
|
|
1484
|
+
declare function toSessionView(session: SessionRecord, projectId: string, extra?: Partial<SessionView>): SessionView;
|
|
1485
|
+
/**
|
|
1486
|
+
* List management-snapshot sessions under principal-derived scope.
|
|
1487
|
+
* Defaults to active only. Does not audit (list is not privileged-write).
|
|
1488
|
+
*/
|
|
1489
|
+
declare function listSessions(store: ManagementStore, opts?: {
|
|
1490
|
+
scope?: ResourceScope; /** When true, include revoked tombstones */
|
|
1491
|
+
includeRevoked?: boolean;
|
|
1492
|
+
limit?: number;
|
|
1493
|
+
}): SessionView[];
|
|
1494
|
+
/**
|
|
1495
|
+
* Cursor-paginated management-snapshot sessions (FOLLOW.md P2.3.1).
|
|
1496
|
+
* Ordering: createdAt descending, then id descending (newest first; the id
|
|
1497
|
+
* tiebreak makes the keyset a strict total order — see pagination.ts).
|
|
1498
|
+
* Postgres runtime sessions paginate via listSessionsPageInAuth (auth-bridge).
|
|
1499
|
+
*/
|
|
1500
|
+
declare function listSessionsPage(store: ManagementStore, opts?: {
|
|
1501
|
+
scope?: ResourceScope; /** When true, include revoked tombstones */
|
|
1502
|
+
includeRevoked?: boolean;
|
|
1503
|
+
limit?: number; /** Opaque cursor from a previous page's nextCursor (fail-closed). */
|
|
1504
|
+
cursor?: string;
|
|
1505
|
+
}): {
|
|
1506
|
+
sessions: SessionView[];
|
|
1507
|
+
nextCursor: string | null;
|
|
1508
|
+
};
|
|
1509
|
+
/** Load one safe session view under scope without changing state. */
|
|
1510
|
+
declare function inspectSession(store: ManagementStore, id: string, opts?: {
|
|
1511
|
+
scope?: ResourceScope;
|
|
1512
|
+
}): SessionView;
|
|
1513
|
+
/**
|
|
1514
|
+
* Revoke a management-snapshot session by stable id.
|
|
1515
|
+
* Idempotent: already-revoked under scope returns success with idempotent=true.
|
|
1516
|
+
* Missing or cross-scope fails closed as SESSION_NOT_FOUND.
|
|
1517
|
+
* Always writes an audit event (including idempotent re-revoke).
|
|
1518
|
+
*/
|
|
1519
|
+
declare function revokeSession(store: ManagementStore, id: string, input?: {
|
|
1520
|
+
actor?: string;
|
|
1521
|
+
source?: SessionSource;
|
|
1522
|
+
scope?: ResourceScope;
|
|
1523
|
+
}): RevokeSessionResult;
|
|
1524
|
+
/** Test/helper: ensure principal exists in scope (re-export pattern). */
|
|
1525
|
+
declare function assertSessionPrincipalInScope(store: ManagementStore, principalId: string, scope?: ResourceScope): void;
|
|
1526
|
+
//#endregion
|
|
1527
|
+
//#region src/services/api-keys.d.ts
|
|
1528
|
+
type ApiKeyView = Omit<ApiKey, "digest">;
|
|
1529
|
+
type CreatedApiKey = {
|
|
1530
|
+
apiKey: ApiKeyView;
|
|
1531
|
+
secret: string;
|
|
1532
|
+
};
|
|
1533
|
+
/** Strict normalized scope validation: lowercase, no empty/malformed/duplicates. */
|
|
1534
|
+
declare function normalizeAndValidateApiKeyScopes(input: unknown, stage: string): string[];
|
|
1535
|
+
declare function validateApiKeyName(value: unknown, stage: string): string;
|
|
1536
|
+
declare function listApiKeys(store: ManagementStore, input?: {
|
|
1537
|
+
scope?: ResourceScope;
|
|
1538
|
+
includeRevoked?: boolean;
|
|
1539
|
+
}): ApiKeyView[];
|
|
1540
|
+
declare function inspectApiKey(store: ManagementStore, id: string, input?: {
|
|
1541
|
+
scope?: ResourceScope;
|
|
1542
|
+
}): ApiKeyView;
|
|
1543
|
+
declare function createApiKey(store: ManagementStore, input: {
|
|
1544
|
+
name: unknown;
|
|
1545
|
+
scopes?: unknown;
|
|
1546
|
+
projectId?: string;
|
|
1547
|
+
environmentId?: string;
|
|
1548
|
+
scope?: ResourceScope;
|
|
1549
|
+
actor?: string;
|
|
1550
|
+
source?: "cli" | "console" | "api";
|
|
1551
|
+
}): Promise<CreatedApiKey>;
|
|
1552
|
+
declare function rotateApiKey(store: ManagementStore, id: string, input?: {
|
|
1553
|
+
scope?: ResourceScope;
|
|
1554
|
+
actor?: string;
|
|
1555
|
+
source?: "cli" | "console" | "api";
|
|
1556
|
+
}): Promise<CreatedApiKey & {
|
|
1557
|
+
revokedKey: ApiKeyView;
|
|
1558
|
+
}>;
|
|
1559
|
+
declare function revokeApiKey(store: ManagementStore, id: string, input?: {
|
|
1560
|
+
scope?: ResourceScope;
|
|
1561
|
+
actor?: string;
|
|
1562
|
+
source?: "cli" | "console" | "api";
|
|
1563
|
+
}): Promise<{
|
|
1564
|
+
apiKey: ApiKeyView;
|
|
1565
|
+
idempotent: boolean;
|
|
1566
|
+
}>;
|
|
1567
|
+
//#endregion
|
|
1568
|
+
//#region src/services/roles.d.ts
|
|
1569
|
+
/** Reserved built-in role slugs — cannot be created or mutated as custom roles */
|
|
1570
|
+
declare const BUILT_IN_ROLE_SLUGS: readonly ["owner", "admin", "member"];
|
|
1571
|
+
type BuiltInRoleSlug = (typeof BUILT_IN_ROLE_SLUGS)[number];
|
|
1572
|
+
declare function isBuiltInRoleSlug(slug: string): slug is BuiltInRoleSlug;
|
|
1573
|
+
declare function builtInRoleId(slug: BuiltInRoleSlug): string;
|
|
1574
|
+
type AssignableRole = {
|
|
1575
|
+
slug: string;
|
|
1576
|
+
kind: "built_in" | "custom";
|
|
1577
|
+
roleId: string;
|
|
1578
|
+
};
|
|
1579
|
+
/**
|
|
1580
|
+
* Resolve a role slug for membership assignment within principal-derived scope.
|
|
1581
|
+
* Built-in owner/admin/member always accepted. Custom roles must be active and
|
|
1582
|
+
* in the same project/environment; organization-bound roles must match the org.
|
|
1583
|
+
* Fail closed with stable structured errors for missing/foreign/disabled/archived.
|
|
1584
|
+
*/
|
|
1585
|
+
declare function resolveAssignableRole(store: ManagementStore, roleInput: unknown, opts: {
|
|
1586
|
+
scope: ResourceScope;
|
|
1587
|
+
organizationId: string;
|
|
1588
|
+
stage: string;
|
|
1589
|
+
}): AssignableRole;
|
|
1590
|
+
/**
|
|
1591
|
+
* Normalize and validate permission tokens.
|
|
1592
|
+
* - trim + lowercase
|
|
1593
|
+
* - reject empty / malformed / duplicates
|
|
1594
|
+
* - return stably sorted unique list
|
|
1595
|
+
*/
|
|
1596
|
+
declare function normalizeAndValidatePermissions(input: unknown, stage: string): string[];
|
|
1597
|
+
declare function validateRoleName(name: unknown, stage: string): string;
|
|
1598
|
+
declare function validateRoleSlug(slug: unknown, stage: string): string;
|
|
1599
|
+
/**
|
|
1600
|
+
* Pure validation of a role draft (name/slug/permissions).
|
|
1601
|
+
* Requires scope resolution for API consistency even though nothing is persisted.
|
|
1602
|
+
*/
|
|
1603
|
+
declare function validateRole(store: ManagementStore, input: {
|
|
1604
|
+
name?: unknown;
|
|
1605
|
+
slug?: unknown;
|
|
1606
|
+
permissions?: unknown;
|
|
1607
|
+
scope?: ResourceScope;
|
|
1608
|
+
projectId?: string;
|
|
1609
|
+
environmentId?: string;
|
|
1610
|
+
}): {
|
|
1611
|
+
ok: true;
|
|
1612
|
+
name?: string;
|
|
1613
|
+
slug?: string;
|
|
1614
|
+
permissions?: string[];
|
|
1615
|
+
scope: ResourceScope;
|
|
1616
|
+
};
|
|
1617
|
+
declare function listRoles(store: ManagementStore, filter?: {
|
|
1618
|
+
scope?: ResourceScope;
|
|
1619
|
+
}): CustomRole[];
|
|
1620
|
+
declare function inspectRole(store: ManagementStore, id: string, scope?: ResourceScope): CustomRole;
|
|
1621
|
+
declare function createRole(store: ManagementStore, input: {
|
|
1622
|
+
name: string;
|
|
1623
|
+
slug?: string;
|
|
1624
|
+
description?: string;
|
|
1625
|
+
permissions: string[];
|
|
1626
|
+
projectId?: string;
|
|
1627
|
+
environmentId?: string;
|
|
1628
|
+
actor?: string;
|
|
1629
|
+
source?: "cli" | "console" | "api";
|
|
1630
|
+
scope?: ResourceScope;
|
|
1631
|
+
}): Promise<CustomRole>;
|
|
1632
|
+
declare function updateRole(store: ManagementStore, id: string, input: {
|
|
1633
|
+
name?: string;
|
|
1634
|
+
description?: string | null;
|
|
1635
|
+
permissions?: string[];
|
|
1636
|
+
actor?: string;
|
|
1637
|
+
source?: "cli" | "console" | "api";
|
|
1638
|
+
scope?: ResourceScope;
|
|
1639
|
+
}): Promise<CustomRole>;
|
|
1640
|
+
//#endregion
|
|
1641
|
+
//#region src/services/members-import.d.ts
|
|
1642
|
+
type MemberImportFormat = "json" | "csv";
|
|
1643
|
+
type MemberImportPlanRow = {
|
|
1644
|
+
row: number;
|
|
1645
|
+
principalId: string;
|
|
1646
|
+
role: string;
|
|
1647
|
+
idempotent: boolean;
|
|
1648
|
+
};
|
|
1649
|
+
type MemberImportPlan = {
|
|
1650
|
+
organizationId: string;
|
|
1651
|
+
format: MemberImportFormat;
|
|
1652
|
+
rows: MemberImportPlanRow[];
|
|
1653
|
+
summary: {
|
|
1654
|
+
total: number;
|
|
1655
|
+
wouldAdd: number;
|
|
1656
|
+
idempotent: number;
|
|
1657
|
+
};
|
|
1658
|
+
};
|
|
1659
|
+
type MemberImportRowResult = {
|
|
1660
|
+
row: number;
|
|
1661
|
+
principalId: string;
|
|
1662
|
+
status: "success" | "idempotent" | "failure";
|
|
1663
|
+
error?: {
|
|
1664
|
+
code: string;
|
|
1665
|
+
stage: string;
|
|
1666
|
+
retryable: boolean;
|
|
1667
|
+
};
|
|
1668
|
+
};
|
|
1669
|
+
type MemberImportResult = MemberImportPlan["summary"] & {
|
|
1670
|
+
completed: true;
|
|
1671
|
+
partial: boolean;
|
|
1672
|
+
results: MemberImportRowResult[];
|
|
1673
|
+
success: number;
|
|
1674
|
+
failure: number;
|
|
1675
|
+
};
|
|
1676
|
+
declare function planMemberImport(store: ManagementStore, input: {
|
|
1677
|
+
organizationId: string;
|
|
1678
|
+
content: string;
|
|
1679
|
+
format: MemberImportFormat;
|
|
1680
|
+
}): MemberImportPlan;
|
|
1681
|
+
/** Applies a fully validated plan in deterministic file order. Each callback is durable before the next row. */
|
|
1682
|
+
declare function executeMemberImportPlan(plan: MemberImportPlan, apply: (row: MemberImportPlanRow) => Promise<Membership>): Promise<MemberImportResult>;
|
|
1683
|
+
//#endregion
|
|
1684
|
+
//#region src/services/identity.d.ts
|
|
1685
|
+
type RuntimeUserIdentity = {
|
|
1686
|
+
/** Canonical Clearance / runtime user id — becomes management principal id */id: string;
|
|
1687
|
+
email: string;
|
|
1688
|
+
name: string;
|
|
1689
|
+
createdAt?: string | Date;
|
|
1690
|
+
updatedAt?: string | Date;
|
|
1691
|
+
};
|
|
1692
|
+
type RuntimeOrganizationIdentity = {
|
|
1693
|
+
id: string;
|
|
1694
|
+
name: string;
|
|
1695
|
+
slug: string;
|
|
1696
|
+
createdAt?: string | Date;
|
|
1697
|
+
/**
|
|
1698
|
+
* Canonical Clearance owner membership id. When present, management
|
|
1699
|
+
* membership for the owner uses this exact stable id (create or reconcile).
|
|
1700
|
+
*/
|
|
1701
|
+
ownerMembershipId?: string;
|
|
1702
|
+
};
|
|
1703
|
+
/**
|
|
1704
|
+
* Persist a runtime user into the management store with the identical stable id.
|
|
1705
|
+
* Safe to call repeatedly (idempotent by id and by email-within-scope).
|
|
1706
|
+
*
|
|
1707
|
+
* For Postgres, call syncRuntimeUserToManagementDurable (or await store.ready())
|
|
1708
|
+
* so durability and constraint failures surface.
|
|
1709
|
+
*/
|
|
1710
|
+
declare function syncRuntimeUserToManagement(store: ManagementStore, runtimeUser: RuntimeUserIdentity, opts?: {
|
|
1711
|
+
projectId?: string;
|
|
1712
|
+
environmentId?: string;
|
|
1713
|
+
actor?: string;
|
|
1714
|
+
source?: "cli" | "console" | "api" | "system" | "scim";
|
|
1715
|
+
}): Principal;
|
|
1716
|
+
/**
|
|
1717
|
+
* Async variant that awaits durable commit (required for Postgres backend).
|
|
1718
|
+
* Does not swallow failures — rethrows after ready().
|
|
1719
|
+
*/
|
|
1720
|
+
declare function syncRuntimeUserToManagementDurable(store: ManagementStore, runtimeUser: RuntimeUserIdentity, opts?: {
|
|
1721
|
+
projectId?: string;
|
|
1722
|
+
environmentId?: string;
|
|
1723
|
+
actor?: string;
|
|
1724
|
+
source?: "cli" | "console" | "api" | "system" | "scim";
|
|
1725
|
+
}): Promise<Principal>;
|
|
1726
|
+
/**
|
|
1727
|
+
* Mirror a runtime organization and its owner membership into the canonical
|
|
1728
|
+
* scoped management view while preserving the runtime organization id.
|
|
1729
|
+
* When ownerMembershipId is provided, management uses that exact stable
|
|
1730
|
+
* membership id (create or reconcile). Organization, membership, and audit
|
|
1731
|
+
* records commit in one store mutation.
|
|
1732
|
+
*/
|
|
1733
|
+
declare function syncRuntimeOrganizationToManagementDurable(store: ManagementStore, runtimeOrganization: RuntimeOrganizationIdentity, ownerPrincipalId: string, opts?: {
|
|
1734
|
+
projectId?: string;
|
|
1735
|
+
environmentId?: string;
|
|
1736
|
+
actor?: string;
|
|
1737
|
+
role?: string;
|
|
1738
|
+
}): Promise<Organization>;
|
|
1739
|
+
//#endregion
|
|
1740
|
+
//#region src/services/doctor.d.ts
|
|
1741
|
+
declare function runDoctor(store: ManagementStore, opts?: {
|
|
1742
|
+
dataPath?: string;
|
|
1743
|
+
secrets?: Record<string, string | undefined>;
|
|
1744
|
+
}): Promise<{
|
|
1745
|
+
checks: DoctorCheck[];
|
|
1746
|
+
ok: boolean;
|
|
1747
|
+
releaseVersion: string;
|
|
1748
|
+
}>;
|
|
1749
|
+
//#endregion
|
|
1750
|
+
//#region src/services/sso.d.ts
|
|
1751
|
+
type SsoActorSource = "cli" | "console" | "api" | "system";
|
|
1752
|
+
interface SsoMutationOpts {
|
|
1753
|
+
actor?: string;
|
|
1754
|
+
source?: SsoActorSource;
|
|
1755
|
+
scope?: ResourceScope;
|
|
1756
|
+
}
|
|
1757
|
+
/**
|
|
1758
|
+
* Resolve an SSO connection under principal-derived org scope.
|
|
1759
|
+
* Missing and cross-scope ids fail closed as SSO_NOT_FOUND.
|
|
1760
|
+
*/
|
|
1761
|
+
declare function resolveSsoConnection(store: ManagementStore, id: string, opts?: {
|
|
1762
|
+
scope?: ResourceScope;
|
|
1763
|
+
stage?: string;
|
|
1764
|
+
}): IdentityConnection;
|
|
1765
|
+
/** Public inspect — never returns encrypted secret material. */
|
|
1766
|
+
declare function inspectSsoConnection(store: ManagementStore, id: string, opts?: {
|
|
1767
|
+
scope?: ResourceScope;
|
|
1768
|
+
}): IdentityConnection;
|
|
1769
|
+
interface SsoCreateInput {
|
|
1770
|
+
organizationId: string;
|
|
1771
|
+
protocol: "saml" | "oidc";
|
|
1772
|
+
provider: string;
|
|
1773
|
+
issuer?: string;
|
|
1774
|
+
audience?: string;
|
|
1775
|
+
metadataUrl?: string;
|
|
1776
|
+
clientId?: string;
|
|
1777
|
+
clientSecret?: string;
|
|
1778
|
+
samlEntryPoint?: string;
|
|
1779
|
+
samlCertificate?: string;
|
|
1780
|
+
domains?: string[];
|
|
1781
|
+
actor?: string;
|
|
1782
|
+
}
|
|
1783
|
+
declare function createSsoConnection(store: ManagementStore, input: SsoCreateInput): IdentityConnection;
|
|
1784
|
+
/**
|
|
1785
|
+
* Configure an SSO connection under principal-derived scope (FOLLOW.md P2.3.5).
|
|
1786
|
+
* Missing and cross-scope ids fail closed as SSO_NOT_FOUND with no write —
|
|
1787
|
+
* the same scope contract as rotateSsoCredential / disableSsoConnection.
|
|
1788
|
+
* Validation + write + audit run in ONE store.mutate against the draft
|
|
1789
|
+
* (core.ts atomic mutation pattern), so Postgres FOR UPDATE commits the
|
|
1790
|
+
* configure and its audit event together or not at all.
|
|
1791
|
+
*/
|
|
1792
|
+
declare function configureSsoConnection(store: ManagementStore, id: string, patch: Partial<Pick<IdentityConnection, "issuer" | "audience" | "metadataUrl" | "clientId" | "domains" | "attributeMapping" | "status">> & {
|
|
1793
|
+
clientSecret?: string;
|
|
1794
|
+
}, opts?: SsoMutationOpts): IdentityConnection;
|
|
1795
|
+
declare function listSsoConnections(store: ManagementStore, organizationId?: string): IdentityConnection[];
|
|
1796
|
+
/**
|
|
1797
|
+
* Rotate stored SSO client secret envelope under the current credential key.
|
|
1798
|
+
* Plaintext is preserved; only AEAD key envelope / fingerprint metadata change.
|
|
1799
|
+
* Never returns encrypted material — fingerprints only.
|
|
1800
|
+
*/
|
|
1801
|
+
declare function rotateSsoCredential(store: ManagementStore, id: string, opts?: SsoMutationOpts): IdentityConnection;
|
|
1802
|
+
/**
|
|
1803
|
+
* Disable an SSO connection (status=disabled). Idempotent when already disabled.
|
|
1804
|
+
* Management-only path; prefer disableSsoConnectionReal when DATABASE_URL is set
|
|
1805
|
+
* so runtime ssoProvider rows stay coherent.
|
|
1806
|
+
*/
|
|
1807
|
+
declare function disableSsoConnection(store: ManagementStore, id: string, opts?: SsoMutationOpts): {
|
|
1808
|
+
connection: IdentityConnection;
|
|
1809
|
+
idempotent: boolean;
|
|
1810
|
+
};
|
|
1811
|
+
/**
|
|
1812
|
+
* Disable SSO connection and remove the matching runtime ssoProvider row.
|
|
1813
|
+
* Uses mutateCoordinated when available (Postgres) so management + runtime
|
|
1814
|
+
* commit together; otherwise best-effort runtime delete then management disable.
|
|
1815
|
+
*/
|
|
1816
|
+
declare function disableSsoConnectionReal(store: ManagementStore, id: string, opts?: SsoMutationOpts): Promise<{
|
|
1817
|
+
connection: IdentityConnection;
|
|
1818
|
+
idempotent: boolean;
|
|
1819
|
+
runtimeRemoved: boolean;
|
|
1820
|
+
}>;
|
|
1821
|
+
interface SsoTestOptions {
|
|
1822
|
+
/**
|
|
1823
|
+
* Lab/fixture path — always simulation mode (not live IdP conformance).
|
|
1824
|
+
* Fail-closed: unknown fixtures throw rather than pass.
|
|
1825
|
+
*/
|
|
1826
|
+
fixture?: "ok" | "wrong-issuer" | "wrong-audience" | "malformed" | "expired" | "clock-skew" | "replay";
|
|
1827
|
+
assertionIssuer?: string;
|
|
1828
|
+
assertionAudience?: string;
|
|
1829
|
+
}
|
|
1830
|
+
/** All fixture-driven SSO tests are simulation (not live conformance). */
|
|
1831
|
+
declare const SSO_FIXTURE_MODE: "simulation";
|
|
1832
|
+
declare function testSsoConnection(store: ManagementStore, id: string, opts?: SsoTestOptions): {
|
|
1833
|
+
pass: boolean;
|
|
1834
|
+
trace: DiagnosticTrace;
|
|
1835
|
+
connection: IdentityConnection;
|
|
1836
|
+
mode: "simulation";
|
|
1837
|
+
};
|
|
1838
|
+
//#endregion
|
|
1839
|
+
//#region src/services/scim.d.ts
|
|
1840
|
+
type ScimActorSource = "cli" | "console" | "api" | "system";
|
|
1841
|
+
interface ScimMutationOpts {
|
|
1842
|
+
actor?: string;
|
|
1843
|
+
source?: ScimActorSource;
|
|
1844
|
+
scope?: ResourceScope;
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1847
|
+
* Resolve a SCIM directory connection under principal-derived org scope.
|
|
1848
|
+
* Missing and cross-scope ids fail closed as SCIM_NOT_FOUND.
|
|
1849
|
+
*/
|
|
1850
|
+
declare function resolveScimConnection(store: ManagementStore, id: string, opts?: {
|
|
1851
|
+
scope?: ResourceScope;
|
|
1852
|
+
stage?: string;
|
|
1853
|
+
}): DirectoryConnection;
|
|
1854
|
+
/** Public inspect — never returns encrypted bearer material. */
|
|
1855
|
+
declare function inspectScimConnection(store: ManagementStore, id: string, opts?: {
|
|
1856
|
+
scope?: ResourceScope;
|
|
1857
|
+
}): DirectoryConnection;
|
|
1858
|
+
declare function createScimConnection(store: ManagementStore, input: {
|
|
1859
|
+
organizationId: string;
|
|
1860
|
+
provider: string;
|
|
1861
|
+
endpoint?: string;
|
|
1862
|
+
bearerToken?: string;
|
|
1863
|
+
deprovisioningPolicy?: DirectoryConnection["deprovisioningPolicy"];
|
|
1864
|
+
actor?: string;
|
|
1865
|
+
}): DirectoryConnection;
|
|
1866
|
+
declare function listScimConnections(store: ManagementStore, organizationId?: string): DirectoryConnection[];
|
|
1867
|
+
/**
|
|
1868
|
+
* Rotate stored SCIM bearer envelope under the current credential key.
|
|
1869
|
+
* Plaintext token is preserved; only AEAD envelope / fingerprint metadata change.
|
|
1870
|
+
* Never returns encrypted material — fingerprints only.
|
|
1871
|
+
*/
|
|
1872
|
+
declare function rotateScimCredential(store: ManagementStore, id: string, opts?: ScimMutationOpts): DirectoryConnection;
|
|
1873
|
+
/**
|
|
1874
|
+
* Disable a SCIM directory connection (status=disabled). Idempotent when already disabled.
|
|
1875
|
+
* Management-only path; prefer disableScimConnectionReal when DATABASE_URL is set
|
|
1876
|
+
* so runtime scimProvider rows stay coherent.
|
|
1877
|
+
*/
|
|
1878
|
+
declare function disableScimConnection(store: ManagementStore, id: string, opts?: ScimMutationOpts): {
|
|
1879
|
+
connection: DirectoryConnection;
|
|
1880
|
+
idempotent: boolean;
|
|
1881
|
+
};
|
|
1882
|
+
/**
|
|
1883
|
+
* Disable SCIM connection and remove the matching runtime scimProvider row.
|
|
1884
|
+
* Coordinated when Postgres mutateCoordinated is available.
|
|
1885
|
+
*/
|
|
1886
|
+
declare function disableScimConnectionReal(store: ManagementStore, id: string, opts?: ScimMutationOpts): Promise<{
|
|
1887
|
+
connection: DirectoryConnection;
|
|
1888
|
+
idempotent: boolean;
|
|
1889
|
+
runtimeRemoved: boolean;
|
|
1890
|
+
}>;
|
|
1891
|
+
interface ScimUserPayload {
|
|
1892
|
+
userName: string;
|
|
1893
|
+
displayName?: string;
|
|
1894
|
+
active?: boolean;
|
|
1895
|
+
externalId?: string;
|
|
1896
|
+
}
|
|
1897
|
+
/** Fixture-driven SCIM apply path is simulation (not live directory conformance). */
|
|
1898
|
+
declare const SCIM_FIXTURE_MODE: "simulation";
|
|
1899
|
+
/** Local HTTP protocol probe evidence label — not external provider certification. */
|
|
1900
|
+
declare const SCIM_LOCAL_PROTOCOL_EVIDENCE: "local protocol verification (not external IdP/directory certification)";
|
|
1901
|
+
/**
|
|
1902
|
+
* Perform an actual SCIM HTTP connection check against the configured endpoint.
|
|
1903
|
+
* Network / auth / malformed body / non-success status all fail.
|
|
1904
|
+
*/
|
|
1905
|
+
declare function checkScimConnection(store: ManagementStore, id: string, opts?: {
|
|
1906
|
+
/** Override token (tests); otherwise decrypt stored envelope */bearerToken?: string;
|
|
1907
|
+
path?: string;
|
|
1908
|
+
fetchImpl?: typeof fetch;
|
|
1909
|
+
}): Promise<{
|
|
1910
|
+
pass: boolean;
|
|
1911
|
+
trace: DiagnosticTrace;
|
|
1912
|
+
connection: DirectoryConnection;
|
|
1913
|
+
mode: "simulation";
|
|
1914
|
+
evidence: typeof SCIM_LOCAL_PROTOCOL_EVIDENCE;
|
|
1915
|
+
externalProviderCertified: false;
|
|
1916
|
+
}>;
|
|
1917
|
+
declare function testScimConnection(store: ManagementStore, id: string, opts?: {
|
|
1918
|
+
dryRun?: boolean;
|
|
1919
|
+
users?: ScimUserPayload[];
|
|
1920
|
+
fixture?: "ok" | "malformed" | "unauthorized";
|
|
1921
|
+
}): {
|
|
1922
|
+
pass: boolean;
|
|
1923
|
+
trace: DiagnosticTrace;
|
|
1924
|
+
proposed: Array<{
|
|
1925
|
+
action: string;
|
|
1926
|
+
email: string;
|
|
1927
|
+
}>;
|
|
1928
|
+
connection: DirectoryConnection;
|
|
1929
|
+
mode: "simulation";
|
|
1930
|
+
};
|
|
1931
|
+
/**
|
|
1932
|
+
* Validate that a SCIM diagnostic trace is visible under principal scope.
|
|
1933
|
+
* Used by dry-run and by replay before mutation.
|
|
1934
|
+
*/
|
|
1935
|
+
declare function inspectScimTrace(store: ManagementStore, traceId: string, opts?: {
|
|
1936
|
+
scope?: ResourceScope;
|
|
1937
|
+
}): DiagnosticTrace;
|
|
1938
|
+
/**
|
|
1939
|
+
* Replay a SCIM diagnostic trace under principal-derived scope.
|
|
1940
|
+
* Writes a new trace row + audit; never mutates directory connections or tokens.
|
|
1941
|
+
*/
|
|
1942
|
+
declare function replayScimTrace(store: ManagementStore, traceId: string, opts?: ScimMutationOpts): DiagnosticTrace;
|
|
1943
|
+
//#endregion
|
|
1944
|
+
//#region src/services/scim-probe.d.ts
|
|
1945
|
+
type ScimProbeOutcome = {
|
|
1946
|
+
ok: true;
|
|
1947
|
+
status: number;
|
|
1948
|
+
path: string;
|
|
1949
|
+
bodySnippet: string;
|
|
1950
|
+
} | {
|
|
1951
|
+
ok: false;
|
|
1952
|
+
reason: "network" | "authentication" | "malformed_body" | "non_success";
|
|
1953
|
+
status?: number;
|
|
1954
|
+
message: string;
|
|
1955
|
+
path?: string;
|
|
1956
|
+
};
|
|
1957
|
+
type ScimProbeOptions = {
|
|
1958
|
+
endpoint: string;
|
|
1959
|
+
bearerToken?: string; /** Relative path under endpoint (default ServiceProviderConfig) */
|
|
1960
|
+
path?: string;
|
|
1961
|
+
timeoutMs?: number;
|
|
1962
|
+
fetchImpl?: typeof fetch;
|
|
1963
|
+
};
|
|
1964
|
+
/**
|
|
1965
|
+
* Issue a real SCIM GET against the connection endpoint.
|
|
1966
|
+
* Uses application/scim+json Accept and optional Bearer token.
|
|
1967
|
+
*/
|
|
1968
|
+
declare function probeScimEndpoint(opts: ScimProbeOptions): Promise<ScimProbeOutcome>;
|
|
1969
|
+
declare function probeOutcomeToError(outcome: Extract<ScimProbeOutcome, {
|
|
1970
|
+
ok: false;
|
|
1971
|
+
}>): ClearanceError;
|
|
1972
|
+
/**
|
|
1973
|
+
* Deterministic local SCIM HTTP fixture for tests.
|
|
1974
|
+
* Modes: ok | unauthorized | malformed | non_success
|
|
1975
|
+
*/
|
|
1976
|
+
declare function createLocalScimFixtureServer(mode?: "ok" | "unauthorized" | "malformed" | "non_success", expectedToken?: string): {
|
|
1977
|
+
server: ReturnType<typeof createServer>;
|
|
1978
|
+
listen: () => Promise<{
|
|
1979
|
+
baseUrl: string;
|
|
1980
|
+
port: number;
|
|
1981
|
+
}>;
|
|
1982
|
+
close: () => Promise<void>;
|
|
1983
|
+
requests: Array<{
|
|
1984
|
+
method?: string;
|
|
1985
|
+
url?: string;
|
|
1986
|
+
authorization?: string;
|
|
1987
|
+
}>;
|
|
1988
|
+
};
|
|
1989
|
+
//#endregion
|
|
1990
|
+
//#region src/services/sso-real.d.ts
|
|
1991
|
+
type SsoTestFixture = "ok" | "okta" | "entra" | "wrong-issuer" | "wrong-audience" | "malformed" | "expired" | "clock-skew" | "local-oidc";
|
|
1992
|
+
/** Fixture/matrix paths use simulation mode — not live IdP conformance. */
|
|
1993
|
+
declare const SSO_REAL_FIXTURE_MODE: "simulation";
|
|
1994
|
+
/** Matrix fixtures (okta/entra JSON) are never tenant certification. */
|
|
1995
|
+
declare const SSO_MATRIX_NOT_CERTIFIED: false;
|
|
1996
|
+
declare function validateSamlProviderConfig(input: {
|
|
1997
|
+
entryPoint?: string;
|
|
1998
|
+
certificate?: string;
|
|
1999
|
+
}): {
|
|
2000
|
+
entryPoint: string;
|
|
2001
|
+
certificate: string;
|
|
2002
|
+
fingerprint: string;
|
|
2003
|
+
};
|
|
2004
|
+
/**
|
|
2005
|
+
* Create SSO connection from a matrix fixture file (Okta/Entra) or explicit opts.
|
|
2006
|
+
* Persists into real ssoProvider table and validates discovery URL with upstream util.
|
|
2007
|
+
*
|
|
2008
|
+
* When `setupAttemptId` is set (customer setup reserve path), connection and
|
|
2009
|
+
* runtime provider ids are deterministic for crash-safe retry reconcile.
|
|
2010
|
+
* Without it, ids remain generated (CLI/operator path).
|
|
2011
|
+
*/
|
|
2012
|
+
declare function createSsoConnectionReal(store: ManagementStore, input: {
|
|
2013
|
+
organizationId: string;
|
|
2014
|
+
protocol?: "saml" | "oidc";
|
|
2015
|
+
provider?: string;
|
|
2016
|
+
issuer?: string;
|
|
2017
|
+
audience?: string;
|
|
2018
|
+
domain?: string;
|
|
2019
|
+
clientId?: string;
|
|
2020
|
+
clientSecret?: string;
|
|
2021
|
+
samlEntryPoint?: string;
|
|
2022
|
+
samlCertificate?: string; /** Load fixtures/sso/{matrix}-oidc.json — okta | entra */
|
|
2023
|
+
matrix?: "okta" | "entra";
|
|
2024
|
+
fixturePath?: string;
|
|
2025
|
+
actor?: string;
|
|
2026
|
+
/**
|
|
2027
|
+
* Setup reservation/attempt id. When set, derives stable runtime +
|
|
2028
|
+
* management ids and reuses them across retries after lease expiry.
|
|
2029
|
+
*/
|
|
2030
|
+
setupAttemptId?: string;
|
|
2031
|
+
}): Promise<IdentityConnection>;
|
|
2032
|
+
/**
|
|
2033
|
+
* Test SSO using real @clearance/sso validators.
|
|
2034
|
+
* Fixture names map to fixtures/sso/adversarial-cases.json or matrix discovery files.
|
|
2035
|
+
*/
|
|
2036
|
+
declare function testSsoConnectionReal(store: ManagementStore, id: string, opts?: {
|
|
2037
|
+
fixture?: SsoTestFixture;
|
|
2038
|
+
}): {
|
|
2039
|
+
pass: boolean;
|
|
2040
|
+
trace: DiagnosticTrace;
|
|
2041
|
+
connection: IdentityConnection;
|
|
2042
|
+
mode: "simulation";
|
|
2043
|
+
certifiedExternalTenant?: false;
|
|
2044
|
+
evidence?: string;
|
|
2045
|
+
} | Promise<{
|
|
2046
|
+
pass: boolean;
|
|
2047
|
+
trace: DiagnosticTrace;
|
|
2048
|
+
connection: IdentityConnection;
|
|
2049
|
+
mode: "simulation";
|
|
2050
|
+
certifiedExternalTenant?: false;
|
|
2051
|
+
evidence?: string;
|
|
2052
|
+
authorizationUrl?: string;
|
|
2053
|
+
}>;
|
|
2054
|
+
/** Run Okta + Entra positive matrix against a connection's registered issuer. */
|
|
2055
|
+
declare function runSsoMatrix(store: ManagementStore, connectionId: string): {
|
|
2056
|
+
okta: boolean;
|
|
2057
|
+
entra: boolean;
|
|
2058
|
+
};
|
|
2059
|
+
//#endregion
|
|
2060
|
+
//#region src/services/sso-local.d.ts
|
|
2061
|
+
declare const SSO_LOCAL_PROTOCOL_MODE: "simulation";
|
|
2062
|
+
declare const SSO_LOCAL_EVIDENCE_LABEL: "local protocol verification (not Okta/Entra tenant certification)";
|
|
2063
|
+
declare function generatePkcePair(): {
|
|
2064
|
+
codeVerifier: string;
|
|
2065
|
+
codeChallenge: string;
|
|
2066
|
+
codeChallengeMethod: "S256";
|
|
2067
|
+
};
|
|
2068
|
+
declare function buildAuthorizationUrl(input: {
|
|
2069
|
+
authorizationEndpoint: string;
|
|
2070
|
+
clientId: string;
|
|
2071
|
+
redirectUri: string;
|
|
2072
|
+
state: string;
|
|
2073
|
+
nonce: string;
|
|
2074
|
+
codeChallenge: string;
|
|
2075
|
+
codeChallengeMethod?: string;
|
|
2076
|
+
scope?: string;
|
|
2077
|
+
}): string;
|
|
2078
|
+
type LocalOidcSession = {
|
|
2079
|
+
state: string;
|
|
2080
|
+
nonce: string;
|
|
2081
|
+
codeVerifier: string;
|
|
2082
|
+
codeChallenge: string;
|
|
2083
|
+
clientId: string;
|
|
2084
|
+
redirectUri: string;
|
|
2085
|
+
issuedCode?: string;
|
|
2086
|
+
};
|
|
2087
|
+
/**
|
|
2088
|
+
* Deterministic local OIDC issuer fixture (discovery + authorize + token).
|
|
2089
|
+
* Validates state echo, PKCE S256, and returns id_token carrying nonce.
|
|
2090
|
+
*/
|
|
2091
|
+
declare function createLocalOidcIssuerFixture(opts?: {
|
|
2092
|
+
clientId?: string;
|
|
2093
|
+
clientSecret?: string;
|
|
2094
|
+
}): {
|
|
2095
|
+
listen: () => Promise<{
|
|
2096
|
+
issuer: string;
|
|
2097
|
+
authorizationEndpoint: string;
|
|
2098
|
+
tokenEndpoint: string;
|
|
2099
|
+
jwksUri: string;
|
|
2100
|
+
discovery: Record<string, unknown>;
|
|
2101
|
+
}>;
|
|
2102
|
+
close: () => Promise<void>;
|
|
2103
|
+
sessions: Map<string, LocalOidcSession>;
|
|
2104
|
+
registerSession: (session: LocalOidcSession) => void;
|
|
2105
|
+
};
|
|
2106
|
+
declare function decodeJwtPayload(idToken: string): Record<string, unknown>;
|
|
2107
|
+
/**
|
|
2108
|
+
* Exercise full local OIDC authorize→callback→token path with state/nonce/PKCE.
|
|
2109
|
+
* Labels evidence as local protocol verification; never Okta/Entra certification.
|
|
2110
|
+
*/
|
|
2111
|
+
declare function verifySsoOidcLocalProtocol(store: ManagementStore, connectionId: string, opts?: {
|
|
2112
|
+
issuer?: {
|
|
2113
|
+
authorizationEndpoint: string;
|
|
2114
|
+
tokenEndpoint: string;
|
|
2115
|
+
issuer: string;
|
|
2116
|
+
};
|
|
2117
|
+
clientId?: string;
|
|
2118
|
+
redirectUri?: string;
|
|
2119
|
+
fetchImpl?: typeof fetch;
|
|
2120
|
+
}): Promise<{
|
|
2121
|
+
pass: boolean;
|
|
2122
|
+
trace: DiagnosticTrace;
|
|
2123
|
+
connection: IdentityConnection;
|
|
2124
|
+
mode: "simulation";
|
|
2125
|
+
evidence: typeof SSO_LOCAL_EVIDENCE_LABEL;
|
|
2126
|
+
authorizationUrl: string;
|
|
2127
|
+
certifiedExternalTenant: false;
|
|
2128
|
+
}>;
|
|
2129
|
+
//#endregion
|
|
2130
|
+
//#region src/services/scim-real.d.ts
|
|
2131
|
+
declare const SCIM_REAL_FIXTURE_MODE: "simulation";
|
|
2132
|
+
/**
|
|
2133
|
+
* Create SCIM connection in runtime scimProvider + management store.
|
|
2134
|
+
*
|
|
2135
|
+
* When `setupAttemptId` is set (customer setup reserve path), connection and
|
|
2136
|
+
* runtime provider ids are deterministic for crash-safe retry reconcile.
|
|
2137
|
+
* SCIM bearer material stays encrypted at rest; plaintext is returned only as
|
|
2138
|
+
* `bearerTokenOnce` on the successful create/recovery response (not re-fetchable).
|
|
2139
|
+
*/
|
|
2140
|
+
declare function createScimConnectionReal(store: ManagementStore, input: {
|
|
2141
|
+
organizationId: string;
|
|
2142
|
+
provider: string;
|
|
2143
|
+
actor?: string;
|
|
2144
|
+
/**
|
|
2145
|
+
* External SCIM base URL. Defaults to the local setup endpoint;
|
|
2146
|
+
* a real tenant URL is required for live conformance probes.
|
|
2147
|
+
*/
|
|
2148
|
+
endpoint?: string;
|
|
2149
|
+
/**
|
|
2150
|
+
* Setup reservation/attempt id. When set, derives stable runtime +
|
|
2151
|
+
* management ids and reuses them across retries after lease expiry.
|
|
2152
|
+
*/
|
|
2153
|
+
setupAttemptId?: string;
|
|
2154
|
+
}): Promise<DirectoryConnection & {
|
|
2155
|
+
bearerTokenOnce?: string;
|
|
2156
|
+
}>;
|
|
2157
|
+
/**
|
|
2158
|
+
* Connection check against configured endpoint via real HTTP.
|
|
2159
|
+
* When endpoint is absolute, uses checkScimConnection.
|
|
2160
|
+
* Relative plugin paths are exercised via auth.handler ServiceProviderConfig GET.
|
|
2161
|
+
*/
|
|
2162
|
+
declare function testScimConnectionReal(store: ManagementStore, id: string, opts?: {
|
|
2163
|
+
dryRun?: boolean;
|
|
2164
|
+
fixture?: "ok" | "malformed" | "unauthorized";
|
|
2165
|
+
users?: Array<{
|
|
2166
|
+
userName: string;
|
|
2167
|
+
displayName?: string;
|
|
2168
|
+
active?: boolean;
|
|
2169
|
+
}>; /** Absolute URL override for local fixture protocol verification */
|
|
2170
|
+
endpointOverride?: string;
|
|
2171
|
+
bearerToken?: string;
|
|
2172
|
+
fetchImpl?: typeof fetch;
|
|
2173
|
+
}): Promise<{
|
|
2174
|
+
pass: boolean;
|
|
2175
|
+
trace: DiagnosticTrace;
|
|
2176
|
+
proposed: Array<{
|
|
2177
|
+
action: string;
|
|
2178
|
+
email: string;
|
|
2179
|
+
}>;
|
|
2180
|
+
connection: DirectoryConnection;
|
|
2181
|
+
mode: "simulation";
|
|
2182
|
+
evidence?: string;
|
|
2183
|
+
externalProviderCertified: false;
|
|
2184
|
+
}>;
|
|
2185
|
+
//#endregion
|
|
2186
|
+
//#region src/services/live-conformance.d.ts
|
|
2187
|
+
declare const LIVE_CONFORMANCE_MODE: "live";
|
|
2188
|
+
declare const LIVE_EVIDENCE_LABEL = "live read-only conformance probe against the configured external endpoint (not full sign-in certification)";
|
|
2189
|
+
declare function assertLiveEndpoint(rawUrl: string | undefined, subsystem: "sso" | "scim", stage: string): URL;
|
|
2190
|
+
interface LiveProbeResult<C> {
|
|
2191
|
+
pass: boolean;
|
|
2192
|
+
trace: DiagnosticTrace;
|
|
2193
|
+
connection: C;
|
|
2194
|
+
mode: typeof LIVE_CONFORMANCE_MODE;
|
|
2195
|
+
evidence: string;
|
|
2196
|
+
endpoint: string;
|
|
2197
|
+
}
|
|
2198
|
+
interface LiveProbeOptions {
|
|
2199
|
+
fetchImpl?: typeof fetch;
|
|
2200
|
+
timeoutMs?: number;
|
|
2201
|
+
}
|
|
2202
|
+
/**
|
|
2203
|
+
* Live OIDC conformance: fetch and validate the real discovery document and
|
|
2204
|
+
* JWKS from the configured issuer. SAML connections refuse — live SAML
|
|
2205
|
+
* metadata exchange needs a browser actor (rendered E2E), and pretending
|
|
2206
|
+
* otherwise is exactly what this codebase does not do.
|
|
2207
|
+
*/
|
|
2208
|
+
declare function testSsoConnectionLive(store: ManagementStore, id: string, opts?: LiveProbeOptions): Promise<LiveProbeResult<IdentityConnection>>;
|
|
2209
|
+
/**
|
|
2210
|
+
* Live SCIM conformance: real read-only GETs (ServiceProviderConfig, then a
|
|
2211
|
+
* one-item Users page) against the configured endpoint with the stored
|
|
2212
|
+
* bearer credential. Never mutates the tenant.
|
|
2213
|
+
*/
|
|
2214
|
+
declare function testScimConnectionLive(store: ManagementStore, id: string, opts?: LiveProbeOptions): Promise<LiveProbeResult<DirectoryConnection>>;
|
|
2215
|
+
//#endregion
|
|
2216
|
+
//#region src/services/setup-links.d.ts
|
|
2217
|
+
type SetupKind = "sso" | "scim";
|
|
2218
|
+
/** Bounded lease so a crashed holder does not permanently burn the capability. */
|
|
2219
|
+
declare const SETUP_RESERVATION_TTL_MS = 120000;
|
|
2220
|
+
/**
|
|
2221
|
+
* Stable reservation / setup-attempt id derived from the capability digest.
|
|
2222
|
+
* Same capability always re-leases the same attempt id after expiry so runtime
|
|
2223
|
+
* and management rows can be reconciled without exposing the raw token.
|
|
2224
|
+
*/
|
|
2225
|
+
declare function deriveSetupReservationId(capabilityDigest: string): string;
|
|
2226
|
+
/**
|
|
2227
|
+
* Deterministic runtime PK + unique providerId for a setup attempt.
|
|
2228
|
+
* Used only when completing a reserved capability; normal CLI/operator creates
|
|
2229
|
+
* keep generated ids. Material is a hash of kind+attempt — never the raw token.
|
|
2230
|
+
*/
|
|
2231
|
+
declare function deriveSetupConnectionIds(kind: SetupKind, setupAttemptId: string): {
|
|
2232
|
+
connectionId: string;
|
|
2233
|
+
providerId: string;
|
|
2234
|
+
};
|
|
2235
|
+
declare function createSetupLink(store: ManagementStore, input: {
|
|
2236
|
+
organizationId: string;
|
|
2237
|
+
kind: SetupKind;
|
|
2238
|
+
ttlMinutes?: number;
|
|
2239
|
+
actor?: string; /** Absolute console base; defaults to CLEARANCE_CONSOLE_URL */
|
|
2240
|
+
baseUrl?: string;
|
|
2241
|
+
}): {
|
|
2242
|
+
url: string;
|
|
2243
|
+
expiresAt: string; /** Raw capability — return once; never persisted */
|
|
2244
|
+
token: string;
|
|
2245
|
+
tokenFingerprint: string;
|
|
2246
|
+
capabilityId: string;
|
|
2247
|
+
};
|
|
2248
|
+
type RedeemSetupLinkInput = {
|
|
2249
|
+
token: string;
|
|
2250
|
+
kind: SetupKind; /** When set, must match capability */
|
|
2251
|
+
organizationId?: string;
|
|
2252
|
+
projectId?: string;
|
|
2253
|
+
environmentId?: string;
|
|
2254
|
+
actor?: string;
|
|
2255
|
+
};
|
|
2256
|
+
/**
|
|
2257
|
+
* Atomically consume a setup capability (single durable mutation).
|
|
2258
|
+
* Prefer reserve → provision → commit for flows with external side effects.
|
|
2259
|
+
*/
|
|
2260
|
+
declare function redeemSetupLink(store: ManagementStore, input: RedeemSetupLinkInput): Promise<SetupCapability>;
|
|
2261
|
+
type ReserveSetupLinkResult = {
|
|
2262
|
+
capability: SetupCapability;
|
|
2263
|
+
reservationId: string;
|
|
2264
|
+
};
|
|
2265
|
+
/**
|
|
2266
|
+
* Atomically lease a setup capability for in-flight provisioning.
|
|
2267
|
+
* Does not consume the capability; commit does. Concurrent reserves yield
|
|
2268
|
+
* one winner and SETUP_LINK_IN_PROGRESS / REPLAY for losers.
|
|
2269
|
+
*/
|
|
2270
|
+
declare function reserveSetupLink(store: ManagementStore, input: RedeemSetupLinkInput & {
|
|
2271
|
+
reservationTtlMs?: number;
|
|
2272
|
+
}): Promise<ReserveSetupLinkResult>;
|
|
2273
|
+
type CommitSetupLinkInput = RedeemSetupLinkInput & {
|
|
2274
|
+
reservationId: string;
|
|
2275
|
+
};
|
|
2276
|
+
/**
|
|
2277
|
+
* Atomically consume a previously reserved capability. Replay after commit fails.
|
|
2278
|
+
* Never reopens a redeemed capability.
|
|
2279
|
+
*/
|
|
2280
|
+
declare function commitSetupLink(store: ManagementStore, input: CommitSetupLinkInput): Promise<SetupCapability>;
|
|
2281
|
+
type ReleaseSetupLinkInput = {
|
|
2282
|
+
token: string;
|
|
2283
|
+
kind: SetupKind;
|
|
2284
|
+
reservationId: string;
|
|
2285
|
+
actor?: string;
|
|
2286
|
+
};
|
|
2287
|
+
/**
|
|
2288
|
+
* Drop an in-progress reservation after failed provisioning.
|
|
2289
|
+
* Never un-consumes a committed (redeemed) capability.
|
|
2290
|
+
*/
|
|
2291
|
+
declare function releaseSetupLink(store: ManagementStore, input: ReleaseSetupLinkInput): Promise<SetupCapability | null>;
|
|
2292
|
+
declare function revokeSetupLink(store: ManagementStore, input: {
|
|
2293
|
+
capabilityId?: string;
|
|
2294
|
+
token?: string;
|
|
2295
|
+
actor?: string;
|
|
2296
|
+
}): SetupCapability;
|
|
2297
|
+
declare function listSetupLinks(store: ManagementStore, organizationId?: string): Omit<SetupCapability, "digest">[];
|
|
2298
|
+
//#endregion
|
|
2299
|
+
//#region src/services/readiness.d.ts
|
|
2300
|
+
/**
|
|
2301
|
+
* Enterprise readiness from control-plane state.
|
|
2302
|
+
* Fixture/synthetic SSO+SCIM tests are labeled simulation and never set liveCertified.
|
|
2303
|
+
*/
|
|
2304
|
+
declare function runReadinessCheck(store: ManagementStore, organizationId: string): ReadinessReport;
|
|
2305
|
+
declare function getLatestReadiness(store: ManagementStore, organizationId: string): ReadinessReport;
|
|
2306
|
+
//#endregion
|
|
2307
|
+
//#region src/services/migration.d.ts
|
|
2308
|
+
/** Deliberately small, portable Clearance export contract accepted by Clearance. */
|
|
2309
|
+
interface LegacyExportFixture {
|
|
2310
|
+
source: "legacy";
|
|
2311
|
+
users: Array<{
|
|
2312
|
+
id: string;
|
|
2313
|
+
email: string;
|
|
2314
|
+
name: string;
|
|
2315
|
+
}>;
|
|
2316
|
+
organizations: Array<{
|
|
2317
|
+
id: string;
|
|
2318
|
+
name: string;
|
|
2319
|
+
slug?: string;
|
|
2320
|
+
}>;
|
|
2321
|
+
members: Array<{
|
|
2322
|
+
userId: string;
|
|
2323
|
+
organizationId: string;
|
|
2324
|
+
role?: string;
|
|
2325
|
+
}>;
|
|
2326
|
+
}
|
|
2327
|
+
type MigrationPreview = {
|
|
2328
|
+
source: "legacy";
|
|
2329
|
+
fixtureChecksum: string;
|
|
2330
|
+
counts: {
|
|
2331
|
+
users: number;
|
|
2332
|
+
organizations: number;
|
|
2333
|
+
members: number;
|
|
2334
|
+
};
|
|
2335
|
+
wouldCreate: {
|
|
2336
|
+
users: number;
|
|
2337
|
+
organizations: number;
|
|
2338
|
+
members: number;
|
|
2339
|
+
};
|
|
2340
|
+
idempotent: {
|
|
2341
|
+
users: number;
|
|
2342
|
+
organizations: number;
|
|
2343
|
+
members: number;
|
|
2344
|
+
};
|
|
2345
|
+
};
|
|
2346
|
+
/** Reads and fully validates the one Legacy fixture shape supported by this lane. */
|
|
2347
|
+
declare function loadLegacyFixture(path: string): LegacyExportFixture;
|
|
2348
|
+
/** Validate conflicts and calculate a non-mutating Clearance import preview. */
|
|
2349
|
+
declare function previewMigration(store: ManagementStore, fixture: LegacyExportFixture): MigrationPreview;
|
|
2350
|
+
declare function planMigration(store: ManagementStore, fixture: LegacyExportFixture): MigrationPlan;
|
|
2351
|
+
declare function assertMigrationRunnable(plan: MigrationPlan, stage: string): void;
|
|
2352
|
+
declare function runMigration(store: ManagementStore, planId: string, fixture: LegacyExportFixture, opts?: {
|
|
2353
|
+
dryRun?: boolean;
|
|
2354
|
+
}): MigrationPlan;
|
|
2355
|
+
declare function verifyMigration(store: ManagementStore, planId: string, fixture: LegacyExportFixture): {
|
|
2356
|
+
plan: MigrationPlan;
|
|
2357
|
+
reconciled: boolean;
|
|
2358
|
+
actual: Record<string, number>;
|
|
2359
|
+
expected: Record<string, number>;
|
|
2360
|
+
};
|
|
2361
|
+
declare function rollbackMigration(store: ManagementStore, planId: string, fixture: LegacyExportFixture): MigrationPlan;
|
|
2362
|
+
declare function migrationStatus(store: ManagementStore, planId: string): MigrationPlan;
|
|
2363
|
+
//#endregion
|
|
2364
|
+
//#region src/services/migration-postgres.d.ts
|
|
2365
|
+
declare function runMigrationDurable(store: ManagementStore, planId: string, fixture: LegacyExportFixture, opts?: {
|
|
2366
|
+
dryRun?: boolean;
|
|
2367
|
+
}): Promise<MigrationPlan>;
|
|
2368
|
+
declare function verifyMigrationDurable(store: ManagementStore, planId: string, fixture: LegacyExportFixture): Promise<{
|
|
2369
|
+
plan: MigrationPlan;
|
|
2370
|
+
reconciled: boolean;
|
|
2371
|
+
actual: Record<string, number>;
|
|
2372
|
+
expected: Record<string, number>;
|
|
2373
|
+
} | {
|
|
2374
|
+
plan: MigrationPlan;
|
|
2375
|
+
reconciled: boolean;
|
|
2376
|
+
actual: {
|
|
2377
|
+
users: number;
|
|
2378
|
+
organizations: number;
|
|
2379
|
+
members: number;
|
|
2380
|
+
};
|
|
2381
|
+
expected: {
|
|
2382
|
+
users: number;
|
|
2383
|
+
organizations: number;
|
|
2384
|
+
members: number;
|
|
2385
|
+
};
|
|
2386
|
+
}>;
|
|
2387
|
+
declare function rollbackMigrationDurable(store: ManagementStore, planId: string, fixture: LegacyExportFixture): Promise<MigrationPlan>;
|
|
2388
|
+
//#endregion
|
|
2389
|
+
//#region src/services/runtime-schema.d.ts
|
|
2390
|
+
type RuntimeSchemaPlanResult = {
|
|
2391
|
+
pendingTables: number;
|
|
2392
|
+
pendingFields: number;
|
|
2393
|
+
sql: string;
|
|
2394
|
+
};
|
|
2395
|
+
type RuntimeSchemaMetadata = Omit<RuntimeSchemaPlanResult, "sql">;
|
|
2396
|
+
/** Canonical Clearance migration plan used by status, generate, and migrate. */
|
|
2397
|
+
declare function planRuntimeSchema(stage?: string): Promise<RuntimeSchemaPlanResult>;
|
|
2398
|
+
declare function getRuntimeSchemaStatus(): Promise<{
|
|
2399
|
+
configured: false;
|
|
2400
|
+
state: "unconfigured";
|
|
2401
|
+
pendingTables: 0;
|
|
2402
|
+
pendingFields: 0;
|
|
2403
|
+
} | ({
|
|
2404
|
+
configured: true;
|
|
2405
|
+
state: "configured";
|
|
2406
|
+
} & RuntimeSchemaMetadata)>;
|
|
2407
|
+
declare function generateRuntimeSchema(input: {
|
|
2408
|
+
outputPath: string;
|
|
2409
|
+
force: boolean;
|
|
2410
|
+
dryRun: boolean;
|
|
2411
|
+
}): Promise<{
|
|
2412
|
+
outputId: string;
|
|
2413
|
+
pendingTables: number;
|
|
2414
|
+
pendingFields: number;
|
|
2415
|
+
kind: string;
|
|
2416
|
+
dryRun: boolean;
|
|
2417
|
+
}>;
|
|
2418
|
+
declare function migrateRuntimeSchema(input: {
|
|
2419
|
+
dryRun: boolean;
|
|
2420
|
+
}): Promise<{
|
|
2421
|
+
kind: string;
|
|
2422
|
+
dryRun: boolean;
|
|
2423
|
+
pendingTables: number;
|
|
2424
|
+
pendingFields: number;
|
|
2425
|
+
} | {
|
|
2426
|
+
appliedTables: number;
|
|
2427
|
+
appliedFields: number;
|
|
2428
|
+
kind: string;
|
|
2429
|
+
dryRun: boolean;
|
|
2430
|
+
pendingTables?: undefined;
|
|
2431
|
+
pendingFields?: undefined;
|
|
2432
|
+
}>;
|
|
2433
|
+
//#endregion
|
|
2434
|
+
//#region src/services/backup.d.ts
|
|
2435
|
+
declare function createBackup(store: ManagementStore, backupDir?: string): BackupRecord;
|
|
2436
|
+
declare function verifyBackup(store: ManagementStore, backupId: string): BackupRecord;
|
|
2437
|
+
/**
|
|
2438
|
+
* Restore backup into an isolated target store path (does not clobber source unless same path).
|
|
2439
|
+
*/
|
|
2440
|
+
declare function restoreBackup(store: ManagementStore, backupId: string, targetPath: string): {
|
|
2441
|
+
targetPath: string;
|
|
2442
|
+
counts: Record<string, number>;
|
|
2443
|
+
checksum: string;
|
|
2444
|
+
};
|
|
2445
|
+
declare function upgradeCheck(store: ManagementStore): {
|
|
2446
|
+
current: string;
|
|
2447
|
+
latest: string;
|
|
2448
|
+
runtimeBaseline: string;
|
|
2449
|
+
action: "none" | "upgrade_available" | "plan_required";
|
|
2450
|
+
notes: string[];
|
|
2451
|
+
};
|
|
2452
|
+
declare function emptyIsolatedStorePath(path: string): void;
|
|
2453
|
+
//#endregion
|
|
2454
|
+
//#region src/services/backup-pg.d.ts
|
|
2455
|
+
type PostgresContainerSettings = {
|
|
2456
|
+
container: string;
|
|
2457
|
+
user: string;
|
|
2458
|
+
database: string;
|
|
2459
|
+
};
|
|
2460
|
+
type PostgresRestoreOptions = {
|
|
2461
|
+
retain?: boolean;
|
|
2462
|
+
};
|
|
2463
|
+
/** Settings for the Docker fallback used by the local Compose stack. */
|
|
2464
|
+
declare function postgresContainerSettings(env?: NodeJS.ProcessEnv): PostgresContainerSettings;
|
|
2465
|
+
/** Make backup material inaccessible to other local users, including existing directories. */
|
|
2466
|
+
declare function ensurePrivateBackupDirectory(path: string): void;
|
|
2467
|
+
/** Dumps and sidecar metadata are sensitive operational artifacts. */
|
|
2468
|
+
declare function secureBackupArtifact(path: string): void;
|
|
2469
|
+
declare function validateIsolatedRestoreDatabaseName(candidate: string, activeDatabase?: string): string;
|
|
2470
|
+
declare function dockerDumpArgs(settings?: PostgresContainerSettings): string[];
|
|
2471
|
+
declare function dockerPsqlArgs(settings: PostgresContainerSettings, database: string, args: string[]): string[];
|
|
2472
|
+
declare function postgresBackupFailure(stage: "backup.create" | "backup.restore"): ClearanceError;
|
|
2473
|
+
declare function createPostgresBackup(store: ManagementStore, backupDir?: string): BackupRecord;
|
|
2474
|
+
/**
|
|
2475
|
+
* Durable by construction (P2.2): validation + verified-flag write + audit
|
|
2476
|
+
* commit in one awaited mutation, so a failed Postgres write rejects here
|
|
2477
|
+
* instead of surfacing later (or never) via ready(). The CLI `backup verify`
|
|
2478
|
+
* previously printed success while the queued write could still fail.
|
|
2479
|
+
*/
|
|
2480
|
+
declare function verifyPostgresBackup(store: ManagementStore, backupId: string): Promise<BackupRecord>;
|
|
2481
|
+
/**
|
|
2482
|
+
* Restore dump into an isolated database name (creates DB if possible).
|
|
2483
|
+
*/
|
|
2484
|
+
declare function restorePostgresBackup(store: ManagementStore, backupId: string, isolatedDbName?: string, options?: PostgresRestoreOptions): Promise<{
|
|
2485
|
+
database: string;
|
|
2486
|
+
checksum: string;
|
|
2487
|
+
verified: true;
|
|
2488
|
+
retained: boolean;
|
|
2489
|
+
}>;
|
|
2490
|
+
declare function upgradeCheckWithDb(store: ManagementStore): Promise<{
|
|
2491
|
+
current: string;
|
|
2492
|
+
latest: string;
|
|
2493
|
+
runtimeBaseline: string;
|
|
2494
|
+
action: "none";
|
|
2495
|
+
notes: string[];
|
|
2496
|
+
authTableCounts: Record<string, number>;
|
|
2497
|
+
}>;
|
|
2498
|
+
//#endregion
|
|
2499
|
+
//#region src/services/fixtures.d.ts
|
|
2500
|
+
declare function fixturesRoot(): string;
|
|
2501
|
+
declare function loadJsonFixture<T = unknown>(relativePath: string): T;
|
|
2502
|
+
type SsoOidcFixture = {
|
|
2503
|
+
matrix: string;
|
|
2504
|
+
provider: string;
|
|
2505
|
+
protocol: "oidc" | "saml";
|
|
2506
|
+
issuer: string;
|
|
2507
|
+
audience?: string;
|
|
2508
|
+
domain?: string;
|
|
2509
|
+
clientId?: string;
|
|
2510
|
+
clientSecret?: string;
|
|
2511
|
+
discovery: {
|
|
2512
|
+
issuer: string;
|
|
2513
|
+
authorization_endpoint: string;
|
|
2514
|
+
token_endpoint: string;
|
|
2515
|
+
jwks_uri: string;
|
|
2516
|
+
[k: string]: unknown;
|
|
2517
|
+
};
|
|
2518
|
+
};
|
|
2519
|
+
type ScimUsersFixture = {
|
|
2520
|
+
provider: string;
|
|
2521
|
+
users: Array<{
|
|
2522
|
+
schemas: string[];
|
|
2523
|
+
userName: string;
|
|
2524
|
+
name?: {
|
|
2525
|
+
formatted?: string;
|
|
2526
|
+
};
|
|
2527
|
+
emails?: Array<{
|
|
2528
|
+
value: string;
|
|
2529
|
+
primary?: boolean;
|
|
2530
|
+
}>;
|
|
2531
|
+
active?: boolean;
|
|
2532
|
+
}>;
|
|
2533
|
+
};
|
|
2534
|
+
type AdversarialFixtureFile = {
|
|
2535
|
+
cases: Array<{
|
|
2536
|
+
id: string;
|
|
2537
|
+
stage: string;
|
|
2538
|
+
discovery?: Record<string, unknown>;
|
|
2539
|
+
configuredIssuer?: string;
|
|
2540
|
+
saml?: {
|
|
2541
|
+
notBefore?: string;
|
|
2542
|
+
notOnOrAfter?: string;
|
|
2543
|
+
};
|
|
2544
|
+
audience?: string;
|
|
2545
|
+
configuredAudience?: string;
|
|
2546
|
+
}>;
|
|
2547
|
+
};
|
|
2548
|
+
//#endregion
|
|
2549
|
+
//#region src/contracts/surfaces.d.ts
|
|
2550
|
+
/**
|
|
2551
|
+
* Shared CLI ↔ API ↔ Console surface registry.
|
|
2552
|
+
* Every management surface that reaches GA must appear here with all three contracts.
|
|
2553
|
+
*/
|
|
2554
|
+
interface ManagementSurface {
|
|
2555
|
+
id: string;
|
|
2556
|
+
cliCommand: string;
|
|
2557
|
+
apiPath: string;
|
|
2558
|
+
/** Route key in packages/clearance-console/public/app.js `routes` */
|
|
2559
|
+
consoleRoute: string;
|
|
2560
|
+
}
|
|
2561
|
+
declare const MANAGEMENT_SURFACES: ManagementSurface[];
|
|
2562
|
+
declare function consoleRoutesFromContract(): string[];
|
|
2563
|
+
//#endregion
|
|
2564
|
+
//#region src/auth-bridge.d.ts
|
|
2565
|
+
declare function getAuthBundle(): ClearanceAuthBundle;
|
|
2566
|
+
/** Test helper — reset singleton between suites */
|
|
2567
|
+
declare function resetAuthBundle(): void;
|
|
2568
|
+
/** Release the shared runtime pool for short-lived callers such as the CLI. */
|
|
2569
|
+
declare function closeAuthBundle(): Promise<void>;
|
|
2570
|
+
declare function ensureAuthMigrated(): Promise<void>;
|
|
2571
|
+
declare function listUsersFromDb(): Promise<Principal[]>;
|
|
2572
|
+
declare function createUserInAuth(input: {
|
|
2573
|
+
email: string;
|
|
2574
|
+
name: string;
|
|
2575
|
+
password?: string; /** When set, persists the runtime user into management with the same stable id */
|
|
2576
|
+
managementStore?: ManagementStore;
|
|
2577
|
+
}): Promise<Principal>;
|
|
2578
|
+
/**
|
|
2579
|
+
* Map an already-created runtime user into management (same stable id + scope).
|
|
2580
|
+
* Prefer this after product signup so CLI/API/console see one identity.
|
|
2581
|
+
*/
|
|
2582
|
+
declare function bridgeRuntimeUserToManagement(store: ManagementStore, runtimeUser: {
|
|
2583
|
+
id: string;
|
|
2584
|
+
email: string;
|
|
2585
|
+
name: string;
|
|
2586
|
+
createdAt?: string | Date;
|
|
2587
|
+
updatedAt?: string | Date;
|
|
2588
|
+
}, opts?: {
|
|
2589
|
+
projectId?: string;
|
|
2590
|
+
environmentId?: string;
|
|
2591
|
+
}): Promise<Principal>;
|
|
2592
|
+
declare function listOrgsFromDb(): Promise<Organization[]>;
|
|
2593
|
+
/**
|
|
2594
|
+
* Create a runtime organization (and optional owner member). When userId is set,
|
|
2595
|
+
* returns the exact Clearance owner membership id so management sync can keep
|
|
2596
|
+
* the same stable membership id.
|
|
2597
|
+
*/
|
|
2598
|
+
declare function createOrgInAuth(input: {
|
|
2599
|
+
name: string;
|
|
2600
|
+
slug?: string;
|
|
2601
|
+
userId?: string;
|
|
2602
|
+
}): Promise<Organization & {
|
|
2603
|
+
ownerMembershipId?: string;
|
|
2604
|
+
}>;
|
|
2605
|
+
declare function countAuthTables(): Promise<Record<string, number>>;
|
|
2606
|
+
/** Ensure a system operator user exists for FK-backed SSO provider rows. */
|
|
2607
|
+
declare function ensureOperatorUser(): Promise<string>;
|
|
2608
|
+
/**
|
|
2609
|
+
* Insert SSO provider, or reconcile an existing deterministic row.
|
|
2610
|
+
* When `id` is provided (setup attempt path), retries read/reuse the same PK
|
|
2611
|
+
* and fail closed on organization/provider/protocol/domain mismatch.
|
|
2612
|
+
* Without `id`, generates a new id (CLI/operator path).
|
|
2613
|
+
*/
|
|
2614
|
+
declare function insertSsoProvider(input: {
|
|
2615
|
+
/** Deterministic runtime/management id for setup-attempt recovery */id?: string;
|
|
2616
|
+
providerId: string;
|
|
2617
|
+
issuer: string;
|
|
2618
|
+
domain: string;
|
|
2619
|
+
organizationId?: string;
|
|
2620
|
+
protocol: "saml" | "oidc";
|
|
2621
|
+
oidc?: {
|
|
2622
|
+
clientId: string;
|
|
2623
|
+
clientSecret: string;
|
|
2624
|
+
};
|
|
2625
|
+
saml?: {
|
|
2626
|
+
entryPoint: string;
|
|
2627
|
+
cert: string;
|
|
2628
|
+
audience?: string;
|
|
2629
|
+
};
|
|
2630
|
+
}): Promise<{
|
|
2631
|
+
id: string;
|
|
2632
|
+
clientSecretFingerprint?: string;
|
|
2633
|
+
reused?: boolean;
|
|
2634
|
+
}>;
|
|
2635
|
+
declare function deleteSsoProviderById(id: string): Promise<void>;
|
|
2636
|
+
/**
|
|
2637
|
+
* Insert SCIM provider, or reconcile an existing deterministic row.
|
|
2638
|
+
* On reuse, reconstructs the bearer handoff in-memory from encrypted stored
|
|
2639
|
+
* material only (never persists plaintext). Without `id`, generates new ids.
|
|
2640
|
+
*/
|
|
2641
|
+
declare function insertScimProvider(input: {
|
|
2642
|
+
/** Deterministic runtime/management id for setup-attempt recovery */id?: string;
|
|
2643
|
+
providerId: string;
|
|
2644
|
+
organizationId?: string;
|
|
2645
|
+
token?: string;
|
|
2646
|
+
}): Promise<{
|
|
2647
|
+
id: string;
|
|
2648
|
+
token: string;
|
|
2649
|
+
reused?: boolean;
|
|
2650
|
+
}>;
|
|
2651
|
+
declare function deleteScimProviderById(id: string): Promise<void>;
|
|
2652
|
+
/**
|
|
2653
|
+
* Low-level runtime session rows. Never selects token / credential columns.
|
|
2654
|
+
* Prefer listSessionsInAuth for scoped operator listing.
|
|
2655
|
+
*/
|
|
2656
|
+
declare function listSessionsFromDb(): Promise<Array<{
|
|
2657
|
+
id: string;
|
|
2658
|
+
userId: string;
|
|
2659
|
+
createdAt: string;
|
|
2660
|
+
expiresAt?: string;
|
|
2661
|
+
ipAddress?: string;
|
|
2662
|
+
userAgent?: string;
|
|
2663
|
+
}>>;
|
|
2664
|
+
/**
|
|
2665
|
+
* List active runtime sessions under principal-derived scope.
|
|
2666
|
+
* Only returns sessions whose userId maps to an in-scope management principal.
|
|
2667
|
+
* Never exposes token material.
|
|
2668
|
+
*/
|
|
2669
|
+
declare function listSessionsInAuth(store: ManagementStore, opts?: {
|
|
2670
|
+
scope?: ResourceScope;
|
|
2671
|
+
limit?: number;
|
|
2672
|
+
}): Promise<SessionView[]>;
|
|
2673
|
+
/**
|
|
2674
|
+
* Cursor-paginated runtime sessions (FOLLOW.md P2.3.1), CLI/API parity with
|
|
2675
|
+
* the JSON-store listSessionsPage. Ordering: "createdAt" descending, then id
|
|
2676
|
+
* descending — the same documented keyset. The cursor carries the row's
|
|
2677
|
+
* full-precision createdAt text (Postgres microseconds), so the keyset row
|
|
2678
|
+
* comparison cannot skip same-millisecond sessions.
|
|
2679
|
+
*/
|
|
2680
|
+
declare function listSessionsPageInAuth(store: ManagementStore, opts?: {
|
|
2681
|
+
scope?: ResourceScope;
|
|
2682
|
+
limit?: number; /** Opaque cursor from a previous page's nextCursor (fail-closed). */
|
|
2683
|
+
cursor?: string;
|
|
2684
|
+
}): Promise<{
|
|
2685
|
+
sessions: SessionView[];
|
|
2686
|
+
nextCursor: string | null;
|
|
2687
|
+
}>;
|
|
2688
|
+
/** Load one safe runtime or revoked-tombstone session view without mutation. */
|
|
2689
|
+
declare function inspectSessionInAuth(store: ManagementStore, id: string, opts?: {
|
|
2690
|
+
scope?: ResourceScope;
|
|
2691
|
+
}): Promise<SessionView>;
|
|
2692
|
+
/**
|
|
2693
|
+
* Revoke one runtime session by stable id, coordinated with management tombstone + audit.
|
|
2694
|
+
*
|
|
2695
|
+
* Idempotent under authorized contract:
|
|
2696
|
+
* - Active runtime row → delete + management revoked tombstone + audit
|
|
2697
|
+
* - Already revoked management tombstone (no runtime row) → success, idempotent=true
|
|
2698
|
+
* - Missing / cross-scope → SESSION_NOT_FOUND (fail closed)
|
|
2699
|
+
*/
|
|
2700
|
+
declare function revokeSessionInAuth(store: ManagementStore, id: string, input?: {
|
|
2701
|
+
actor?: string;
|
|
2702
|
+
source?: SessionSource;
|
|
2703
|
+
scope?: ResourceScope;
|
|
2704
|
+
}): Promise<RevokeSessionResult>;
|
|
2705
|
+
type LifecycleSource = "cli" | "console" | "api" | "import" | "scim" | "system";
|
|
2706
|
+
/**
|
|
2707
|
+
* Update name/email/status with runtime tables first-class in the same TX as
|
|
2708
|
+
* the management principal snapshot (identical stable id).
|
|
2709
|
+
*/
|
|
2710
|
+
declare function updateUserInAuth(store: ManagementStore, id: string, input: {
|
|
2711
|
+
name?: string;
|
|
2712
|
+
email?: string;
|
|
2713
|
+
status?: "active" | "disabled" | string;
|
|
2714
|
+
actor?: string;
|
|
2715
|
+
source?: LifecycleSource;
|
|
2716
|
+
scope?: ResourceScope;
|
|
2717
|
+
}): Promise<Principal>;
|
|
2718
|
+
/**
|
|
2719
|
+
* Disable principal: ban runtime user, revoke all runtime + management sessions,
|
|
2720
|
+
* set management status=disabled, single success audit — all one transaction.
|
|
2721
|
+
*/
|
|
2722
|
+
declare function disableUserInAuth(store: ManagementStore, id: string, input?: {
|
|
2723
|
+
actor?: string;
|
|
2724
|
+
source?: LifecycleSource;
|
|
2725
|
+
scope?: ResourceScope;
|
|
2726
|
+
}): Promise<Principal>;
|
|
2727
|
+
/**
|
|
2728
|
+
* Soft-delete management principal and make runtime auth impossible:
|
|
2729
|
+
* revoke sessions, strip credential accounts, ban + anonymize runtime email
|
|
2730
|
+
* (preserve original email only on the management audit snapshot).
|
|
2731
|
+
*/
|
|
2732
|
+
declare function deleteUserInAuth(store: ManagementStore, id: string, input?: {
|
|
2733
|
+
actor?: string;
|
|
2734
|
+
source?: LifecycleSource;
|
|
2735
|
+
scope?: ResourceScope;
|
|
2736
|
+
}): Promise<Principal>;
|
|
2737
|
+
/**
|
|
2738
|
+
* Add membership with runtime member table + management snapshot + one audit
|
|
2739
|
+
* in a single coordinated transaction. Preserves runtime membership ids when
|
|
2740
|
+
* a matching Clearance row exists. Idempotent for active duplicates.
|
|
2741
|
+
*/
|
|
2742
|
+
declare function addMemberInAuth(store: ManagementStore, input: {
|
|
2743
|
+
organizationId: string;
|
|
2744
|
+
principalId: string;
|
|
2745
|
+
role?: string;
|
|
2746
|
+
source?: MembershipSource;
|
|
2747
|
+
actor?: string;
|
|
2748
|
+
auditSource?: MembershipActorSource;
|
|
2749
|
+
scope?: ResourceScope;
|
|
2750
|
+
}): Promise<Membership>;
|
|
2751
|
+
/**
|
|
2752
|
+
* Update membership role with runtime + management + single audit in one TX.
|
|
2753
|
+
* Missing/divergent pairs fail closed after deterministic reconcile attempts.
|
|
2754
|
+
*/
|
|
2755
|
+
declare function updateMemberInAuth(store: ManagementStore, id: string, input: {
|
|
2756
|
+
role: string;
|
|
2757
|
+
actor?: string;
|
|
2758
|
+
auditSource?: MembershipActorSource;
|
|
2759
|
+
scope?: ResourceScope;
|
|
2760
|
+
}): Promise<Membership>;
|
|
2761
|
+
/**
|
|
2762
|
+
* Remove membership: soft-remove management + hard-delete runtime member +
|
|
2763
|
+
* one success audit in a single coordinated transaction.
|
|
2764
|
+
*/
|
|
2765
|
+
declare function removeMemberInAuth(store: ManagementStore, id: string, input?: {
|
|
2766
|
+
actor?: string;
|
|
2767
|
+
auditSource?: MembershipActorSource;
|
|
2768
|
+
scope?: ResourceScope;
|
|
2769
|
+
}): Promise<Membership>;
|
|
2770
|
+
type OrgLifecycleSource = "cli" | "console" | "api" | "import" | "system";
|
|
2771
|
+
/**
|
|
2772
|
+
* Update organization name and/or slug with identical values in Clearance
|
|
2773
|
+
* runtime and management snapshot (stable organization id) in one transaction.
|
|
2774
|
+
*
|
|
2775
|
+
* Management values are authoritative for the intended final state. A true
|
|
2776
|
+
* idempotent no-op is allowed only when both runtime and management already
|
|
2777
|
+
* match those final values (returns current org, no audit). If runtime is
|
|
2778
|
+
* divergent, reconcile it to the management-authoritative finals in the same
|
|
2779
|
+
* coordinated transaction and emit exactly one update/reconcile audit.
|
|
2780
|
+
* Missing runtime org, cross-scope id, invalid slug, or slug conflict fails
|
|
2781
|
+
* closed with zero writes and no success audit.
|
|
2782
|
+
*/
|
|
2783
|
+
declare function updateOrganizationInAuth(store: ManagementStore, id: string, input: {
|
|
2784
|
+
name?: string;
|
|
2785
|
+
slug?: string;
|
|
2786
|
+
actor?: string;
|
|
2787
|
+
source?: OrgLifecycleSource;
|
|
2788
|
+
scope?: ResourceScope;
|
|
2789
|
+
}): Promise<Organization>;
|
|
2790
|
+
/**
|
|
2791
|
+
* Archive organization with coordinated runtime + management transaction.
|
|
2792
|
+
*
|
|
2793
|
+
* Behavior (hard-delete runtime, management tombstone):
|
|
2794
|
+
* - Hard-delete all runtime `member` rows for the org, then the runtime
|
|
2795
|
+
* `organization` row (invitations cascade via FK). Runtime org cannot be used.
|
|
2796
|
+
* - Preserve management organization row with status=archived (tombstone keeps
|
|
2797
|
+
* the stable organization id for audit/recovery).
|
|
2798
|
+
* - Soft-remove all active management memberships for the org (status=removed).
|
|
2799
|
+
* - Exactly one success audit on first archive; re-archive is idempotent with
|
|
2800
|
+
* no duplicate audit.
|
|
2801
|
+
* - Owner/last-owner membership invariants are intentionally not enforced: archive
|
|
2802
|
+
* is a deliberate org-level lifecycle end, not a membership remove.
|
|
2803
|
+
* - Dry-run / missing confirm preview without mutation (same contract as management).
|
|
2804
|
+
* - Active org missing from runtime fails closed (ORG_RUNTIME_NOT_FOUND).
|
|
2805
|
+
* - Already-archived + missing runtime → success, idempotent=true.
|
|
2806
|
+
*/
|
|
2807
|
+
declare function archiveOrganizationInAuth(store: ManagementStore, id: string, input?: {
|
|
2808
|
+
dryRun?: boolean; /** Required for mutation. CLI maps --yes → confirm=true. */
|
|
2809
|
+
confirm?: boolean;
|
|
2810
|
+
actor?: string;
|
|
2811
|
+
source?: OrgLifecycleSource;
|
|
2812
|
+
scope?: ResourceScope;
|
|
2813
|
+
}): Promise<ArchiveOrganizationResult>;
|
|
2814
|
+
//#endregion
|
|
2815
|
+
export { AUDIT_MAX_EVENTS_DEFAULT, AUDIT_MAX_EVENTS_MAX, AUDIT_MAX_EVENTS_MIN, AUDIT_PRUNED_ACTION, AdversarialFixtureFile, ApiKey, ApiKeyView, ArchiveOrganizationResult, AssignableRole, AuditEvent, AuditEventInput, BUILT_IN_ROLE_SLUGS, BackupRecord, BuiltInRoleSlug, CLEARANCE_RELEASE_VERSION, ClearanceError, type CommitSetupLinkInput, ConfigRecord, ConformanceMode, CreateStoreOptions, CreatedApiKey, CredentialKeyring, CustomRole, DataStoreSnapshot, DiagnosticTrace, DirectoryConnection, DoctorCheck, EVENTS_EXPORT_DEFAULT_LIMIT, EVENTS_EXPORT_FORMATS, EVENTS_EXPORT_MAX_LIMIT, EVENTS_LIST_DEFAULT_PAGE_LIMIT, EVENTS_LIST_MAX_PAGE_LIMIT, EVENTS_TAIL_DEFAULT_LIMIT, EVENTS_TAIL_MAX_LIMIT, EncryptedCredential, Environment, EnvironmentInspectResult, EnvironmentLocalStatus, EnvironmentPromoteBlocker, EnvironmentPromotePlanStep, EnvironmentPromoteResult, EventInspectResult, EventsExportEnvelope, EventsExportFormat, EventsExportOptions, EventsTailCursor, EventsTailFilter, FORBIDDEN_DEFAULT_SECRETS, IDEMPOTENCY_DEFAULT_TTL_MS, IdempotencyBackend, IdempotencyRecord, IdentityConnection, IdentityProtocol, JsonStore, LIVE_CONFORMANCE_MODE, LIVE_EVIDENCE_LABEL, LegacyExportFixture, LiveProbeOptions, LiveProbeResult, LocalOidcSession, MANAGEMENT_SURFACES, ManagementStore, ManagementSurface, MemberImportFormat, MemberImportPlan, MemberImportPlanRow, MemberImportResult, MemberImportRowResult, Membership, MembershipActorSource, MembershipSource, MigrationPlan, MigrationPreview, ORGS_LIST_DEFAULT_PAGE_LIMIT, ORGS_LIST_MAX_PAGE_LIMIT, Organization, PageCursorKey, PageOrder, PageSurface, type PgStore, PostgresRestoreOptions, Principal, Project, REPLAYABLE_TRACE_SUBSYSTEMS, ReadinessCheck, ReadinessReport, type RedeemSetupLinkInput, type ReleaseSetupLinkInput, ReplayDiagnosticOptions, ReplayDiagnosticResult, ReplayableTraceSubsystem, type ReserveSetupLinkResult, ResourceId, ResourcePage, ResourceScope, RevokeSessionResult, RuntimeOrganizationIdentity, RuntimeSchemaPlanResult, RuntimeUserIdentity, SCIM_FIXTURE_MODE, SCIM_LOCAL_PROTOCOL_EVIDENCE, SCIM_REAL_FIXTURE_MODE, SETUP_RESERVATION_TTL_MS, SSO_FIXTURE_MODE, SSO_LOCAL_EVIDENCE_LABEL, SSO_LOCAL_PROTOCOL_MODE, SSO_MATRIX_NOT_CERTIFIED, SSO_REAL_FIXTURE_MODE, STORE_SCHEMA_VERSION, ScimActorSource, ScimMutationOpts, ScimProbeOptions, ScimProbeOutcome, ScimUserPayload, ScimUsersFixture, ScopedResource, SessionRecord, SessionSource, SessionView, SetupCapability, type SetupKind, SsoActorSource, SsoCreateInput, SsoMutationOpts, SsoOidcFixture, SsoTestFixture, SsoTestOptions, USERS_EXPORT_DEFAULT_LIMIT, USERS_EXPORT_FORMATS, USERS_EXPORT_MAX_LIMIT, USERS_LIST_DEFAULT_PAGE_LIMIT, USERS_LIST_MAX_PAGE_LIMIT, UsersExportEnvelope, UsersExportFormat, UsersExportOptions, WRITE_ONLY_SECRET_FIELDS, WriteExportArtifactCodes, addMember, addMemberInAuth, appendAuditEvent, archiveOrganization, archiveOrganizationInAuth, assertClientScopeHeaders, assertCredentialKeyConfigured, assertIdempotencyKeyValid, assertLiveEndpoint, assertMigrationRunnable, assertOwnerInvariant, assertProductionCredentialKey, assertProductionSecret, assertResourceInScope, assertSessionPrincipalInScope, auditMaxEvents, beginEventsTail, bridgeRuntimeUserToManagement, buildAuditEvent, buildAuthorizationUrl, builtInRoleId, checkScimConnection, closeAuthBundle, commitSetupLink, configureSsoConnection, consoleRoutesFromContract, correlationId, countAuthTables, createApiKey, createBackup, createEnvironment, createIdempotencyBackend, createLocalOidcIssuerFixture, createLocalScimFixtureServer, createManagementStore, createOrgInAuth, createOrganization, createPgStore, createPostgresBackup, createProject, createRole, createScimConnection, createScimConnectionReal, createSession, createSetupLink, createSsoConnection, createSsoConnectionReal, createUser, createUserInAuth, decodeJwtPayload, decodePageCursor, decryptCredential, defaultDataPath, deleteScimProviderById, deleteSsoProviderById, deleteUser, deleteUserInAuth, deriveSetupConnectionIds, deriveSetupReservationId, diffConfig, disableScimConnection, disableScimConnectionReal, disableSsoConnection, disableSsoConnectionReal, disableUser, disableUserInAuth, dockerDumpArgs, dockerPsqlArgs, emptyIsolatedStorePath, emptySnapshot, encodePageCursor, encryptCredential, enforceAuditRetention, ensureAuthMigrated, ensureOperatorUser, ensurePrivateBackupDirectory, executeMemberImportPlan, exportEvents, exportUsers, findActiveMembership, fingerprint, fingerprintCredential, fingerprintIdempotentRequest, fixturesRoot, generatePkcePair, generateRuntimeSchema, getAuthBundle, getCredentialKeyring, getLatestReadiness, getRuntimeSchemaStatus, idempotencyConflictError, initProject, insertScimProvider, insertSsoProvider, inspectApiKey, inspectEnvironment, inspectEvent, inspectMembership, inspectOrganization, inspectRole, inspectScimConnection, inspectScimTrace, inspectSession, inspectSessionInAuth, inspectSsoConnection, inspectUser, isBuiltInRoleSlug, isClearanceError, isCredentialEnvelope, isDevelopmentLike, isForbiddenDefaultSecret, isManagementStore, isReplayableTraceSubsystem, isSecretLikeConfigEntry, isSecretLikeConfigKey, listApiKeys, listEnvironments, listEvents, listEventsPage, listMembers, listOrganizations, listOrganizationsPage, listOrgsFromDb, listProjects, listRoles, listScimConnections, listSessions, listSessionsFromDb, listSessionsInAuth, listSessionsPage, listSessionsPageInAuth, listSetupLinks, listSsoConnections, listUsers, listUsersFromDb, listUsersPage, loadJsonFixture, loadLegacyFixture, migrateRuntimeSchema, migrationStatus, newId, normalizeAndValidateApiKeyScopes, normalizeAndValidatePermissions, normalizeEventsExportBefore, normalizeEventsExportFormat, normalizeEventsExportLimit, normalizeEventsTailLimit, normalizePageLimit, normalizeSessionLimit, normalizeSnapshot, normalizeUsersExportFormat, normalizeUsersExportLimit, normalizeUsersExportStatus, nowIso, overviewStats, paginateByCreatedAt, parseConfigJson, parseCorsOrigins, parseCredentialEnvelope, parseUserStatusInput, pgStoreId, planEnvironmentCreate, planMemberImport, planMigration, planProjectCreate, planRuntimeSchema, pollEventsTail, postgresBackupFailure, postgresContainerSettings, previewMigration, probeOutcomeToError, probeScimEndpoint, promoteEnvironment, publicConfig, publicDirectoryConnection, publicIdentityConnection, recordEvent, redactRecord, redactValue, redeemSetupLink, releaseSetupLink, removeMember, removeMemberInAuth, replayDiagnosticTrace, replayScimTrace, requireOperatorToken, reserveSetupLink, resetAuthBundle, resolveAssignableRole, resolveCredentialKeyring, resolveIdempotencyTtlMs, resolveMembershipId, resolveOperatorScope, resolveScimConnection, resolveSsoConnection, restoreBackup, restorePostgresBackup, revokeApiKey, revokeSession, revokeSessionInAuth, revokeSetupLink, rollbackMigration, rollbackMigrationDurable, rotateApiKey, rotateCredential, rotateScimCredential, rotateSsoCredential, runDoctor, runMigration, runMigrationDurable, runReadinessCheck, runSsoMatrix, sanitizeAuditEvent, sanitizePrincipalForExport, sanitizeSessionView, scopeFilter, secureBackupArtifact, selectEventsForExport, selectUsersForExport, setConfig, sortEventsDeterministic, sortUsersDeterministic, syncRuntimeOrganizationToManagementDurable, syncRuntimeUserToManagement, syncRuntimeUserToManagementDurable, testScimConnection, testScimConnectionLive, testScimConnectionReal, testSsoConnection, testSsoConnectionLive, testSsoConnectionReal, toSessionView, updateMember, updateMemberInAuth, updateOrganization, updateOrganizationInAuth, updateRole, updateUser, updateUserInAuth, upgradeCheck, upgradeCheckWithDb, validateApiKeyName, validateConfig, validateCurrentConfig, validateIsolatedRestoreDatabaseName, validateRole, validateRoleName, validateRoleSlug, validateSamlProviderConfig, verifyBackup, verifyMigration, verifyMigrationDurable, verifyPostgresBackup, verifySsoOidcLocalProtocol, writeExportArtifact };
|