@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.
- package/.env.example +64 -0
- package/LICENSE +176 -0
- package/NOTICE +5 -0
- package/README.md +424 -0
- package/bin/keylore-http.js +3 -0
- package/bin/keylore-stdio.js +3 -0
- package/data/auth-clients.json +54 -0
- package/data/catalog.json +53 -0
- package/data/policies.json +25 -0
- package/dist/adapters/adapter-registry.js +143 -0
- package/dist/adapters/aws-secrets-manager-adapter.js +99 -0
- package/dist/adapters/command-runner.js +17 -0
- package/dist/adapters/env-secret-adapter.js +42 -0
- package/dist/adapters/gcp-secret-manager-adapter.js +129 -0
- package/dist/adapters/local-secret-adapter.js +54 -0
- package/dist/adapters/onepassword-secret-adapter.js +83 -0
- package/dist/adapters/reference-utils.js +44 -0
- package/dist/adapters/types.js +1 -0
- package/dist/adapters/vault-secret-adapter.js +103 -0
- package/dist/app.js +132 -0
- package/dist/cli/args.js +51 -0
- package/dist/cli/run.js +483 -0
- package/dist/cli.js +18 -0
- package/dist/config.js +295 -0
- package/dist/domain/types.js +967 -0
- package/dist/http/admin-ui.js +3010 -0
- package/dist/http/server.js +1210 -0
- package/dist/index.js +40 -0
- package/dist/mcp/create-server.js +388 -0
- package/dist/mcp/stdio.js +7 -0
- package/dist/repositories/credential-repository.js +109 -0
- package/dist/repositories/interfaces.js +1 -0
- package/dist/repositories/json-file.js +20 -0
- package/dist/repositories/pg-access-token-repository.js +118 -0
- package/dist/repositories/pg-approval-repository.js +157 -0
- package/dist/repositories/pg-audit-log.js +62 -0
- package/dist/repositories/pg-auth-client-repository.js +98 -0
- package/dist/repositories/pg-authorization-code-repository.js +95 -0
- package/dist/repositories/pg-break-glass-repository.js +174 -0
- package/dist/repositories/pg-credential-repository.js +163 -0
- package/dist/repositories/pg-oauth-client-assertion-repository.js +25 -0
- package/dist/repositories/pg-policy-repository.js +62 -0
- package/dist/repositories/pg-refresh-token-repository.js +125 -0
- package/dist/repositories/pg-rotation-run-repository.js +127 -0
- package/dist/repositories/pg-tenant-repository.js +56 -0
- package/dist/repositories/policy-repository.js +24 -0
- package/dist/runtime/sandbox-runner.js +114 -0
- package/dist/services/access-fingerprint.js +13 -0
- package/dist/services/approval-service.js +148 -0
- package/dist/services/audit-log.js +38 -0
- package/dist/services/auth-context.js +43 -0
- package/dist/services/auth-secrets.js +14 -0
- package/dist/services/auth-service.js +784 -0
- package/dist/services/backup-service.js +610 -0
- package/dist/services/break-glass-service.js +207 -0
- package/dist/services/broker-service.js +557 -0
- package/dist/services/core-mode-service.js +154 -0
- package/dist/services/egress-policy.js +119 -0
- package/dist/services/local-secret-store.js +119 -0
- package/dist/services/maintenance-service.js +99 -0
- package/dist/services/notification-service.js +83 -0
- package/dist/services/policy-engine.js +85 -0
- package/dist/services/rate-limit-service.js +80 -0
- package/dist/services/rotation-service.js +271 -0
- package/dist/services/telemetry.js +149 -0
- package/dist/services/tenant-service.js +127 -0
- package/dist/services/trace-export-service.js +126 -0
- package/dist/services/trace-service.js +87 -0
- package/dist/storage/bootstrap.js +68 -0
- package/dist/storage/database.js +39 -0
- package/dist/storage/in-memory-database.js +40 -0
- package/dist/storage/migrations.js +27 -0
- package/migrations/001_init.sql +49 -0
- package/migrations/002_phase2_auth.sql +53 -0
- package/migrations/003_v05_operations.sql +9 -0
- package/migrations/004_v07_security.sql +28 -0
- package/migrations/005_v08_reviews.sql +11 -0
- package/migrations/006_v09_auth_trace_rotation.sql +51 -0
- package/migrations/007_v010_multi_tenant.sql +32 -0
- package/migrations/008_v011_auth_tenant_ops.sql +95 -0
- package/package.json +78 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { parseRef } from "./reference-utils.js";
|
|
2
|
+
function pickTimestamp(payload, camel, snake) {
|
|
3
|
+
const value = payload[camel] ?? payload[snake];
|
|
4
|
+
return typeof value === "string" ? value : undefined;
|
|
5
|
+
}
|
|
6
|
+
function pickVersion(payload) {
|
|
7
|
+
const value = payload.version;
|
|
8
|
+
if (typeof value === "number") {
|
|
9
|
+
return String(value);
|
|
10
|
+
}
|
|
11
|
+
return typeof value === "string" ? value : undefined;
|
|
12
|
+
}
|
|
13
|
+
export class OnePasswordSecretAdapter {
|
|
14
|
+
commandRunner;
|
|
15
|
+
binary;
|
|
16
|
+
id = "1password";
|
|
17
|
+
constructor(commandRunner, binary) {
|
|
18
|
+
this.commandRunner = commandRunner;
|
|
19
|
+
this.binary = binary;
|
|
20
|
+
}
|
|
21
|
+
async resolve(credential) {
|
|
22
|
+
const result = await this.commandRunner.run(this.binary, ["read", credential.binding.ref], {
|
|
23
|
+
env: process.env,
|
|
24
|
+
timeoutMs: 10_000,
|
|
25
|
+
});
|
|
26
|
+
const secret = result.stdout.trimEnd();
|
|
27
|
+
if (!secret) {
|
|
28
|
+
throw new Error(`1Password returned no secret value for ${credential.binding.ref}.`);
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
secret,
|
|
32
|
+
headerName: credential.binding.headerName,
|
|
33
|
+
headerValue: credential.binding.authType === "bearer"
|
|
34
|
+
? `${credential.binding.headerPrefix ?? "Bearer "}${secret}`
|
|
35
|
+
: secret,
|
|
36
|
+
inspection: await this.inspect(credential),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async inspect(credential) {
|
|
40
|
+
const parsed = parseRef(credential.binding.ref);
|
|
41
|
+
const reference = new URL(parsed.resource);
|
|
42
|
+
const vault = reference.hostname;
|
|
43
|
+
const item = reference.pathname.split("/").filter(Boolean)[0];
|
|
44
|
+
const args = ["item", "get", item ?? "", "--vault", vault ?? "", "--format", "json"];
|
|
45
|
+
const result = await this.commandRunner.run(this.binary, args, {
|
|
46
|
+
env: process.env,
|
|
47
|
+
timeoutMs: 10_000,
|
|
48
|
+
});
|
|
49
|
+
const payload = JSON.parse(result.stdout);
|
|
50
|
+
return {
|
|
51
|
+
adapter: this.id,
|
|
52
|
+
ref: credential.binding.ref,
|
|
53
|
+
status: "ok",
|
|
54
|
+
resolved: true,
|
|
55
|
+
version: pickVersion(payload),
|
|
56
|
+
createdAt: pickTimestamp(payload, "createdAt", "created_at"),
|
|
57
|
+
updatedAt: pickTimestamp(payload, "updatedAt", "updated_at"),
|
|
58
|
+
notes: ["1Password inspection uses item metadata from the local op CLI session."],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async healthcheck() {
|
|
62
|
+
try {
|
|
63
|
+
await this.commandRunner.run(this.binary, ["--version"], {
|
|
64
|
+
env: process.env,
|
|
65
|
+
timeoutMs: 5_000,
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
adapter: this.id,
|
|
69
|
+
available: true,
|
|
70
|
+
status: "ok",
|
|
71
|
+
details: `${this.binary} is available.`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
return {
|
|
76
|
+
adapter: this.id,
|
|
77
|
+
available: false,
|
|
78
|
+
status: "error",
|
|
79
|
+
details: error instanceof Error ? error.message : "1Password CLI unavailable.",
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
function splitRef(ref) {
|
|
2
|
+
const [pathPart = "", queryPart = ""] = ref.split("?", 2);
|
|
3
|
+
const [resource = "", field] = pathPart.split("#", 2);
|
|
4
|
+
return {
|
|
5
|
+
resource,
|
|
6
|
+
field: field || undefined,
|
|
7
|
+
query: new URLSearchParams(queryPart),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function parseRef(ref) {
|
|
11
|
+
return splitRef(ref);
|
|
12
|
+
}
|
|
13
|
+
export function extractField(value, fieldPath) {
|
|
14
|
+
if (!fieldPath) {
|
|
15
|
+
if (typeof value === "string") {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
19
|
+
const entries = Object.entries(value);
|
|
20
|
+
if (entries.length === 1 && typeof entries[0]?.[1] === "string") {
|
|
21
|
+
return entries[0][1];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
throw new Error("Secret reference requires a field selector for structured data.");
|
|
25
|
+
}
|
|
26
|
+
const segments = fieldPath.split(".").filter(Boolean);
|
|
27
|
+
let current = value;
|
|
28
|
+
for (const segment of segments) {
|
|
29
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
30
|
+
throw new Error(`Secret field not found: ${fieldPath}`);
|
|
31
|
+
}
|
|
32
|
+
current = current[segment];
|
|
33
|
+
}
|
|
34
|
+
if (typeof current !== "string") {
|
|
35
|
+
throw new Error(`Secret field is not a string: ${fieldPath}`);
|
|
36
|
+
}
|
|
37
|
+
return current;
|
|
38
|
+
}
|
|
39
|
+
export function daysUntil(timestamp) {
|
|
40
|
+
if (!timestamp) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return Math.ceil((new Date(timestamp).getTime() - Date.now()) / 86_400_000);
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { extractField, parseRef } from "./reference-utils.js";
|
|
2
|
+
function normalizeTimestamp(value) {
|
|
3
|
+
return typeof value === "string" ? value : undefined;
|
|
4
|
+
}
|
|
5
|
+
export class VaultSecretAdapter {
|
|
6
|
+
addr;
|
|
7
|
+
token;
|
|
8
|
+
namespace;
|
|
9
|
+
id = "vault";
|
|
10
|
+
constructor(addr, token, namespace) {
|
|
11
|
+
this.addr = addr;
|
|
12
|
+
this.token = token;
|
|
13
|
+
this.namespace = namespace;
|
|
14
|
+
}
|
|
15
|
+
headers() {
|
|
16
|
+
if (!this.addr || !this.token) {
|
|
17
|
+
throw new Error("Vault adapter requires KEYLORE_VAULT_ADDR and KEYLORE_VAULT_TOKEN.");
|
|
18
|
+
}
|
|
19
|
+
const headers = new Headers({
|
|
20
|
+
"x-vault-token": this.token,
|
|
21
|
+
});
|
|
22
|
+
if (this.namespace) {
|
|
23
|
+
headers.set("x-vault-namespace", this.namespace);
|
|
24
|
+
}
|
|
25
|
+
return headers;
|
|
26
|
+
}
|
|
27
|
+
async read(credential) {
|
|
28
|
+
const parsed = parseRef(credential.binding.ref);
|
|
29
|
+
const version = parsed.query.get("version");
|
|
30
|
+
const target = `${this.addr}/v1/${parsed.resource.replace(/^\//, "")}`;
|
|
31
|
+
const url = version ? `${target}?version=${encodeURIComponent(version)}` : target;
|
|
32
|
+
const response = await fetch(url, {
|
|
33
|
+
headers: this.headers(),
|
|
34
|
+
});
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
throw new Error(`Vault request failed with ${response.status} for ${credential.binding.ref}.`);
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
parsed,
|
|
40
|
+
payload: (await response.json()),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async resolve(credential) {
|
|
44
|
+
const { parsed, payload } = await this.read(credential);
|
|
45
|
+
const secret = extractField(payload.data?.data, parsed.field);
|
|
46
|
+
return {
|
|
47
|
+
secret,
|
|
48
|
+
headerName: credential.binding.headerName,
|
|
49
|
+
headerValue: credential.binding.authType === "bearer"
|
|
50
|
+
? `${credential.binding.headerPrefix ?? "Bearer "}${secret}`
|
|
51
|
+
: secret,
|
|
52
|
+
inspection: await this.inspect(credential),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
async inspect(credential) {
|
|
56
|
+
const { payload } = await this.read(credential);
|
|
57
|
+
const metadata = payload.data?.metadata ?? {};
|
|
58
|
+
return {
|
|
59
|
+
adapter: this.id,
|
|
60
|
+
ref: credential.binding.ref,
|
|
61
|
+
status: "ok",
|
|
62
|
+
resolved: true,
|
|
63
|
+
version: typeof metadata.version === "number"
|
|
64
|
+
? String(metadata.version)
|
|
65
|
+
: typeof metadata.version === "string"
|
|
66
|
+
? metadata.version
|
|
67
|
+
: undefined,
|
|
68
|
+
createdAt: normalizeTimestamp(metadata.created_time),
|
|
69
|
+
expiresAt: normalizeTimestamp(metadata.deletion_time),
|
|
70
|
+
state: metadata.destroyed === true ? "destroyed" : "active",
|
|
71
|
+
notes: ["Vault inspection reads KV v2 metadata from the bound secret path."],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async healthcheck() {
|
|
75
|
+
if (!this.addr || !this.token) {
|
|
76
|
+
return {
|
|
77
|
+
adapter: this.id,
|
|
78
|
+
available: false,
|
|
79
|
+
status: "error",
|
|
80
|
+
details: "Vault adapter is not configured.",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const response = await fetch(`${this.addr}/v1/sys/health?standbyok=true&perfstandbyok=true`, {
|
|
85
|
+
headers: this.headers(),
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
adapter: this.id,
|
|
89
|
+
available: response.ok,
|
|
90
|
+
status: response.ok ? "ok" : "error",
|
|
91
|
+
details: response.ok ? "Vault health endpoint responded." : `Vault health returned ${response.status}.`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
return {
|
|
96
|
+
adapter: this.id,
|
|
97
|
+
available: false,
|
|
98
|
+
status: "error",
|
|
99
|
+
details: error instanceof Error ? error.message : "Vault healthcheck failed.",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import pino from "pino";
|
|
2
|
+
import { SecretAdapterRegistry } from "./adapters/adapter-registry.js";
|
|
3
|
+
import { AwsSecretsManagerAdapter } from "./adapters/aws-secrets-manager-adapter.js";
|
|
4
|
+
import { ExecFileCommandRunner } from "./adapters/command-runner.js";
|
|
5
|
+
import { EnvSecretAdapter } from "./adapters/env-secret-adapter.js";
|
|
6
|
+
import { LocalSecretAdapter } from "./adapters/local-secret-adapter.js";
|
|
7
|
+
import { GcpSecretManagerAdapter } from "./adapters/gcp-secret-manager-adapter.js";
|
|
8
|
+
import { OnePasswordSecretAdapter } from "./adapters/onepassword-secret-adapter.js";
|
|
9
|
+
import { VaultSecretAdapter } from "./adapters/vault-secret-adapter.js";
|
|
10
|
+
import { loadConfig } from "./config.js";
|
|
11
|
+
import { PgAccessTokenRepository } from "./repositories/pg-access-token-repository.js";
|
|
12
|
+
import { PgAuthorizationCodeRepository } from "./repositories/pg-authorization-code-repository.js";
|
|
13
|
+
import { PgApprovalRepository } from "./repositories/pg-approval-repository.js";
|
|
14
|
+
import { PgAuditLogService } from "./repositories/pg-audit-log.js";
|
|
15
|
+
import { PgAuthClientRepository } from "./repositories/pg-auth-client-repository.js";
|
|
16
|
+
import { PgBreakGlassRepository } from "./repositories/pg-break-glass-repository.js";
|
|
17
|
+
import { PgCredentialRepository } from "./repositories/pg-credential-repository.js";
|
|
18
|
+
import { PgOAuthClientAssertionRepository } from "./repositories/pg-oauth-client-assertion-repository.js";
|
|
19
|
+
import { PgPolicyRepository } from "./repositories/pg-policy-repository.js";
|
|
20
|
+
import { PgRefreshTokenRepository } from "./repositories/pg-refresh-token-repository.js";
|
|
21
|
+
import { PgRotationRunRepository } from "./repositories/pg-rotation-run-repository.js";
|
|
22
|
+
import { PgTenantRepository } from "./repositories/pg-tenant-repository.js";
|
|
23
|
+
import { ApprovalService } from "./services/approval-service.js";
|
|
24
|
+
import { AuthService } from "./services/auth-service.js";
|
|
25
|
+
import { BackupService } from "./services/backup-service.js";
|
|
26
|
+
import { BreakGlassService } from "./services/break-glass-service.js";
|
|
27
|
+
import { BrokerService } from "./services/broker-service.js";
|
|
28
|
+
import { CoreModeService } from "./services/core-mode-service.js";
|
|
29
|
+
import { validateEgressTarget } from "./services/egress-policy.js";
|
|
30
|
+
import { LocalSecretStore } from "./services/local-secret-store.js";
|
|
31
|
+
import { MaintenanceService } from "./services/maintenance-service.js";
|
|
32
|
+
import { NotificationService } from "./services/notification-service.js";
|
|
33
|
+
import { PolicyEngine } from "./services/policy-engine.js";
|
|
34
|
+
import { PgRateLimitService } from "./services/rate-limit-service.js";
|
|
35
|
+
import { RotationService } from "./services/rotation-service.js";
|
|
36
|
+
import { TelemetryService } from "./services/telemetry.js";
|
|
37
|
+
import { TenantService } from "./services/tenant-service.js";
|
|
38
|
+
import { TraceExportService } from "./services/trace-export-service.js";
|
|
39
|
+
import { TraceService } from "./services/trace-service.js";
|
|
40
|
+
import { bootstrapFromFiles } from "./storage/bootstrap.js";
|
|
41
|
+
import { createPostgresDatabase } from "./storage/database.js";
|
|
42
|
+
import { runMigrations } from "./storage/migrations.js";
|
|
43
|
+
import { SandboxRunner } from "./runtime/sandbox-runner.js";
|
|
44
|
+
export async function createKeyLoreApp() {
|
|
45
|
+
const config = loadConfig();
|
|
46
|
+
const logger = pino({ name: config.appName, level: config.logLevel });
|
|
47
|
+
const database = createPostgresDatabase(config);
|
|
48
|
+
const telemetry = new TelemetryService();
|
|
49
|
+
const traces = new TraceService(config.traceCaptureEnabled, config.traceRecentSpanLimit);
|
|
50
|
+
const traceExports = new TraceExportService(config.traceExportUrl, config.traceExportAuthHeader, config.traceExportBatchSize, config.traceExportIntervalMs, config.traceExportTimeoutMs, telemetry);
|
|
51
|
+
traces.attachExporter(traceExports);
|
|
52
|
+
await database.healthcheck();
|
|
53
|
+
await runMigrations(database, config.migrationsDir);
|
|
54
|
+
const credentialRepository = new PgCredentialRepository(database);
|
|
55
|
+
const policyRepository = new PgPolicyRepository(database);
|
|
56
|
+
const authClientRepository = new PgAuthClientRepository(database);
|
|
57
|
+
const tenantRepository = new PgTenantRepository(database);
|
|
58
|
+
const assertionRepository = new PgOAuthClientAssertionRepository(database);
|
|
59
|
+
const audit = new PgAuditLogService(database);
|
|
60
|
+
const accessTokens = new PgAccessTokenRepository(database);
|
|
61
|
+
const refreshTokens = new PgRefreshTokenRepository(database);
|
|
62
|
+
const authorizationCodes = new PgAuthorizationCodeRepository(database);
|
|
63
|
+
const approvals = new PgApprovalRepository(database);
|
|
64
|
+
const breakGlassRepository = new PgBreakGlassRepository(database);
|
|
65
|
+
const rotationRuns = new PgRotationRunRepository(database);
|
|
66
|
+
const notificationService = new NotificationService(config.notificationWebhookUrl, config.notificationSigningSecret, config.notificationTimeoutMs, audit, telemetry, traces);
|
|
67
|
+
const rateLimits = new PgRateLimitService(database, config.rateLimitWindowMs, config.rateLimitMaxRequests, telemetry);
|
|
68
|
+
const localSecrets = new LocalSecretStore(config.localSecretsFilePath, config.localSecretsKeyPath);
|
|
69
|
+
const commandRunner = new ExecFileCommandRunner();
|
|
70
|
+
const adapterRegistry = new SecretAdapterRegistry([
|
|
71
|
+
new LocalSecretAdapter(localSecrets),
|
|
72
|
+
new EnvSecretAdapter(),
|
|
73
|
+
new VaultSecretAdapter(config.vaultAddr, config.vaultToken, config.vaultNamespace),
|
|
74
|
+
new OnePasswordSecretAdapter(commandRunner, config.opBinary),
|
|
75
|
+
new AwsSecretsManagerAdapter(commandRunner, config.awsBinary),
|
|
76
|
+
new GcpSecretManagerAdapter(commandRunner, config.gcloudBinary),
|
|
77
|
+
], config, telemetry);
|
|
78
|
+
await credentialRepository.ensureInitialized();
|
|
79
|
+
await policyRepository.ensureInitialized();
|
|
80
|
+
await authClientRepository.ensureInitialized();
|
|
81
|
+
await tenantRepository.ensureInitialized();
|
|
82
|
+
const authService = new AuthService(authClientRepository, accessTokens, refreshTokens, authorizationCodes, assertionRepository, tenantRepository, audit, config.oauthIssuerUrl, config.publicBaseUrl, config.accessTokenTtlSeconds, config.authorizationCodeTtlSeconds, config.refreshTokenTtlSeconds, telemetry);
|
|
83
|
+
const tenantService = new TenantService(tenantRepository, database, audit, authService);
|
|
84
|
+
const approvalService = new ApprovalService(approvals, audit, config.approvalTtlSeconds, config.approvalReviewQuorum, notificationService, traces);
|
|
85
|
+
const breakGlassService = new BreakGlassService(breakGlassRepository, audit, config.breakGlassMaxDurationSeconds, config.breakGlassReviewQuorum, notificationService, traces);
|
|
86
|
+
const rotationService = new RotationService(rotationRuns, credentialRepository, adapterRegistry, audit, notificationService, traces, config.rotationPlanningHorizonDays);
|
|
87
|
+
if (config.bootstrapFromFiles) {
|
|
88
|
+
await bootstrapFromFiles(credentialRepository, policyRepository, authClientRepository, tenantRepository, config.bootstrapCatalogPath, config.bootstrapPolicyPath, config.bootstrapAuthClientsPath);
|
|
89
|
+
}
|
|
90
|
+
const broker = new BrokerService(credentialRepository, policyRepository, audit, adapterRegistry, new PolicyEngine(), approvalService, breakGlassService, tenantRepository, new SandboxRunner(config), validateEgressTarget, config);
|
|
91
|
+
const coreMode = new CoreModeService(broker, policyRepository, localSecrets, config.defaultPrincipal);
|
|
92
|
+
await coreMode.reconcileLocalCredentialPolicies();
|
|
93
|
+
const maintenance = new MaintenanceService(config.maintenanceEnabled, config.maintenanceIntervalMs, approvals, breakGlassRepository, accessTokens, refreshTokens, rateLimits, authorizationCodes, assertionRepository, telemetry);
|
|
94
|
+
traceExports.start();
|
|
95
|
+
maintenance.start();
|
|
96
|
+
const backup = new BackupService(database, config.version, audit);
|
|
97
|
+
return {
|
|
98
|
+
config,
|
|
99
|
+
logger,
|
|
100
|
+
broker,
|
|
101
|
+
auth: authService,
|
|
102
|
+
tenants: tenantService,
|
|
103
|
+
rotations: rotationService,
|
|
104
|
+
approvals: approvalService,
|
|
105
|
+
breakGlass: breakGlassService,
|
|
106
|
+
coreMode,
|
|
107
|
+
database,
|
|
108
|
+
telemetry,
|
|
109
|
+
traces,
|
|
110
|
+
traceExports,
|
|
111
|
+
rateLimits,
|
|
112
|
+
maintenance,
|
|
113
|
+
backup,
|
|
114
|
+
health: {
|
|
115
|
+
readiness: async () => {
|
|
116
|
+
await database.healthcheck();
|
|
117
|
+
const credentialCount = await broker.countCredentials();
|
|
118
|
+
return {
|
|
119
|
+
status: "ready",
|
|
120
|
+
environment: config.environment,
|
|
121
|
+
credentialCount,
|
|
122
|
+
maintenance: maintenance.status(),
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
close: async () => {
|
|
127
|
+
await traceExports.stop();
|
|
128
|
+
await maintenance.stop();
|
|
129
|
+
await database.close();
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
package/dist/cli/args.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
function normalizeFlagName(flag) {
|
|
2
|
+
return flag.replace(/^--/, "");
|
|
3
|
+
}
|
|
4
|
+
export function parseCliArgs(argv) {
|
|
5
|
+
const positionals = [];
|
|
6
|
+
const flags = new Map();
|
|
7
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
8
|
+
const token = argv[index];
|
|
9
|
+
if (token === undefined) {
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
if (!token.startsWith("--")) {
|
|
13
|
+
positionals.push(token);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const trimmed = normalizeFlagName(token);
|
|
17
|
+
if (trimmed.includes("=")) {
|
|
18
|
+
const equalsIndex = trimmed.indexOf("=");
|
|
19
|
+
const name = trimmed.slice(0, equalsIndex);
|
|
20
|
+
const value = trimmed.slice(equalsIndex + 1);
|
|
21
|
+
flags.set(name, value);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const next = argv[index + 1];
|
|
25
|
+
if (!next || next.startsWith("--")) {
|
|
26
|
+
flags.set(trimmed, true);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
flags.set(trimmed, next);
|
|
30
|
+
index += 1;
|
|
31
|
+
}
|
|
32
|
+
return { positionals, flags };
|
|
33
|
+
}
|
|
34
|
+
export function readStringFlag(flags, name) {
|
|
35
|
+
const value = flags.get(name);
|
|
36
|
+
return typeof value === "string" ? value : undefined;
|
|
37
|
+
}
|
|
38
|
+
export function readBooleanFlag(flags, name) {
|
|
39
|
+
return flags.get(name) === true;
|
|
40
|
+
}
|
|
41
|
+
export function readNumberFlag(flags, name) {
|
|
42
|
+
const value = readStringFlag(flags, name);
|
|
43
|
+
if (!value) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
const parsed = Number.parseInt(value, 10);
|
|
47
|
+
if (Number.isNaN(parsed)) {
|
|
48
|
+
throw new Error(`Flag --${name} must be an integer.`);
|
|
49
|
+
}
|
|
50
|
+
return parsed;
|
|
51
|
+
}
|