@simonsbs/keylore 1.0.0-rc4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/.env.example +64 -0
  2. package/LICENSE +176 -0
  3. package/NOTICE +5 -0
  4. package/README.md +424 -0
  5. package/bin/keylore-http.js +3 -0
  6. package/bin/keylore-stdio.js +3 -0
  7. package/data/auth-clients.json +54 -0
  8. package/data/catalog.json +53 -0
  9. package/data/policies.json +25 -0
  10. package/dist/adapters/adapter-registry.js +143 -0
  11. package/dist/adapters/aws-secrets-manager-adapter.js +99 -0
  12. package/dist/adapters/command-runner.js +17 -0
  13. package/dist/adapters/env-secret-adapter.js +42 -0
  14. package/dist/adapters/gcp-secret-manager-adapter.js +129 -0
  15. package/dist/adapters/local-secret-adapter.js +54 -0
  16. package/dist/adapters/onepassword-secret-adapter.js +83 -0
  17. package/dist/adapters/reference-utils.js +44 -0
  18. package/dist/adapters/types.js +1 -0
  19. package/dist/adapters/vault-secret-adapter.js +103 -0
  20. package/dist/app.js +132 -0
  21. package/dist/cli/args.js +51 -0
  22. package/dist/cli/run.js +483 -0
  23. package/dist/cli.js +18 -0
  24. package/dist/config.js +295 -0
  25. package/dist/domain/types.js +967 -0
  26. package/dist/http/admin-ui.js +3010 -0
  27. package/dist/http/server.js +1210 -0
  28. package/dist/index.js +40 -0
  29. package/dist/mcp/create-server.js +388 -0
  30. package/dist/mcp/stdio.js +7 -0
  31. package/dist/repositories/credential-repository.js +109 -0
  32. package/dist/repositories/interfaces.js +1 -0
  33. package/dist/repositories/json-file.js +20 -0
  34. package/dist/repositories/pg-access-token-repository.js +118 -0
  35. package/dist/repositories/pg-approval-repository.js +157 -0
  36. package/dist/repositories/pg-audit-log.js +62 -0
  37. package/dist/repositories/pg-auth-client-repository.js +98 -0
  38. package/dist/repositories/pg-authorization-code-repository.js +95 -0
  39. package/dist/repositories/pg-break-glass-repository.js +174 -0
  40. package/dist/repositories/pg-credential-repository.js +163 -0
  41. package/dist/repositories/pg-oauth-client-assertion-repository.js +25 -0
  42. package/dist/repositories/pg-policy-repository.js +62 -0
  43. package/dist/repositories/pg-refresh-token-repository.js +125 -0
  44. package/dist/repositories/pg-rotation-run-repository.js +127 -0
  45. package/dist/repositories/pg-tenant-repository.js +56 -0
  46. package/dist/repositories/policy-repository.js +24 -0
  47. package/dist/runtime/sandbox-runner.js +114 -0
  48. package/dist/services/access-fingerprint.js +13 -0
  49. package/dist/services/approval-service.js +148 -0
  50. package/dist/services/audit-log.js +38 -0
  51. package/dist/services/auth-context.js +43 -0
  52. package/dist/services/auth-secrets.js +14 -0
  53. package/dist/services/auth-service.js +784 -0
  54. package/dist/services/backup-service.js +610 -0
  55. package/dist/services/break-glass-service.js +207 -0
  56. package/dist/services/broker-service.js +557 -0
  57. package/dist/services/core-mode-service.js +154 -0
  58. package/dist/services/egress-policy.js +119 -0
  59. package/dist/services/local-secret-store.js +119 -0
  60. package/dist/services/maintenance-service.js +99 -0
  61. package/dist/services/notification-service.js +83 -0
  62. package/dist/services/policy-engine.js +85 -0
  63. package/dist/services/rate-limit-service.js +80 -0
  64. package/dist/services/rotation-service.js +271 -0
  65. package/dist/services/telemetry.js +149 -0
  66. package/dist/services/tenant-service.js +127 -0
  67. package/dist/services/trace-export-service.js +126 -0
  68. package/dist/services/trace-service.js +87 -0
  69. package/dist/storage/bootstrap.js +68 -0
  70. package/dist/storage/database.js +39 -0
  71. package/dist/storage/in-memory-database.js +40 -0
  72. package/dist/storage/migrations.js +27 -0
  73. package/migrations/001_init.sql +49 -0
  74. package/migrations/002_phase2_auth.sql +53 -0
  75. package/migrations/003_v05_operations.sql +9 -0
  76. package/migrations/004_v07_security.sql +28 -0
  77. package/migrations/005_v08_reviews.sql +11 -0
  78. package/migrations/006_v09_auth_trace_rotation.sql +51 -0
  79. package/migrations/007_v010_multi_tenant.sql +32 -0
  80. package/migrations/008_v011_auth_tenant_ops.sql +95 -0
  81. package/package.json +78 -0
package/dist/config.js ADDED
@@ -0,0 +1,295 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import * as z from "zod/v4";
6
+ const LOCAL_DATABASE_URL = "postgresql://keylore:keylore@127.0.0.1:5432/keylore";
7
+ const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
8
+ const LOCAL_ADMIN_CLIENT_ID = "keylore-admin-local";
9
+ const LOCAL_ADMIN_CLIENT_SECRET = "keylore-local-admin";
10
+ const LOCAL_CONSUMER_CLIENT_SECRET = "keylore-local-consumer";
11
+ const LOCAL_ADMIN_SCOPES = [
12
+ "catalog:read",
13
+ "catalog:write",
14
+ "admin:read",
15
+ "admin:write",
16
+ "auth:read",
17
+ "auth:write",
18
+ "broker:use",
19
+ "sandbox:run",
20
+ "audit:read",
21
+ "approval:read",
22
+ "approval:review",
23
+ "system:read",
24
+ "system:write",
25
+ "backup:read",
26
+ "backup:write",
27
+ "breakglass:request",
28
+ "breakglass:read",
29
+ "breakglass:review",
30
+ "mcp:use",
31
+ ];
32
+ const emptyStringToUndefined = (schema) => z.preprocess((value) => {
33
+ if (typeof value === "string" && value.trim() === "") {
34
+ return undefined;
35
+ }
36
+ return value;
37
+ }, schema);
38
+ const optionalUrl = emptyStringToUndefined(z.string().url().optional());
39
+ const optionalString = emptyStringToUndefined(z.string().optional());
40
+ function parseDotEnv(contents) {
41
+ const parsed = {};
42
+ for (const line of contents.split(/\r?\n/)) {
43
+ const trimmed = line.trim();
44
+ if (!trimmed || trimmed.startsWith("#")) {
45
+ continue;
46
+ }
47
+ const separator = trimmed.indexOf("=");
48
+ if (separator <= 0) {
49
+ continue;
50
+ }
51
+ const key = trimmed.slice(0, separator).trim();
52
+ const rawValue = trimmed.slice(separator + 1).trim();
53
+ const value = (rawValue.startsWith('"') && rawValue.endsWith('"')) ||
54
+ (rawValue.startsWith("'") && rawValue.endsWith("'"))
55
+ ? rawValue.slice(1, -1)
56
+ : rawValue;
57
+ parsed[key] = value;
58
+ }
59
+ return parsed;
60
+ }
61
+ function isMissing(value) {
62
+ return value === undefined || value.trim() === "";
63
+ }
64
+ function isLoopbackHost(host) {
65
+ return host === undefined || host === "127.0.0.1" || host === "localhost" || host === "::1";
66
+ }
67
+ function hydrateEnvironment(cwd) {
68
+ const envFilePath = path.resolve(cwd, ".env");
69
+ const fileEnv = fs.existsSync(envFilePath) && fs.statSync(envFilePath).isFile()
70
+ ? parseDotEnv(fs.readFileSync(envFilePath, "utf8"))
71
+ : {};
72
+ const effectiveEnv = {
73
+ ...fileEnv,
74
+ ...process.env,
75
+ };
76
+ if (isMissing(effectiveEnv.KEYLORE_DATABASE_URL)) {
77
+ effectiveEnv.KEYLORE_DATABASE_URL = LOCAL_DATABASE_URL;
78
+ }
79
+ const environment = effectiveEnv.KEYLORE_ENVIRONMENT?.trim() || "development";
80
+ const httpHost = effectiveEnv.KEYLORE_HTTP_HOST?.trim() || "127.0.0.1";
81
+ const bootstrapFromFiles = effectiveEnv.KEYLORE_BOOTSTRAP_FROM_FILES !== "false";
82
+ const localQuickstartEnabled = environment !== "production" && bootstrapFromFiles && isLoopbackHost(httpHost);
83
+ const adminSecretWasMissing = isMissing(effectiveEnv.KEYLORE_BOOTSTRAP_ADMIN_CLIENT_SECRET);
84
+ const consumerSecretWasMissing = isMissing(effectiveEnv.KEYLORE_BOOTSTRAP_CONSUMER_CLIENT_SECRET);
85
+ if (localQuickstartEnabled) {
86
+ if (adminSecretWasMissing) {
87
+ effectiveEnv.KEYLORE_BOOTSTRAP_ADMIN_CLIENT_SECRET = LOCAL_ADMIN_CLIENT_SECRET;
88
+ }
89
+ if (consumerSecretWasMissing) {
90
+ effectiveEnv.KEYLORE_BOOTSTRAP_CONSUMER_CLIENT_SECRET = LOCAL_CONSUMER_CLIENT_SECRET;
91
+ }
92
+ }
93
+ for (const [key, value] of Object.entries(effectiveEnv)) {
94
+ if (value !== undefined && isMissing(process.env[key])) {
95
+ process.env[key] = value;
96
+ }
97
+ }
98
+ return {
99
+ env: effectiveEnv,
100
+ localAdminBootstrapAvailable: localQuickstartEnabled && adminSecretWasMissing,
101
+ };
102
+ }
103
+ function resolveRuntimeRoot(cwd) {
104
+ if (fs.existsSync(path.join(cwd, "migrations")) && fs.existsSync(path.join(cwd, "data"))) {
105
+ return cwd;
106
+ }
107
+ return PACKAGE_ROOT;
108
+ }
109
+ function resolveDefaultDataDir(cwd) {
110
+ if (fs.existsSync(path.join(cwd, "data"))) {
111
+ return path.resolve(cwd, "data");
112
+ }
113
+ return path.join(os.homedir(), ".keylore");
114
+ }
115
+ const envSchema = z.object({
116
+ KEYLORE_DATA_DIR: z.string().optional(),
117
+ KEYLORE_CATALOG_FILE: z.string().optional(),
118
+ KEYLORE_POLICY_FILE: z.string().optional(),
119
+ KEYLORE_AUTH_CLIENTS_FILE: z.string().optional(),
120
+ KEYLORE_MIGRATIONS_DIR: z.string().optional(),
121
+ KEYLORE_LOCAL_SECRETS_FILE: z.string().optional(),
122
+ KEYLORE_LOCAL_SECRETS_KEY_FILE: z.string().optional(),
123
+ KEYLORE_DATABASE_URL: z.string().min(1),
124
+ KEYLORE_DATABASE_POOL_MAX: z.coerce.number().int().min(1).max(100).default(10),
125
+ KEYLORE_HTTP_HOST: z.string().default("127.0.0.1"),
126
+ KEYLORE_HTTP_PORT: z.coerce.number().int().min(1).max(65535).default(8787),
127
+ KEYLORE_PUBLIC_BASE_URL: z.string().url().optional(),
128
+ KEYLORE_OAUTH_ISSUER_URL: z.string().url().optional(),
129
+ KEYLORE_ENVIRONMENT: z.string().default("development"),
130
+ KEYLORE_DEFAULT_PRINCIPAL: z.string().default("local-operator"),
131
+ KEYLORE_LOG_LEVEL: z.string().default("info"),
132
+ KEYLORE_BOOTSTRAP_ADMIN_CLIENT_SECRET: z.string().optional(),
133
+ KEYLORE_BOOTSTRAP_CONSUMER_CLIENT_SECRET: z.string().optional(),
134
+ KEYLORE_BOOTSTRAP_FROM_FILES: z
135
+ .string()
136
+ .transform((value) => value !== "false")
137
+ .prefault("true"),
138
+ KEYLORE_MAX_REQUEST_BYTES: z.coerce.number().int().min(1024).default(131072),
139
+ KEYLORE_OUTBOUND_TIMEOUT_MS: z.coerce.number().int().min(100).default(10000),
140
+ KEYLORE_MAX_RESPONSE_BYTES: z.coerce.number().int().min(1024).default(32768),
141
+ KEYLORE_RATE_LIMIT_WINDOW_MS: z.coerce.number().int().min(1000).default(60000),
142
+ KEYLORE_RATE_LIMIT_MAX_REQUESTS: z.coerce.number().int().min(1).default(120),
143
+ KEYLORE_MAINTENANCE_ENABLED: z
144
+ .string()
145
+ .transform((value) => value !== "false")
146
+ .prefault("true"),
147
+ KEYLORE_MAINTENANCE_INTERVAL_MS: z.coerce.number().int().min(1000).default(60000),
148
+ KEYLORE_ACCESS_TOKEN_TTL_SECONDS: z.coerce.number().int().min(60).default(3600),
149
+ KEYLORE_AUTHORIZATION_CODE_TTL_SECONDS: z.coerce.number().int().min(60).max(3600).default(300),
150
+ KEYLORE_REFRESH_TOKEN_TTL_SECONDS: z.coerce.number().int().min(300).default(2592000),
151
+ KEYLORE_APPROVAL_TTL_SECONDS: z.coerce.number().int().min(60).default(1800),
152
+ KEYLORE_APPROVAL_REVIEW_QUORUM: z.coerce.number().int().min(1).max(5).default(1),
153
+ KEYLORE_BREAKGLASS_MAX_DURATION_SECONDS: z.coerce.number().int().min(60).max(86400).default(900),
154
+ KEYLORE_BREAKGLASS_REVIEW_QUORUM: z.coerce.number().int().min(1).max(5).default(1),
155
+ KEYLORE_VAULT_ADDR: optionalUrl,
156
+ KEYLORE_VAULT_TOKEN: optionalString,
157
+ KEYLORE_VAULT_NAMESPACE: optionalString,
158
+ KEYLORE_OP_BIN: z.string().default("op"),
159
+ KEYLORE_AWS_BIN: z.string().default("aws"),
160
+ KEYLORE_GCLOUD_BIN: z.string().default("gcloud"),
161
+ KEYLORE_EGRESS_ALLOW_PRIVATE_IPS: z
162
+ .string()
163
+ .transform((value) => value === "true")
164
+ .prefault("false"),
165
+ KEYLORE_EGRESS_ALLOWED_HOSTS: z.string().default(""),
166
+ KEYLORE_EGRESS_ALLOWED_HTTPS_PORTS: z.string().default("443"),
167
+ KEYLORE_SANDBOX_INJECTION_ENABLED: z
168
+ .string()
169
+ .transform((value) => value === "true")
170
+ .prefault("false"),
171
+ KEYLORE_SANDBOX_COMMAND_ALLOWLIST: z.string().default(""),
172
+ KEYLORE_SANDBOX_ENV_ALLOWLIST: z.string().default(""),
173
+ KEYLORE_SANDBOX_DEFAULT_TIMEOUT_MS: z.coerce.number().int().min(100).default(5000),
174
+ KEYLORE_SANDBOX_MAX_OUTPUT_BYTES: z.coerce.number().int().min(256).default(16384),
175
+ KEYLORE_ADAPTER_MAX_ATTEMPTS: z.coerce.number().int().min(1).max(5).default(2),
176
+ KEYLORE_ADAPTER_RETRY_DELAY_MS: z.coerce.number().int().min(0).default(250),
177
+ KEYLORE_ADAPTER_CIRCUIT_BREAKER_THRESHOLD: z.coerce.number().int().min(1).default(3),
178
+ KEYLORE_ADAPTER_CIRCUIT_BREAKER_COOLDOWN_MS: z.coerce.number().int().min(1000).default(60000),
179
+ KEYLORE_NOTIFICATION_WEBHOOK_URL: optionalUrl,
180
+ KEYLORE_NOTIFICATION_SIGNING_SECRET: optionalString,
181
+ KEYLORE_NOTIFICATION_TIMEOUT_MS: z.coerce.number().int().min(100).default(5000),
182
+ KEYLORE_TRACE_CAPTURE_ENABLED: z
183
+ .string()
184
+ .transform((value) => value !== "false")
185
+ .prefault("true"),
186
+ KEYLORE_TRACE_RECENT_SPAN_LIMIT: z.coerce.number().int().min(10).max(5000).default(500),
187
+ KEYLORE_TRACE_EXPORT_URL: optionalUrl,
188
+ KEYLORE_TRACE_EXPORT_AUTH_HEADER: optionalString,
189
+ KEYLORE_TRACE_EXPORT_BATCH_SIZE: z.coerce.number().int().min(1).max(1000).default(50),
190
+ KEYLORE_TRACE_EXPORT_INTERVAL_MS: z.coerce.number().int().min(100).default(5000),
191
+ KEYLORE_TRACE_EXPORT_TIMEOUT_MS: z.coerce.number().int().min(100).default(5000),
192
+ KEYLORE_ROTATION_PLANNING_HORIZON_DAYS: z.coerce.number().int().min(1).max(365).default(14),
193
+ });
194
+ export function loadConfig(cwd = process.cwd()) {
195
+ const hydrated = hydrateEnvironment(cwd);
196
+ const env = envSchema.parse(hydrated.env);
197
+ const runtimeRoot = resolveRuntimeRoot(cwd);
198
+ const dataDir = path.resolve(env.KEYLORE_DATA_DIR ?? resolveDefaultDataDir(cwd));
199
+ const publicBaseUrl = env.KEYLORE_PUBLIC_BASE_URL ?? `http://${env.KEYLORE_HTTP_HOST}:${env.KEYLORE_HTTP_PORT}`;
200
+ const oauthIssuerUrl = env.KEYLORE_OAUTH_ISSUER_URL ?? `${publicBaseUrl}/oauth`;
201
+ const localQuickstartEnabled = env.KEYLORE_ENVIRONMENT !== "production" &&
202
+ env.KEYLORE_BOOTSTRAP_FROM_FILES &&
203
+ isLoopbackHost(env.KEYLORE_HTTP_HOST);
204
+ return {
205
+ appName: "keylore",
206
+ version: "1.0.0-rc4",
207
+ dataDir,
208
+ bootstrapCatalogPath: path.resolve(runtimeRoot, "data", env.KEYLORE_CATALOG_FILE ?? "catalog.json"),
209
+ bootstrapPolicyPath: path.resolve(runtimeRoot, "data", env.KEYLORE_POLICY_FILE ?? "policies.json"),
210
+ bootstrapAuthClientsPath: path.resolve(runtimeRoot, "data", env.KEYLORE_AUTH_CLIENTS_FILE ?? "auth-clients.json"),
211
+ migrationsDir: path.resolve(runtimeRoot, env.KEYLORE_MIGRATIONS_DIR ?? "migrations"),
212
+ localSecretsFilePath: path.resolve(dataDir, env.KEYLORE_LOCAL_SECRETS_FILE ?? "local-secrets.enc.json"),
213
+ localSecretsKeyPath: path.resolve(dataDir, env.KEYLORE_LOCAL_SECRETS_KEY_FILE ?? "local-secrets.key"),
214
+ databaseUrl: env.KEYLORE_DATABASE_URL,
215
+ databasePoolMax: env.KEYLORE_DATABASE_POOL_MAX,
216
+ httpHost: env.KEYLORE_HTTP_HOST,
217
+ httpPort: env.KEYLORE_HTTP_PORT,
218
+ publicBaseUrl,
219
+ oauthIssuerUrl,
220
+ environment: env.KEYLORE_ENVIRONMENT,
221
+ defaultPrincipal: env.KEYLORE_DEFAULT_PRINCIPAL,
222
+ logLevel: env.KEYLORE_LOG_LEVEL,
223
+ bootstrapFromFiles: env.KEYLORE_BOOTSTRAP_FROM_FILES,
224
+ maxRequestBytes: env.KEYLORE_MAX_REQUEST_BYTES,
225
+ outboundTimeoutMs: env.KEYLORE_OUTBOUND_TIMEOUT_MS,
226
+ maxResponseBytes: env.KEYLORE_MAX_RESPONSE_BYTES,
227
+ rateLimitWindowMs: env.KEYLORE_RATE_LIMIT_WINDOW_MS,
228
+ rateLimitMaxRequests: env.KEYLORE_RATE_LIMIT_MAX_REQUESTS,
229
+ maintenanceEnabled: env.KEYLORE_MAINTENANCE_ENABLED,
230
+ maintenanceIntervalMs: env.KEYLORE_MAINTENANCE_INTERVAL_MS,
231
+ accessTokenTtlSeconds: env.KEYLORE_ACCESS_TOKEN_TTL_SECONDS,
232
+ authorizationCodeTtlSeconds: env.KEYLORE_AUTHORIZATION_CODE_TTL_SECONDS,
233
+ refreshTokenTtlSeconds: env.KEYLORE_REFRESH_TOKEN_TTL_SECONDS,
234
+ approvalTtlSeconds: env.KEYLORE_APPROVAL_TTL_SECONDS,
235
+ approvalReviewQuorum: env.KEYLORE_APPROVAL_REVIEW_QUORUM,
236
+ breakGlassMaxDurationSeconds: env.KEYLORE_BREAKGLASS_MAX_DURATION_SECONDS,
237
+ breakGlassReviewQuorum: env.KEYLORE_BREAKGLASS_REVIEW_QUORUM,
238
+ vaultAddr: env.KEYLORE_VAULT_ADDR || undefined,
239
+ vaultToken: env.KEYLORE_VAULT_TOKEN || undefined,
240
+ vaultNamespace: env.KEYLORE_VAULT_NAMESPACE || undefined,
241
+ opBinary: env.KEYLORE_OP_BIN,
242
+ awsBinary: env.KEYLORE_AWS_BIN,
243
+ gcloudBinary: env.KEYLORE_GCLOUD_BIN,
244
+ egressAllowPrivateIps: env.KEYLORE_EGRESS_ALLOW_PRIVATE_IPS,
245
+ egressAllowedHosts: env.KEYLORE_EGRESS_ALLOWED_HOSTS
246
+ .split(",")
247
+ .map((value) => value.trim())
248
+ .filter(Boolean),
249
+ egressAllowedHttpsPorts: env.KEYLORE_EGRESS_ALLOWED_HTTPS_PORTS
250
+ .split(",")
251
+ .map((value) => Number.parseInt(value.trim(), 10))
252
+ .filter((value) => Number.isInteger(value) && value >= 1 && value <= 65535),
253
+ sandboxInjectionEnabled: env.KEYLORE_SANDBOX_INJECTION_ENABLED,
254
+ sandboxCommandAllowlist: env.KEYLORE_SANDBOX_COMMAND_ALLOWLIST
255
+ .split(",")
256
+ .map((value) => value.trim())
257
+ .filter(Boolean),
258
+ sandboxEnvAllowlist: env.KEYLORE_SANDBOX_ENV_ALLOWLIST
259
+ .split(",")
260
+ .map((value) => value.trim())
261
+ .filter(Boolean),
262
+ sandboxDefaultTimeoutMs: env.KEYLORE_SANDBOX_DEFAULT_TIMEOUT_MS,
263
+ sandboxMaxOutputBytes: env.KEYLORE_SANDBOX_MAX_OUTPUT_BYTES,
264
+ adapterMaxAttempts: env.KEYLORE_ADAPTER_MAX_ATTEMPTS,
265
+ adapterRetryDelayMs: env.KEYLORE_ADAPTER_RETRY_DELAY_MS,
266
+ adapterCircuitBreakerThreshold: env.KEYLORE_ADAPTER_CIRCUIT_BREAKER_THRESHOLD,
267
+ adapterCircuitBreakerCooldownMs: env.KEYLORE_ADAPTER_CIRCUIT_BREAKER_COOLDOWN_MS,
268
+ notificationWebhookUrl: env.KEYLORE_NOTIFICATION_WEBHOOK_URL || undefined,
269
+ notificationSigningSecret: env.KEYLORE_NOTIFICATION_SIGNING_SECRET || undefined,
270
+ notificationTimeoutMs: env.KEYLORE_NOTIFICATION_TIMEOUT_MS,
271
+ traceCaptureEnabled: env.KEYLORE_TRACE_CAPTURE_ENABLED,
272
+ traceRecentSpanLimit: env.KEYLORE_TRACE_RECENT_SPAN_LIMIT,
273
+ traceExportUrl: env.KEYLORE_TRACE_EXPORT_URL || undefined,
274
+ traceExportAuthHeader: env.KEYLORE_TRACE_EXPORT_AUTH_HEADER || undefined,
275
+ traceExportBatchSize: env.KEYLORE_TRACE_EXPORT_BATCH_SIZE,
276
+ traceExportIntervalMs: env.KEYLORE_TRACE_EXPORT_INTERVAL_MS,
277
+ traceExportTimeoutMs: env.KEYLORE_TRACE_EXPORT_TIMEOUT_MS,
278
+ rotationPlanningHorizonDays: env.KEYLORE_ROTATION_PLANNING_HORIZON_DAYS,
279
+ localQuickstartEnabled,
280
+ localQuickstartBootstrap: localQuickstartEnabled
281
+ ? {
282
+ clientId: LOCAL_ADMIN_CLIENT_ID,
283
+ clientSecret: env.KEYLORE_BOOTSTRAP_ADMIN_CLIENT_SECRET ?? LOCAL_ADMIN_CLIENT_SECRET,
284
+ scopes: [...LOCAL_ADMIN_SCOPES],
285
+ }
286
+ : undefined,
287
+ localAdminBootstrap: hydrated.localAdminBootstrapAvailable
288
+ ? {
289
+ clientId: LOCAL_ADMIN_CLIENT_ID,
290
+ clientSecret: LOCAL_ADMIN_CLIENT_SECRET,
291
+ scopes: [...LOCAL_ADMIN_SCOPES],
292
+ }
293
+ : undefined,
294
+ };
295
+ }