@seekrit/cli 0.46.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +373 -154
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2481,7 +2481,8 @@ const SYNC_PROVIDER_KINDS = [
|
|
|
2481
2481
|
"bunnyshell",
|
|
2482
2482
|
"github-actions",
|
|
2483
2483
|
"gcp-secret-manager",
|
|
2484
|
-
"langgraph-platform"
|
|
2484
|
+
"langgraph-platform",
|
|
2485
|
+
"azure-key-vault"
|
|
2485
2486
|
];
|
|
2486
2487
|
z.enum(SYNC_PROVIDER_KINDS);
|
|
2487
2488
|
/**
|
|
@@ -2822,6 +2823,54 @@ const langgraphPlatformConnectionConfigSchema = z.object({
|
|
|
2822
2823
|
message: "set region for a LangChain-hosted account or baseUrl for a self-hosted one, not both",
|
|
2823
2824
|
path: ["baseUrl"]
|
|
2824
2825
|
});
|
|
2826
|
+
/**
|
|
2827
|
+
* The Azure clouds a vault can live in.
|
|
2828
|
+
*
|
|
2829
|
+
* Unlike AWS, where the China partition can be read off the region string
|
|
2830
|
+
* (`cn-…`), nothing about a tenant id or a vault name says which cloud it
|
|
2831
|
+
* belongs to — and two hosts have to agree with the answer: the Entra authority
|
|
2832
|
+
* that issues the token and the DNS suffix the vault answers on. Getting either
|
|
2833
|
+
* wrong is a failure inside an alarm with nobody watching, so it is stated.
|
|
2834
|
+
*/
|
|
2835
|
+
const AZURE_CLOUDS = [
|
|
2836
|
+
"public",
|
|
2837
|
+
"usgov",
|
|
2838
|
+
"china"
|
|
2839
|
+
];
|
|
2840
|
+
/**
|
|
2841
|
+
* A Microsoft Entra directory (tenant) or application (client) id.
|
|
2842
|
+
*
|
|
2843
|
+
* Both are GUIDs. Entra accepts a verified domain name in place of a tenant id
|
|
2844
|
+
* in the token URL, but not a client id, and taking only the GUID for both
|
|
2845
|
+
* keeps one rule — a tenant's GUID is on the same admin-center page as the
|
|
2846
|
+
* client id it pairs with, so nothing is harder to find.
|
|
2847
|
+
*/
|
|
2848
|
+
const azureGuidSchema = z.string().trim().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, "must be a GUID, as the Entra admin center shows it");
|
|
2849
|
+
/**
|
|
2850
|
+
* Azure account scope: which directory to authenticate against, as which app.
|
|
2851
|
+
*
|
|
2852
|
+
* The split follows AWS's, and for the same reason. A service principal's
|
|
2853
|
+
* **client secret** is the credential and is wrapped to the connection's key;
|
|
2854
|
+
* the tenant and client ids are *identifiers* — they appear in the token
|
|
2855
|
+
* request URL, in sign-in logs, and on the app registration blade. Keeping them
|
|
2856
|
+
* here lets the dashboard say which principal a connection authenticates as,
|
|
2857
|
+
* which is the first thing worth knowing when a connection starts failing after
|
|
2858
|
+
* a secret expires.
|
|
2859
|
+
*
|
|
2860
|
+
* Client secrets are the only credential kind here: a certificate or federated
|
|
2861
|
+
* credential would need a private key or a trust relationship the sync engine
|
|
2862
|
+
* has nowhere to keep, and Entra caps a client secret at 24 months, which is a
|
|
2863
|
+
* rotation the connection's `lastError` will make loud.
|
|
2864
|
+
*/
|
|
2865
|
+
const azureKeyVaultConnectionConfigSchema = z.object({
|
|
2866
|
+
provider: z.literal("azure-key-vault"),
|
|
2867
|
+
/** Entra directory (tenant) ID. */
|
|
2868
|
+
tenantId: azureGuidSchema,
|
|
2869
|
+
/** Application (client) ID of the service principal seekrit signs in as. */
|
|
2870
|
+
clientId: azureGuidSchema,
|
|
2871
|
+
/** Which Azure cloud the tenant and its vaults live in. */
|
|
2872
|
+
cloud: z.enum(AZURE_CLOUDS).default("public")
|
|
2873
|
+
});
|
|
2825
2874
|
const syncConnectionConfigSchema = z.discriminatedUnion("provider", [
|
|
2826
2875
|
vercelConnectionConfigSchema,
|
|
2827
2876
|
cloudflareWorkersConnectionConfigSchema,
|
|
@@ -2839,7 +2888,8 @@ const syncConnectionConfigSchema = z.discriminatedUnion("provider", [
|
|
|
2839
2888
|
bunnyshellConnectionConfigSchema,
|
|
2840
2889
|
githubActionsConnectionConfigSchema,
|
|
2841
2890
|
gcpSecretManagerConnectionConfigSchema,
|
|
2842
|
-
langgraphPlatformConnectionConfigSchema
|
|
2891
|
+
langgraphPlatformConnectionConfigSchema,
|
|
2892
|
+
azureKeyVaultConnectionConfigSchema
|
|
2843
2893
|
]);
|
|
2844
2894
|
/** Vercel's three deployment targets. A binding writes to one or more. */
|
|
2845
2895
|
const VERCEL_TARGETS = [
|
|
@@ -3571,6 +3621,65 @@ const langgraphPlatformDestinationSchema = z.object({
|
|
|
3571
3621
|
/** Deployment UUID, from the dashboard URL or `GET /v2/deployments`. */
|
|
3572
3622
|
deploymentId: z.string().trim().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, "must be a LangGraph Platform deployment UUID")
|
|
3573
3623
|
});
|
|
3624
|
+
/**
|
|
3625
|
+
* A Key Vault's name — the leftmost label of its DNS name, so the vault
|
|
3626
|
+
* `acme-prod` answers at `acme-prod.vault.azure.net`.
|
|
3627
|
+
*
|
|
3628
|
+
* Azure's own rule, stated here because the failure it prevents is confusing:
|
|
3629
|
+
* 3–24 characters, alphanumerics and hyphens, starting with a letter, ending
|
|
3630
|
+
* with a letter or digit, and no run of two hyphens. A name that breaks it
|
|
3631
|
+
* cannot exist, so a typo is a DNS failure rather than a 404 — an error that
|
|
3632
|
+
* says nothing about what was wrong.
|
|
3633
|
+
*
|
|
3634
|
+
* The *base URL* is not taken instead. It would let a connection be pointed at
|
|
3635
|
+
* any host, and the whole address seekrit needs is this label plus the cloud
|
|
3636
|
+
* already named on the connection.
|
|
3637
|
+
*/
|
|
3638
|
+
const azureVaultNameSchema = z.string().trim().regex(/^[A-Za-z](?!.*--)[A-Za-z0-9-]{1,22}[A-Za-z0-9]$/, "must be a Key Vault name: 3–24 letters, digits, and single hyphens, starting with a letter");
|
|
3639
|
+
/**
|
|
3640
|
+
* What to do with a name Key Vault cannot store.
|
|
3641
|
+
*
|
|
3642
|
+
* Key Vault secret names are `^[0-9a-zA-Z-]+$` — **no underscores** — and
|
|
3643
|
+
* `DATABASE_URL` is what a secret is actually called nearly everywhere. So this
|
|
3644
|
+
* is not an edge case to fail on: it is the common case, and a connector that
|
|
3645
|
+
* rejected it would push nothing at all.
|
|
3646
|
+
*
|
|
3647
|
+
* - `dash` — `_` becomes `-`, so `DATABASE_URL` is stored as `DATABASE-URL`.
|
|
3648
|
+
* The default, and what every other tool bridging this gap does. The rename
|
|
3649
|
+
* is visible: the dashboard and the run ledger both show it.
|
|
3650
|
+
* - `reject` — fail each such name instead, for an operator who would rather
|
|
3651
|
+
* name every secret explicitly with the binding's `rename` than have seekrit
|
|
3652
|
+
* choose. Nothing is renamed silently under either setting; this one just
|
|
3653
|
+
* refuses rather than translating.
|
|
3654
|
+
*
|
|
3655
|
+
* Translating can make two seekrit names collide (`A_B` and `A-B` both give
|
|
3656
|
+
* `A-B`). That is caught per run and fails *both* names rather than letting
|
|
3657
|
+
* whichever sorts last win — the same rule, and the same reasoning, as
|
|
3658
|
+
* {@link mapSecretNames}.
|
|
3659
|
+
*/
|
|
3660
|
+
const AZURE_KEY_VAULT_NAME_MODES = ["dash", "reject"];
|
|
3661
|
+
/**
|
|
3662
|
+
* Which vault a binding writes to, and under what names.
|
|
3663
|
+
*
|
|
3664
|
+
* There is no layout choice as Secrets Manager has: the reason a `json-bundle`
|
|
3665
|
+
* exists there is billing — AWS charges per secret per month — and Key Vault
|
|
3666
|
+
* charges per *operation*, so fifty names cost the same stored fifty ways. One
|
|
3667
|
+
* secret per name is simply correct here.
|
|
3668
|
+
*/
|
|
3669
|
+
const azureKeyVaultDestinationSchema = z.object({
|
|
3670
|
+
provider: z.literal("azure-key-vault"),
|
|
3671
|
+
/** Vault name, e.g. `acme-prod` for `acme-prod.vault.azure.net`. */
|
|
3672
|
+
vault: azureVaultNameSchema,
|
|
3673
|
+
/**
|
|
3674
|
+
* Prepended to every secret name, e.g. `storefront-`. Key Vault has no
|
|
3675
|
+
* hierarchy — its names are flat, and `/` is not among the characters it
|
|
3676
|
+
* accepts — so unlike Parameter Store's `path` this is a naming convention
|
|
3677
|
+
* and nothing more. Worth setting in a vault that holds anything else.
|
|
3678
|
+
*/
|
|
3679
|
+
prefix: z.string().trim().max(64).regex(/^[A-Za-z0-9-]*$/, "may contain letters, digits, and hyphens").optional(),
|
|
3680
|
+
/** What to do with a name Key Vault cannot store — see {@link AZURE_KEY_VAULT_NAME_MODES}. */
|
|
3681
|
+
nameMode: z.enum(AZURE_KEY_VAULT_NAME_MODES).default("dash")
|
|
3682
|
+
});
|
|
3574
3683
|
const syncDestinationSchema = z.discriminatedUnion("provider", [
|
|
3575
3684
|
vercelDestinationSchema,
|
|
3576
3685
|
cloudflareWorkersDestinationSchema,
|
|
@@ -3588,7 +3697,8 @@ const syncDestinationSchema = z.discriminatedUnion("provider", [
|
|
|
3588
3697
|
bunnyshellDestinationSchema,
|
|
3589
3698
|
githubActionsDestinationSchema,
|
|
3590
3699
|
gcpSecretManagerDestinationSchema,
|
|
3591
|
-
langgraphPlatformDestinationSchema
|
|
3700
|
+
langgraphPlatformDestinationSchema,
|
|
3701
|
+
azureKeyVaultDestinationSchema
|
|
3592
3702
|
]);
|
|
3593
3703
|
/**
|
|
3594
3704
|
* How seekrit secret names become destination key names. Applied in order:
|
|
@@ -4876,7 +4986,7 @@ async function createAgentTaskToken() {
|
|
|
4876
4986
|
}
|
|
4877
4987
|
//#endregion
|
|
4878
4988
|
//#region package.json
|
|
4879
|
-
var version = "0.
|
|
4989
|
+
var version = "1.0.0";
|
|
4880
4990
|
//#endregion
|
|
4881
4991
|
//#region ../../packages/api-client/src/index.ts
|
|
4882
4992
|
var SeekritApiError = class extends Error {
|
|
@@ -5655,7 +5765,18 @@ function parseDurationSeconds(input, flag) {
|
|
|
5655
5765
|
d: 86400
|
|
5656
5766
|
}[m[2] || "s"] ?? 1);
|
|
5657
5767
|
}
|
|
5658
|
-
/**
|
|
5768
|
+
/**
|
|
5769
|
+
* Prompt without echoing input (for passphrases).
|
|
5770
|
+
*
|
|
5771
|
+
* Piping the answer in (`echo … | seekrit secrets get …`) is supported and
|
|
5772
|
+
* common in CI, so this reads stdin rather than insisting on a TTY. But stdin
|
|
5773
|
+
* can also close with nothing on it — `< /dev/null`, a closed pipe, an agent
|
|
5774
|
+
* spawning us with no stdin — and readline signals that by emitting `close`
|
|
5775
|
+
* without ever calling the `question` callback. Left unhandled the promise
|
|
5776
|
+
* never settles, the event loop drains, and Node exits **0** having printed
|
|
5777
|
+
* nothing: `V=$(seekrit secrets get X)` silently yields an empty value and a
|
|
5778
|
+
* success status. So treat EOF-without-an-answer as the error it is.
|
|
5779
|
+
*/
|
|
5659
5780
|
function promptHidden(question) {
|
|
5660
5781
|
const muted = new Writable({ write(_chunk, _encoding, callback) {
|
|
5661
5782
|
callback();
|
|
@@ -5666,8 +5787,15 @@ function promptHidden(question) {
|
|
|
5666
5787
|
output: muted,
|
|
5667
5788
|
terminal: true
|
|
5668
5789
|
});
|
|
5669
|
-
return new Promise((resolve) => {
|
|
5790
|
+
return new Promise((resolve, reject) => {
|
|
5791
|
+
let answered = false;
|
|
5792
|
+
rl.on("close", () => {
|
|
5793
|
+
if (answered) return;
|
|
5794
|
+
process.stderr.write("\n");
|
|
5795
|
+
reject(/* @__PURE__ */ new Error("no passphrase on stdin — set SEEKRIT_PASSPHRASE, pipe it in, or run this in a terminal"));
|
|
5796
|
+
});
|
|
5670
5797
|
rl.question("", (answer) => {
|
|
5798
|
+
answered = true;
|
|
5671
5799
|
rl.close();
|
|
5672
5800
|
process.stderr.write("\n");
|
|
5673
5801
|
resolve(answer);
|
|
@@ -5743,9 +5871,23 @@ const CLI_CLIENT = `cli/${version}`;
|
|
|
5743
5871
|
* `flag > env > .env` credential resolution. Empty for every command but
|
|
5744
5872
|
* `seekrit run`, which loads `.env` before authenticating.
|
|
5745
5873
|
*/
|
|
5874
|
+
/**
|
|
5875
|
+
* A `SEEKRIT_*` value, or undefined if it is absent *or blank*.
|
|
5876
|
+
*
|
|
5877
|
+
* Blank has to mean absent. An unset CI secret, a `${VAR}` that expanded to
|
|
5878
|
+
* nothing, a bare `export SEEKRIT_TOKEN=` — all arrive as `""`, and `??` only
|
|
5879
|
+
* falls back on null/undefined. Left alone, an empty `SEEKRIT_API_URL` makes
|
|
5880
|
+
* every request relative and an empty `SEEKRIT_TOKEN` authenticates as a
|
|
5881
|
+
* bearer of nothing: a 401 where the honest answer is "you have no
|
|
5882
|
+
* credentials", pointing at the API instead of at the missing variable.
|
|
5883
|
+
*/
|
|
5884
|
+
function present(value) {
|
|
5885
|
+
const trimmed = value?.trim();
|
|
5886
|
+
return trimmed ? trimmed : void 0;
|
|
5887
|
+
}
|
|
5746
5888
|
function tryBuildContext(dotenvVars = {}) {
|
|
5747
5889
|
const config = readGlobalConfig();
|
|
5748
|
-
const fromEnv = (key) => process.env[key] ?? dotenvVars[key];
|
|
5890
|
+
const fromEnv = (key) => present(process.env[key]) ?? present(dotenvVars[key]);
|
|
5749
5891
|
const apiUrl = fromEnv("SEEKRIT_API_URL") ?? config.apiUrl ?? "https://api.seekrit.dev";
|
|
5750
5892
|
const token = fromEnv("SEEKRIT_TOKEN") ?? config.token ?? config.sessionToken;
|
|
5751
5893
|
const devUser = fromEnv("SEEKRIT_DEV_USER") ?? config.devUser;
|
|
@@ -5778,6 +5920,24 @@ function isTokenAuth(ctx) {
|
|
|
5778
5920
|
return ctx.auth.type === "bearer" && isServiceToken(ctx.auth.token);
|
|
5779
5921
|
}
|
|
5780
5922
|
/**
|
|
5923
|
+
* Render an unhandled error for the top-level handler.
|
|
5924
|
+
*
|
|
5925
|
+
* `fetch` reports every transport failure as the same opaque `TypeError: fetch
|
|
5926
|
+
* failed` and hides the reason (ECONNREFUSED, DNS, TLS) in `cause`. That is the
|
|
5927
|
+
* single most common thing to go wrong — a stale `SEEKRIT_API_URL`, a dev
|
|
5928
|
+
* server that isn't up, a VPN that is down — and "error: fetch failed" names
|
|
5929
|
+
* neither the address we tried nor why it failed. So unwrap it and say both.
|
|
5930
|
+
*/
|
|
5931
|
+
function describeError(err) {
|
|
5932
|
+
if (!(err instanceof Error)) return String(err);
|
|
5933
|
+
if (err.message !== "fetch failed") return err.message;
|
|
5934
|
+
const config = readGlobalConfig();
|
|
5935
|
+
const apiUrl = present(process.env.SEEKRIT_API_URL) ?? config.apiUrl ?? "https://api.seekrit.dev";
|
|
5936
|
+
const cause = err.cause;
|
|
5937
|
+
const reason = cause instanceof Error ? cause.code ?? cause.message : void 0;
|
|
5938
|
+
return `cannot reach the seekrit API at ${apiUrl}${reason ? ` (${reason})` : ""} — check SEEKRIT_API_URL, your network, and that the API is up`;
|
|
5939
|
+
}
|
|
5940
|
+
/**
|
|
5781
5941
|
* Recover the calling principal's private key:
|
|
5782
5942
|
* - service tokens carry their private key in the token string;
|
|
5783
5943
|
* - users fetch their passphrase-encrypted key from the API and unlock it.
|
|
@@ -6040,7 +6200,7 @@ async function resolvePrincipal(ctx, orgId, options) {
|
|
|
6040
6200
|
}
|
|
6041
6201
|
/** The `--org/--app/--group/--env` selection every grant command shares. */
|
|
6042
6202
|
function withEnvTarget(cmd) {
|
|
6043
|
-
return cmd.option("--org <slug>").option("--app <slug>", "application slug (defaults to seekrit.json)").option("--group <slug>", "target a group environment instead of an app").requiredOption("--env <slug>");
|
|
6203
|
+
return cmd.option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "application slug (defaults to seekrit.json)").option("--group <slug>", "target a group environment instead of an app").requiredOption("--env <slug>", "environment slug");
|
|
6044
6204
|
}
|
|
6045
6205
|
/**
|
|
6046
6206
|
* Environment key grants — who can decrypt what.
|
|
@@ -6075,7 +6235,7 @@ function registerAccessCommands(program) {
|
|
|
6075
6235
|
col("id", (g) => g.principalId)
|
|
6076
6236
|
], "nobody holds a key for this environment"));
|
|
6077
6237
|
});
|
|
6078
|
-
withEnvTarget(grant.command("rm").alias("revoke").description("take away a member's or token's access to an environment's key").option("--user <email>", "revoke an org member by email").option("--token <tokenId>", "revoke a service token by id (skt_…)").option("--yes", "skip the confirmation prompt")).action(async (options) => {
|
|
6238
|
+
withEnvTarget(grant.command("rm").alias("revoke").description("take away a member's or token's access to an environment's key").option("--user <email>", "revoke an org member by email").option("--token <tokenId>", "revoke a service token by id (skt_…)").option("-y, --yes", "skip the confirmation prompt")).action(async (options) => {
|
|
6079
6239
|
if (!options.user === !options.token) fail("pass exactly one of --user or --token");
|
|
6080
6240
|
const ctx = buildContext();
|
|
6081
6241
|
const { orgId, envId, label } = await resolveEnvTarget(ctx, options);
|
|
@@ -6121,7 +6281,7 @@ function registerAccountCommands(program) {
|
|
|
6121
6281
|
col("id", (s) => `${s.id}${s.id === currentSessionId ? " (this one)" : ""}`)
|
|
6122
6282
|
], options.all ? "no CLI sessions" : "no active CLI sessions (try --all)"));
|
|
6123
6283
|
});
|
|
6124
|
-
session.command("revoke <sessionId>").description("sign a device out — its token stops working immediately").option("--yes", "skip the confirmation prompt").action(async (sessionId, options) => {
|
|
6284
|
+
session.command("revoke <sessionId>").description("sign a device out — its token stops working immediately").option("-y, --yes", "skip the confirmation prompt").action(async (sessionId, options) => {
|
|
6125
6285
|
const ctx = buildContext();
|
|
6126
6286
|
const self = ctx.auth.type === "bearer" && isCliSessionToken(ctx.auth.token) && parseCliSessionToken(ctx.auth.token).sessionId === sessionId;
|
|
6127
6287
|
await confirmDestructive(options.yes, self ? `${sessionId} is the session you are using right now — sign it out?` : `Sign out ${sessionId}?`);
|
|
@@ -6852,7 +7012,7 @@ async function kmsRecoverMaterial(ctx, orgId, keyId, version) {
|
|
|
6852
7012
|
}
|
|
6853
7013
|
function registerKmsCommands(program) {
|
|
6854
7014
|
const kms = program.command("kms").description("managed keys for application-layer encryption & signing (client-side)");
|
|
6855
|
-
kms.command("create").description("create a managed key (material is generated locally and wrapped, never sent)").requiredOption("--name <name>", "org-unique key name").requiredOption("--purpose <purpose>", "encrypt | sign").option("--org <slug>").option("--app <slug>", "scope the key to an application").option("--group <slug>", "scope the key to a group").option("--grant-user <email>", "also grant an org member (repeatable)", collect$7, []).option("--grant-token <tokenId>", "also grant a service token (repeatable)", collect$7, []).action(async (options) => {
|
|
7015
|
+
kms.command("create").description("create a managed key (material is generated locally and wrapped, never sent)").requiredOption("--name <name>", "org-unique key name").requiredOption("--purpose <purpose>", "encrypt | sign").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "scope the key to an application").option("--group <slug>", "scope the key to a group").option("--grant-user <email>", "also grant an org member (repeatable)", collect$7, []).option("--grant-token <tokenId>", "also grant a service token (repeatable)", collect$7, []).action(async (options) => {
|
|
6856
7016
|
if (options.purpose !== "encrypt" && options.purpose !== "sign") fail("--purpose must be encrypt or sign");
|
|
6857
7017
|
if (options.app && options.group) fail("pass at most one of --app or --group");
|
|
6858
7018
|
const ctx = buildContext();
|
|
@@ -6905,7 +7065,7 @@ function registerKmsCommands(program) {
|
|
|
6905
7065
|
const { key } = await ctx.client.createKmsKey(org.id, input);
|
|
6906
7066
|
console.error(`created ${key.purpose} key ${key.name} (${key.id}), ${grants.length} grant(s)`);
|
|
6907
7067
|
});
|
|
6908
|
-
kms.command("ls").description("list keys you can see").option("--org <slug>").action(async (options) => {
|
|
7068
|
+
kms.command("ls").description("list keys you can see").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
6909
7069
|
const ctx = buildContext();
|
|
6910
7070
|
const org = await resolveOrg(ctx, options.org);
|
|
6911
7071
|
const { keys } = await ctx.client.listKmsKeys(org.id);
|
|
@@ -6919,7 +7079,7 @@ function registerKmsCommands(program) {
|
|
|
6919
7079
|
console.log(`${k.name}\t${k.purpose}\tv${k.currentVersion}\t${scope}\t${k.id}${state}`);
|
|
6920
7080
|
}
|
|
6921
7081
|
});
|
|
6922
|
-
kms.command("grant").description("grant a principal use of a key's current version").requiredOption("--key <name>", "key name or id").option("--org <slug>").option("--user <email>", "grant an org member").option("--token <tokenId>", "grant a service token").action(async (options) => {
|
|
7082
|
+
kms.command("grant").description("grant a principal use of a key's current version").requiredOption("--key <name>", "key name or id").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--user <email>", "grant an org member").option("--token <tokenId>", "grant a service token").action(async (options) => {
|
|
6923
7083
|
if (!options.user === !options.token) fail("pass exactly one of --user or --token");
|
|
6924
7084
|
const ctx = buildContext();
|
|
6925
7085
|
const org = await resolveOrg(ctx, options.org);
|
|
@@ -6933,19 +7093,20 @@ function registerKmsCommands(program) {
|
|
|
6933
7093
|
});
|
|
6934
7094
|
console.error(`granted ${key.name} to ${recipient.principalId}`);
|
|
6935
7095
|
});
|
|
6936
|
-
kms.command("revoke").description("revoke a principal from a key (all versions)").requiredOption("--key <name>", "key name or id").option("--org <slug>").option("--user <email>").option("--token <tokenId>").action(async (options) => {
|
|
7096
|
+
kms.command("revoke").description("revoke a principal from a key (all versions)").requiredOption("--key <name>", "key name or id").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--user <email>", "revoke this user's grant").option("--token <tokenId>", "revoke this service token's grant").option("-y, --yes", "skip the confirmation").action(async (options) => {
|
|
6937
7097
|
if (!options.user === !options.token) fail("pass exactly one of --user or --token");
|
|
6938
7098
|
const ctx = buildContext();
|
|
6939
7099
|
const org = await resolveOrg(ctx, options.org);
|
|
6940
7100
|
const key = await kmsResolveKey(ctx, org.id, options.key);
|
|
6941
7101
|
const recipient = await kmsResolveRecipient(ctx, org.id, options);
|
|
7102
|
+
await confirmDestructive(options.yes, `Revoke ${recipient.principalId} from ${key.name}? Anything already decrypted stays decrypted.`);
|
|
6942
7103
|
await ctx.client.revokeKmsKey(org.id, key.id, {
|
|
6943
7104
|
principalType: recipient.principalType,
|
|
6944
7105
|
principalId: recipient.principalId
|
|
6945
7106
|
});
|
|
6946
7107
|
console.error(`revoked ${recipient.principalId} from ${key.name}`);
|
|
6947
7108
|
});
|
|
6948
|
-
kms.command("rotate").description("add a new key version and re-wrap it for every current grantee").requiredOption("--key <name>", "key name or id").option("--org <slug>").action(async (options) => {
|
|
7109
|
+
kms.command("rotate").description("add a new key version and re-wrap it for every current grantee").requiredOption("--key <name>", "key name or id").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
6949
7110
|
const ctx = buildContext();
|
|
6950
7111
|
const org = await resolveOrg(ctx, options.org);
|
|
6951
7112
|
const key = await kmsResolveKey(ctx, org.id, options.key);
|
|
@@ -6979,21 +7140,23 @@ function registerKmsCommands(program) {
|
|
|
6979
7140
|
});
|
|
6980
7141
|
console.error(`rotated ${rotated.name} to v${rotated.currentVersion} (${grants.length} grantees)`);
|
|
6981
7142
|
});
|
|
6982
|
-
kms.command("disable").description("disable a key (blocks all use: encrypt/decrypt/sign and new grants/rotations)").requiredOption("--key <name>", "key name or id").option("--org <slug>").action(async (options) => {
|
|
7143
|
+
kms.command("disable").description("disable a key (blocks all use: encrypt/decrypt/sign and new grants/rotations)").requiredOption("--key <name>", "key name or id").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (options) => {
|
|
6983
7144
|
const ctx = buildContext();
|
|
6984
7145
|
const org = await resolveOrg(ctx, options.org);
|
|
6985
7146
|
const key = await kmsResolveKey(ctx, org.id, options.key);
|
|
7147
|
+
await confirmDestructive(options.yes, `Disable ${key.name}? Every encrypt, decrypt, and sign against it starts failing.`);
|
|
6986
7148
|
await ctx.client.disableKmsKey(org.id, key.id);
|
|
6987
7149
|
console.error(`disabled ${key.name}`);
|
|
6988
7150
|
});
|
|
6989
|
-
kms.command("delete").description("delete a key (hides it from all listings; the name frees up for reuse)").requiredOption("--key <name>", "key name or id").option("--org <slug>").action(async (options) => {
|
|
7151
|
+
kms.command("delete").description("delete a key (hides it from all listings; the name frees up for reuse)").requiredOption("--key <name>", "key name or id").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (options) => {
|
|
6990
7152
|
const ctx = buildContext();
|
|
6991
7153
|
const org = await resolveOrg(ctx, options.org);
|
|
6992
7154
|
const key = await kmsResolveKey(ctx, org.id, options.key);
|
|
7155
|
+
await confirmDestructive(options.yes, `Delete ${key.name}? Any ciphertext or signature still relying on it becomes unrecoverable.`);
|
|
6993
7156
|
await ctx.client.deleteKmsKey(org.id, key.id);
|
|
6994
7157
|
console.error(`deleted ${key.name}`);
|
|
6995
7158
|
});
|
|
6996
|
-
kms.command("encrypt").description("encrypt stdin under a key (prints a ce1 ciphertext blob)").requiredOption("--key <name>", "key name or id").option("--org <slug>").option("--context <ctx>", "encryption context bound as AAD (required identically to decrypt)").action(async (options) => {
|
|
7159
|
+
kms.command("encrypt").description("encrypt stdin under a key (prints a ce1 ciphertext blob)").requiredOption("--key <name>", "key name or id").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--context <ctx>", "encryption context bound as AAD (required identically to decrypt)").action(async (options) => {
|
|
6997
7160
|
const ctx = buildContext();
|
|
6998
7161
|
const org = await resolveOrg(ctx, options.org);
|
|
6999
7162
|
const key = await kmsResolveKey(ctx, org.id, options.key);
|
|
@@ -7006,7 +7169,7 @@ function registerKmsCommands(program) {
|
|
|
7006
7169
|
}, plaintext, options.context ?? "");
|
|
7007
7170
|
console.log(blob);
|
|
7008
7171
|
});
|
|
7009
|
-
kms.command("decrypt").description("decrypt a ce1 blob from stdin").requiredOption("--key <name>", "key name or id").option("--org <slug>").option("--context <ctx>", "the same encryption context used to encrypt").action(async (options) => {
|
|
7172
|
+
kms.command("decrypt").description("decrypt a ce1 blob from stdin").requiredOption("--key <name>", "key name or id").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--context <ctx>", "the same encryption context used to encrypt").action(async (options) => {
|
|
7010
7173
|
const ctx = buildContext();
|
|
7011
7174
|
const org = await resolveOrg(ctx, options.org);
|
|
7012
7175
|
const key = await kmsResolveKey(ctx, org.id, options.key);
|
|
@@ -7015,7 +7178,7 @@ function registerKmsCommands(program) {
|
|
|
7015
7178
|
const { material } = await kmsRecoverMaterial(ctx, org.id, key.id, ref.version);
|
|
7016
7179
|
console.log(await kmsDecrypt(material, blob, options.context ?? ""));
|
|
7017
7180
|
});
|
|
7018
|
-
kms.command("generate-data-key").description("generate a data key: prints JSON {plaintextBase64, wrapped}").requiredOption("--key <name>", "key name or id").option("--org <slug>").action(async (options) => {
|
|
7181
|
+
kms.command("generate-data-key").description("generate a data key: prints JSON {plaintextBase64, wrapped}").requiredOption("--key <name>", "key name or id").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
7019
7182
|
const ctx = buildContext();
|
|
7020
7183
|
const org = await resolveOrg(ctx, options.org);
|
|
7021
7184
|
const key = await kmsResolveKey(ctx, org.id, options.key);
|
|
@@ -7030,7 +7193,7 @@ function registerKmsCommands(program) {
|
|
|
7030
7193
|
wrapped: dk.wrapped
|
|
7031
7194
|
}));
|
|
7032
7195
|
});
|
|
7033
|
-
kms.command("open-data-key").description("recover a data key from a dk1 blob on stdin (prints plaintext base64)").requiredOption("--key <name>", "key name or id").option("--org <slug>").action(async (options) => {
|
|
7196
|
+
kms.command("open-data-key").description("recover a data key from a dk1 blob on stdin (prints plaintext base64)").requiredOption("--key <name>", "key name or id").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
7034
7197
|
const ctx = buildContext();
|
|
7035
7198
|
const org = await resolveOrg(ctx, options.org);
|
|
7036
7199
|
const key = await kmsResolveKey(ctx, org.id, options.key);
|
|
@@ -7039,7 +7202,7 @@ function registerKmsCommands(program) {
|
|
|
7039
7202
|
const { material } = await kmsRecoverMaterial(ctx, org.id, key.id, ref.version);
|
|
7040
7203
|
console.log(toBase64(await decryptDataKey(material, wrapped)));
|
|
7041
7204
|
});
|
|
7042
|
-
kms.command("sign").description("sign stdin with a signing key (prints an sg1 signature)").requiredOption("--key <name>", "key name or id").option("--org <slug>").action(async (options) => {
|
|
7205
|
+
kms.command("sign").description("sign stdin with a signing key (prints an sg1 signature)").requiredOption("--key <name>", "key name or id").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
7043
7206
|
const ctx = buildContext();
|
|
7044
7207
|
const org = await resolveOrg(ctx, options.org);
|
|
7045
7208
|
const key = await kmsResolveKey(ctx, org.id, options.key);
|
|
@@ -7052,7 +7215,7 @@ function registerKmsCommands(program) {
|
|
|
7052
7215
|
version: currentVersion
|
|
7053
7216
|
}, message));
|
|
7054
7217
|
});
|
|
7055
|
-
kms.command("verify").description("verify an sg1 signature over stdin (exit 0 = valid)").requiredOption("--key <name>", "key name or id").requiredOption("--signature <sg1>", "the signature blob").option("--org <slug>").action(async (options) => {
|
|
7218
|
+
kms.command("verify").description("verify an sg1 signature over stdin (exit 0 = valid)").requiredOption("--key <name>", "key name or id").requiredOption("--signature <sg1>", "the signature blob").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
7056
7219
|
const ctx = buildContext();
|
|
7057
7220
|
const org = await resolveOrg(ctx, options.org);
|
|
7058
7221
|
const key = await kmsResolveKey(ctx, org.id, options.key);
|
|
@@ -7154,7 +7317,7 @@ async function buildRecoveryConfig(ctx, orgId, thresholdRaw, custodianRefs) {
|
|
|
7154
7317
|
}
|
|
7155
7318
|
function registerRecoveryCommands(program) {
|
|
7156
7319
|
const recovery = program.command("recovery").description("customer-controlled M-of-N recovery (zero-knowledge)");
|
|
7157
|
-
recovery.command("status").description("show recovery configuration and environment coverage").option("--org <slug>").action(async (options) => {
|
|
7320
|
+
recovery.command("status").description("show recovery configuration and environment coverage").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
7158
7321
|
const ctx = buildContext();
|
|
7159
7322
|
const org = await resolveOrg(ctx, options.org);
|
|
7160
7323
|
const { recovery: status } = await ctx.client.getRecovery(org.id);
|
|
@@ -7168,7 +7331,7 @@ function registerRecoveryCommands(program) {
|
|
|
7168
7331
|
for (const cst of status.custodians) console.log(` - ${cst.label ?? cst.principalId} (${cst.principalType}, share #${cst.shareIndex})`);
|
|
7169
7332
|
if (status.coverage.unprotectedEnvIds.length > 0) console.log(`${status.coverage.unprotectedEnvIds.length} environment(s) not yet protected — run \`seekrit recovery sync\``);
|
|
7170
7333
|
});
|
|
7171
|
-
recovery.command("setup").description("enable recovery: split a fresh recovery key across custodians").requiredOption("--threshold <M>", "custodians required to recover (M in M-of-N)").option("--custodian <email|skt_id>", "a recovery custodian (repeatable)", collect$6, []).option("--org <slug>").action(async (options) => {
|
|
7334
|
+
recovery.command("setup").description("enable recovery: split a fresh recovery key across custodians").requiredOption("--threshold <M>", "custodians required to recover (M in M-of-N)").option("--custodian <email|skt_id>", "a recovery custodian (repeatable)", collect$6, []).option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
7172
7335
|
const ctx = buildContext();
|
|
7173
7336
|
const org = await resolveOrg(ctx, options.org);
|
|
7174
7337
|
const config = await buildRecoveryConfig(ctx, org.id, options.threshold, options.custodian);
|
|
@@ -7181,12 +7344,12 @@ function registerRecoveryCommands(program) {
|
|
|
7181
7344
|
console.error(`recovery-protected ${wrapped} environment(s) you can decrypt`);
|
|
7182
7345
|
if (skipped > 0) console.error(`${skipped} environment(s) need another admin to run \`seekrit recovery sync\``);
|
|
7183
7346
|
});
|
|
7184
|
-
recovery.command("sync").description("recovery-protect environments you can decrypt but that aren't yet covered").option("--org <slug>").action(async (options) => {
|
|
7347
|
+
recovery.command("sync").description("recovery-protect environments you can decrypt but that aren't yet covered").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
7185
7348
|
const ctx = buildContext();
|
|
7186
7349
|
const { wrapped, skipped } = await syncRecoveryGrants(ctx, (await resolveOrg(ctx, options.org)).id);
|
|
7187
7350
|
console.error(`recovery-protected ${wrapped} environment(s); skipped ${skipped} you cannot decrypt`);
|
|
7188
7351
|
});
|
|
7189
|
-
recovery.command("rotate").description("rotate the recovery key (new keypair, custodians, and env re-wraps)").requiredOption("--threshold <M>", "custodians required to recover (M in M-of-N)").option("--custodian <email|skt_id>", "a recovery custodian (repeatable)", collect$6, []).option("--org <slug>").action(async (options) => {
|
|
7352
|
+
recovery.command("rotate").description("rotate the recovery key (new keypair, custodians, and env re-wraps)").requiredOption("--threshold <M>", "custodians required to recover (M in M-of-N)").option("--custodian <email|skt_id>", "a recovery custodian (repeatable)", collect$6, []).option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
7190
7353
|
const ctx = buildContext();
|
|
7191
7354
|
const org = await resolveOrg(ctx, options.org);
|
|
7192
7355
|
const config = await buildRecoveryConfig(ctx, org.id, options.threshold, options.custodian);
|
|
@@ -7199,13 +7362,14 @@ function registerRecoveryCommands(program) {
|
|
|
7199
7362
|
console.error(`re-wrapped ${wrapped} environment(s) you can decrypt to the new recovery key`);
|
|
7200
7363
|
if (skipped > 0) console.error(`${skipped} environment(s) still need another admin to run \`seekrit recovery sync\``);
|
|
7201
7364
|
});
|
|
7202
|
-
recovery.command("disable").description("disable recovery and remove all recovery grants").option("--org <slug>").action(async (options) => {
|
|
7365
|
+
recovery.command("disable").description("disable recovery and remove all recovery grants").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (options) => {
|
|
7203
7366
|
const ctx = buildContext();
|
|
7204
7367
|
const org = await resolveOrg(ctx, options.org);
|
|
7368
|
+
await confirmDestructive(options.yes, `Disable recovery for ${org.slug}? Every custodian share is dropped, and the org loses its break-glass path.`);
|
|
7205
7369
|
await ctx.client.disableRecovery(org.id);
|
|
7206
7370
|
console.error("recovery disabled; recovery grants removed");
|
|
7207
7371
|
});
|
|
7208
|
-
recovery.command("request").description("start a recovery ceremony (defaults to recovering access for yourself)").option("--target-user <email>", "recover access for another member").option("--target-token <id>", "recover access for a service token").option("--reason <text>", "note recorded in the audit trail").option("--org <slug>").action(async (options) => {
|
|
7372
|
+
recovery.command("request").description("start a recovery ceremony (defaults to recovering access for yourself)").option("--target-user <email>", "recover access for another member").option("--target-token <id>", "recover access for a service token").option("--reason <text>", "note recorded in the audit trail").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
7209
7373
|
const ctx = buildContext();
|
|
7210
7374
|
const org = await resolveOrg(ctx, options.org);
|
|
7211
7375
|
const target = options.targetUser || options.targetToken ? await kmsResolveRecipient(ctx, org.id, {
|
|
@@ -7222,7 +7386,7 @@ function registerRecoveryCommands(program) {
|
|
|
7222
7386
|
console.error(` custodians run: seekrit recovery approve ${request.id}`);
|
|
7223
7387
|
console.error(` then the target: seekrit recovery complete ${request.id}`);
|
|
7224
7388
|
});
|
|
7225
|
-
recovery.command("approve").description("as a custodian, contribute your share to a recovery request").argument("<requestId>").option("--org <slug>").action(async (requestId, options) => {
|
|
7389
|
+
recovery.command("approve").description("as a custodian, contribute your share to a recovery request").argument("<requestId>").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (requestId, options) => {
|
|
7226
7390
|
const ctx = buildContext();
|
|
7227
7391
|
const org = await resolveOrg(ctx, options.org);
|
|
7228
7392
|
const { request } = await ctx.client.getRecoveryRequest(org.id, requestId);
|
|
@@ -7235,7 +7399,7 @@ function registerRecoveryCommands(program) {
|
|
|
7235
7399
|
});
|
|
7236
7400
|
console.error(`contributed share #${myShare.shareIndex}: ${res.contributed}/${res.threshold} collected${res.quorumReached ? " — quorum reached" : ""}`);
|
|
7237
7401
|
});
|
|
7238
|
-
recovery.command("complete").description("as the recovery target, reconstruct the key and restore your access").argument("<requestId>").option("--org <slug>").action(async (requestId, options) => {
|
|
7402
|
+
recovery.command("complete").description("as the recovery target, reconstruct the key and restore your access").argument("<requestId>").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (requestId, options) => {
|
|
7239
7403
|
const ctx = buildContext();
|
|
7240
7404
|
const org = await resolveOrg(ctx, options.org);
|
|
7241
7405
|
const { request, contributions, quorumReached } = await ctx.client.getRecoveryRequest(org.id, requestId);
|
|
@@ -7259,9 +7423,10 @@ function registerRecoveryCommands(program) {
|
|
|
7259
7423
|
});
|
|
7260
7424
|
console.error(`recovery complete: restored access to ${restored.length} environment(s)`);
|
|
7261
7425
|
});
|
|
7262
|
-
recovery.command("cancel").description("cancel an open recovery request").argument("<requestId>").option("--org <slug>").action(async (requestId, options) => {
|
|
7426
|
+
recovery.command("cancel").description("cancel an open recovery request").argument("<requestId>").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (requestId, options) => {
|
|
7263
7427
|
const ctx = buildContext();
|
|
7264
7428
|
const org = await resolveOrg(ctx, options.org);
|
|
7429
|
+
await confirmDestructive(options.yes, `Cancel recovery request ${requestId}? Approvals already collected are discarded.`);
|
|
7265
7430
|
await ctx.client.cancelRecoveryRequest(org.id, requestId);
|
|
7266
7431
|
console.error(`recovery request ${requestId} canceled`);
|
|
7267
7432
|
});
|
|
@@ -7275,7 +7440,7 @@ function registerRecoveryCommands(program) {
|
|
|
7275
7440
|
*/
|
|
7276
7441
|
function registerAppCommands(program) {
|
|
7277
7442
|
const app = program.command("app").description("manage applications");
|
|
7278
|
-
app.command("list").alias("ls").description("list applications in an organization").option("--org <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
7443
|
+
app.command("list").alias("ls").description("list applications in an organization").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (options) => {
|
|
7279
7444
|
const ctx = buildContext();
|
|
7280
7445
|
const ref = await resolveOrg(ctx, options.org);
|
|
7281
7446
|
const { apps } = await ctx.client.listApps(ref.id);
|
|
@@ -7286,7 +7451,7 @@ function registerAppCommands(program) {
|
|
|
7286
7451
|
col("id", (a) => a.id)
|
|
7287
7452
|
], "no applications — create one with `seekrit app create`"));
|
|
7288
7453
|
});
|
|
7289
|
-
app.command("show [slug]").description("show an application, its environments, and your access to each").option("--org <slug>").option("--app <slug>", "application slug (defaults to seekrit.json)").option("--json", "print the raw API response").action(async (slug, options) => {
|
|
7454
|
+
app.command("show [slug]").description("show an application, its environments, and your access to each").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "application slug (defaults to seekrit.json)").option("--json", "print the raw API response").action(async (slug, options) => {
|
|
7290
7455
|
const ctx = buildContext();
|
|
7291
7456
|
const ref = await resolveApp(ctx, {
|
|
7292
7457
|
org: options.org,
|
|
@@ -7322,7 +7487,7 @@ function registerAppCommands(program) {
|
|
|
7322
7487
|
}
|
|
7323
7488
|
});
|
|
7324
7489
|
});
|
|
7325
|
-
app.command("create").description("create an application").option("--org <slug>").requiredOption("--name <name>").requiredOption("--slug <slug>").action(async (options) => {
|
|
7490
|
+
app.command("create").description("create an application").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--name <name>", "display name").requiredOption("--slug <slug>", "url-safe identifier (permanent)").action(async (options) => {
|
|
7326
7491
|
const ctx = buildContext();
|
|
7327
7492
|
const orgRef = await resolveOrg(ctx, options.org);
|
|
7328
7493
|
const created = await ctx.client.createApp(orgRef.id, {
|
|
@@ -7331,7 +7496,7 @@ function registerAppCommands(program) {
|
|
|
7331
7496
|
});
|
|
7332
7497
|
console.error(`created app ${created.app.slug} (${created.app.id})`);
|
|
7333
7498
|
});
|
|
7334
|
-
app.command("rename [slug]").description("change an application's display name (the slug is permanent)").option("--org <slug>").option("--app <slug>", "application slug (defaults to seekrit.json)").requiredOption("--name <name>", "new display name").action(async (slug, options) => {
|
|
7499
|
+
app.command("rename [slug]").description("change an application's display name (the slug is permanent)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "application slug (defaults to seekrit.json)").requiredOption("--name <name>", "new display name").action(async (slug, options) => {
|
|
7335
7500
|
const ctx = buildContext();
|
|
7336
7501
|
const ref = await resolveApp(ctx, {
|
|
7337
7502
|
org: options.org,
|
|
@@ -7340,7 +7505,7 @@ function registerAppCommands(program) {
|
|
|
7340
7505
|
const { app: row } = await ctx.client.updateApp(ref.orgId, ref.id, { name: options.name });
|
|
7341
7506
|
console.error(`renamed ${row.slug} to "${row.name}"`);
|
|
7342
7507
|
});
|
|
7343
|
-
app.command("rm <slug>").alias("delete").description("delete an application and every environment and secret in it").option("--org <slug>").option("--yes", "skip the confirmation prompt").action(async (slug, options) => {
|
|
7508
|
+
app.command("rm <slug>").alias("delete").description("delete an application and every environment and secret in it").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation prompt").action(async (slug, options) => {
|
|
7344
7509
|
const ctx = buildContext();
|
|
7345
7510
|
const ref = await resolveApp(ctx, {
|
|
7346
7511
|
org: options.org,
|
|
@@ -7352,7 +7517,7 @@ function registerAppCommands(program) {
|
|
|
7352
7517
|
console.error(`deleted app ${ref.slug}`);
|
|
7353
7518
|
});
|
|
7354
7519
|
const env = program.command("env").description("manage environments");
|
|
7355
|
-
env.command("list").alias("ls").description("list an application's environments").option("--org <slug>").option("--app <slug>", "application slug (defaults to seekrit.json)").option("--json", "print the raw API response").action(async (options) => {
|
|
7520
|
+
env.command("list").alias("ls").description("list an application's environments").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "application slug (defaults to seekrit.json)").option("--json", "print the raw API response").action(async (options) => {
|
|
7356
7521
|
const ctx = buildContext();
|
|
7357
7522
|
const ref = await resolveApp(ctx, options);
|
|
7358
7523
|
const { environments } = await ctx.client.getApp(ref.orgId, ref.id);
|
|
@@ -7363,7 +7528,7 @@ function registerAppCommands(program) {
|
|
|
7363
7528
|
col("id", (e) => e.id)
|
|
7364
7529
|
], "no environments — create one with `seekrit env create`"));
|
|
7365
7530
|
});
|
|
7366
|
-
env.command("show").description("show one environment: composed groups, who holds a key, secret count").option("--org <slug>").option("--app <slug>", "application slug (defaults to seekrit.json)").requiredOption("--env <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
7531
|
+
env.command("show").description("show one environment: composed groups, who holds a key, secret count").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "application slug (defaults to seekrit.json)").requiredOption("--env <slug>", "environment slug").option("--json", "print the raw API response").action(async (options) => {
|
|
7367
7532
|
const ctx = buildContext();
|
|
7368
7533
|
const target = await resolveAppEnv(ctx, options);
|
|
7369
7534
|
const [{ environment }, { groups }, { secrets }, { branches }] = await Promise.all([
|
|
@@ -7403,7 +7568,7 @@ function registerAppCommands(program) {
|
|
|
7403
7568
|
}
|
|
7404
7569
|
});
|
|
7405
7570
|
});
|
|
7406
|
-
env.command("create").description("create an application environment (generates its data key locally)").option("--org <slug>").requiredOption("--app <slug>").requiredOption("--name <name>").requiredOption("--slug <slug>").action(async (options) => {
|
|
7571
|
+
env.command("create").description("create an application environment (generates its data key locally)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--app <slug>", "application slug").requiredOption("--name <name>", "display name").requiredOption("--slug <slug>", "url-safe identifier (permanent)").action(async (options) => {
|
|
7407
7572
|
const ctx = buildContext();
|
|
7408
7573
|
const orgRef = await resolveOrg(ctx, options.org);
|
|
7409
7574
|
const { apps } = await ctx.client.listApps(orgRef.id);
|
|
@@ -7422,7 +7587,7 @@ function registerAppCommands(program) {
|
|
|
7422
7587
|
});
|
|
7423
7588
|
console.error(`created environment ${created.environment.slug} (${created.environment.id})`);
|
|
7424
7589
|
});
|
|
7425
|
-
env.command("rm").alias("delete").description("delete an environment and every secret in it").option("--org <slug>").option("--app <slug>", "application slug (defaults to seekrit.json)").option("--group <slug>", "delete a group environment instead of an app one").requiredOption("--env <slug>").option("--yes", "skip the confirmation prompt").action(async (options) => {
|
|
7590
|
+
env.command("rm").alias("delete").description("delete an environment and every secret in it").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "application slug (defaults to seekrit.json)").option("--group <slug>", "delete a group environment instead of an app one").requiredOption("--env <slug>", "environment slug").option("-y, --yes", "skip the confirmation prompt").action(async (options) => {
|
|
7426
7591
|
const ctx = buildContext();
|
|
7427
7592
|
const target = await resolveEnvTarget(ctx, options);
|
|
7428
7593
|
const { secrets } = await ctx.client.listSecrets(target.orgId, target.envId);
|
|
@@ -7431,7 +7596,7 @@ function registerAppCommands(program) {
|
|
|
7431
7596
|
console.error(`deleted ${target.label}`);
|
|
7432
7597
|
});
|
|
7433
7598
|
const envGroups = env.command("groups").description("compose shared groups into an application environment");
|
|
7434
|
-
envGroups.command("add").description("compose a group into an app environment").option("--org <slug>").requiredOption("--app <slug>").requiredOption("--env <slug>").requiredOption("--group <slug>").option("--position <n>", "precedence among groups (higher wins)").action(async (options) => {
|
|
7599
|
+
envGroups.command("add").description("compose a group into an app environment").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--app <slug>", "application slug").requiredOption("--env <slug>", "environment slug").requiredOption("--group <slug>", "group slug").option("--position <n>", "precedence among groups (higher wins)").action(async (options) => {
|
|
7435
7600
|
const ctx = buildContext();
|
|
7436
7601
|
const target = await resolveAppEnv(ctx, options);
|
|
7437
7602
|
const group = await resolveGroup(ctx, {
|
|
@@ -7444,7 +7609,7 @@ function registerAppCommands(program) {
|
|
|
7444
7609
|
});
|
|
7445
7610
|
console.error(`composed ${group.slug} into ${target.appSlug}/${target.envSlug}`);
|
|
7446
7611
|
});
|
|
7447
|
-
envGroups.command("list").alias("ls").description("list groups composed into an app environment").option("--org <slug>").requiredOption("--app <slug>").requiredOption("--env <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
7612
|
+
envGroups.command("list").alias("ls").description("list groups composed into an app environment").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--app <slug>", "application slug").requiredOption("--env <slug>", "environment slug").option("--json", "print the raw API response").action(async (options) => {
|
|
7448
7613
|
const ctx = buildContext();
|
|
7449
7614
|
const target = await resolveAppEnv(ctx, options);
|
|
7450
7615
|
const { groups } = await ctx.client.listEnvGroups(target.orgId, target.envId);
|
|
@@ -7454,13 +7619,14 @@ function registerAppCommands(program) {
|
|
|
7454
7619
|
col("name", (g) => g.name)
|
|
7455
7620
|
], "no groups composed into this environment"));
|
|
7456
7621
|
});
|
|
7457
|
-
envGroups.command("rm").description("remove a group from an app environment").option("--org <slug>").requiredOption("--app <slug>").requiredOption("--env <slug>").requiredOption("--group <slug>").action(async (options) => {
|
|
7622
|
+
envGroups.command("rm").description("remove a group from an app environment").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--app <slug>", "application slug").requiredOption("--env <slug>", "environment slug").requiredOption("--group <slug>", "group slug").option("-y, --yes", "skip the confirmation prompt").action(async (options) => {
|
|
7458
7623
|
const ctx = buildContext();
|
|
7459
7624
|
const target = await resolveAppEnv(ctx, options);
|
|
7460
7625
|
const group = await resolveGroup(ctx, {
|
|
7461
7626
|
org: options.org,
|
|
7462
7627
|
group: options.group
|
|
7463
7628
|
});
|
|
7629
|
+
await confirmDestructive(options.yes, `Remove ${group.slug} from ${target.appSlug}/${target.envSlug}? Everything it contributed disappears from that environment.`);
|
|
7464
7630
|
await ctx.client.unlinkEnvGroup(target.orgId, target.envId, group.id);
|
|
7465
7631
|
console.error(`removed ${group.slug} from ${target.appSlug}/${target.envSlug}`);
|
|
7466
7632
|
});
|
|
@@ -8483,7 +8649,7 @@ async function decryptArchive(archive, key, filter) {
|
|
|
8483
8649
|
}
|
|
8484
8650
|
function registerArchiveCommands(program) {
|
|
8485
8651
|
const archive = program.command("archive").description("export the whole org as one signed file, and open it offline");
|
|
8486
|
-
archive.command("create").description("download a signed archive of everything seekrit stores for the org").option("--org <slug>").option("-o, --out <file>", "write the archive here (default: ./seekrit-<org>-<date>.json)").option("--no-versions", "omit each secret's ciphertext history").option("--no-audit", "omit the audit trail").option("--audit-limit <n>", "keep at most this many of the newest audit rows", Number).option("--json", "print the archive to stdout instead of writing a file").action(async (options) => {
|
|
8652
|
+
archive.command("create").description("download a signed archive of everything seekrit stores for the org").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-o, --out <file>", "write the archive here (default: ./seekrit-<org>-<date>.json)").option("--no-versions", "omit each secret's ciphertext history").option("--no-audit", "omit the audit trail").option("--audit-limit <n>", "keep at most this many of the newest audit rows", Number).option("--json", "print the archive to stdout instead of writing a file").action(async (options) => {
|
|
8487
8653
|
const ctx = buildContext();
|
|
8488
8654
|
const ref = await resolveOrg(ctx, options.org);
|
|
8489
8655
|
const input = {
|
|
@@ -8596,7 +8762,7 @@ function registerArchiveCommands(program) {
|
|
|
8596
8762
|
} catch {}
|
|
8597
8763
|
fail("none of the recovery shares in this archive unwrap with that key");
|
|
8598
8764
|
});
|
|
8599
|
-
archive.command("decrypt <file>").description("decrypt an archive's secrets with your own key (offline)").option("-o, --out <dir>", "write one file per environment into this directory").option("--stdout", "print plaintext to stdout instead of writing files").option("--env <label>", "only this environment (app/env, group@env, slug, or id)").option("--format <format>", "dotenv | json | shell", "dotenv").option("--token <skt_…>", "decrypt as a service token instead of a passphrase").option("--key-file <path>", "decrypt with a private key JWK file").option("--share <file>", "custodian share for an offline quorum (repeatable)", collect$5).option("--yes", "skip the confirmation when printing plaintext to stdout").action(async (file, options) => {
|
|
8765
|
+
archive.command("decrypt <file>").description("decrypt an archive's secrets with your own key (offline)").option("-o, --out <dir>", "write one file per environment into this directory").option("--stdout", "print plaintext to stdout instead of writing files").option("--env <label>", "only this environment (app/env, group@env, slug, or id)").option("--format <format>", "dotenv | json | shell", "dotenv").option("--token <skt_…>", "decrypt as a service token instead of a passphrase").option("--key-file <path>", "decrypt with a private key JWK file").option("--share <file>", "custodian share for an offline quorum (repeatable)", collect$5).option("-y, --yes", "skip the confirmation when printing plaintext to stdout").action(async (file, options) => {
|
|
8600
8766
|
if (!options.out && !options.stdout) fail("choose a destination: --out <dir> to write files, or --stdout to print plaintext");
|
|
8601
8767
|
if (![
|
|
8602
8768
|
"dotenv",
|
|
@@ -8654,7 +8820,7 @@ function parseLimit(raw) {
|
|
|
8654
8820
|
*/
|
|
8655
8821
|
function registerAuditCommands(program) {
|
|
8656
8822
|
const audit = program.command("audit").description("read the org audit trail");
|
|
8657
|
-
audit.command("list", { isDefault: true }).alias("ls").description("show the org audit trail").option("--org <slug>").option("--limit <n>", `entries per page (max ${MAX_PAGE})`, "50").option("--action <action>", "only this action, e.g. env.key_granted").option("--resource-type <type>", "only this resource type, e.g. environment").option("--cursor <cursor>", "continue from a previous page's cursor").option("--all", "page through the whole trail, not just the first page").option("--metadata", "include each entry's metadata as JSON").option("--json", "print the raw API response").action(async (options) => {
|
|
8823
|
+
audit.command("list", { isDefault: true }).alias("ls").description("show the org audit trail").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--limit <n>", `entries per page (max ${MAX_PAGE})`, "50").option("--action <action>", "only this action, e.g. env.key_granted").option("--resource-type <type>", "only this resource type, e.g. environment").option("--cursor <cursor>", "continue from a previous page's cursor").option("--all", "page through the whole trail, not just the first page").option("--metadata", "include each entry's metadata as JSON").option("--json", "print the raw API response").action(async (options) => {
|
|
8658
8824
|
if (options.action && !AUDIT_ACTIONS.includes(options.action)) fail(`unknown action "${options.action}" — see \`seekrit audit actions\``);
|
|
8659
8825
|
const ctx = buildContext();
|
|
8660
8826
|
const ref = await resolveOrg(ctx, options.org);
|
|
@@ -8737,7 +8903,7 @@ function resolveBaseCredential(opts) {
|
|
|
8737
8903
|
function registerAwsCommands(program) {
|
|
8738
8904
|
const aws = program.command("aws").description("temporary AWS credentials (STS AssumeRole, zero-knowledge)");
|
|
8739
8905
|
const target = aws.command("target").description("manage AWS role targets");
|
|
8740
|
-
target.command("add").description("register an assumable IAM role to issue temporary credentials from").requiredOption("--name <name>", "display name, e.g. prod-deploy").requiredOption("--role-arn <arn>", "the IAM role to assume, arn:aws:iam::<acct>:role/<name>").requiredOption("--region <region>", "region whose STS endpoint to call, e.g. us-east-1").option("--org <slug>").option("--external-id <id>", "STS ExternalId the role's trust policy requires").option("--session-policy <file>", "path to an inline session policy JSON (further restricts)").option("--max-ttl <duration>", "clamp requested credential lifetime, e.g. 8h").option("--access-key-id <id>", "base IAM access key id (else AWS_ACCESS_KEY_ID)").option("--secret-access-key <secret>", "base IAM secret (else AWS_SECRET_ACCESS_KEY)").action(async (options) => {
|
|
8906
|
+
target.command("add").description("register an assumable IAM role to issue temporary credentials from").requiredOption("--name <name>", "display name, e.g. prod-deploy").requiredOption("--role-arn <arn>", "the IAM role to assume, arn:aws:iam::<acct>:role/<name>").requiredOption("--region <region>", "region whose STS endpoint to call, e.g. us-east-1").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--external-id <id>", "STS ExternalId the role's trust policy requires").option("--session-policy <file>", "path to an inline session policy JSON (further restricts)").option("--max-ttl <duration>", "clamp requested credential lifetime, e.g. 8h").option("--access-key-id <id>", "base IAM access key id (else AWS_ACCESS_KEY_ID)").option("--secret-access-key <secret>", "base IAM secret (else AWS_SECRET_ACCESS_KEY)").action(async (options) => {
|
|
8741
8907
|
const ctx = buildContext();
|
|
8742
8908
|
const org = await resolveOrg(ctx, options.org);
|
|
8743
8909
|
const sessionPolicy = options.sessionPolicy ? readFileSync(options.sessionPolicy, "utf8").trim() : void 0;
|
|
@@ -8762,7 +8928,7 @@ function registerAwsCommands(program) {
|
|
|
8762
8928
|
console.error("\nEnsure the role trusts the admin principal, then `seekrit aws lease`:\n");
|
|
8763
8929
|
console.log(awsTrustPolicyInstructions(config));
|
|
8764
8930
|
});
|
|
8765
|
-
target.command("list").description("list AWS role targets").option("--org <slug>").action(async (options) => {
|
|
8931
|
+
target.command("list").description("list AWS role targets").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
8766
8932
|
const ctx = buildContext();
|
|
8767
8933
|
const org = await resolveOrg(ctx, options.org);
|
|
8768
8934
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -8772,7 +8938,7 @@ function registerAwsCommands(program) {
|
|
|
8772
8938
|
console.log(`${t.id}\t${t.name}\t${cfg.region}\t${cfg.roleArn}`);
|
|
8773
8939
|
}
|
|
8774
8940
|
});
|
|
8775
|
-
target.command("trust <targetId>").description("reprint the IAM trust-policy setup for an AWS target").option("--org <slug>").action(async (targetId, options) => {
|
|
8941
|
+
target.command("trust <targetId>").description("reprint the IAM trust-policy setup for an AWS target").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (targetId, options) => {
|
|
8776
8942
|
const ctx = buildContext();
|
|
8777
8943
|
const org = await resolveOrg(ctx, options.org);
|
|
8778
8944
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -8782,13 +8948,14 @@ function registerAwsCommands(program) {
|
|
|
8782
8948
|
if (cfg.provider !== "aws") fail("not an aws target (see `seekrit pg`/`seekrit ssh`)");
|
|
8783
8949
|
console.log(awsTrustPolicyInstructions(cfg));
|
|
8784
8950
|
});
|
|
8785
|
-
target.command("rm <targetId>").description("delete an AWS role target").option("--org <slug>").action(async (targetId, options) => {
|
|
8951
|
+
target.command("rm <targetId>").description("delete an AWS role target").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (targetId, options) => {
|
|
8786
8952
|
const ctx = buildContext();
|
|
8787
8953
|
const org = await resolveOrg(ctx, options.org);
|
|
8954
|
+
await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
|
|
8788
8955
|
await ctx.client.deleteLeaseTarget(org.id, targetId);
|
|
8789
8956
|
console.error(`deleted ${targetId}`);
|
|
8790
8957
|
});
|
|
8791
|
-
aws.command("lease <target>").description("mint short-lived AWS credentials; prints ready-to-source export lines").option("--org <slug>").option("--ttl <duration>", "credential lifetime, e.g. 15m, 1h, 12h", "1h").option("--json", "print the full credential as JSON").action(async (targetRef, options) => {
|
|
8958
|
+
aws.command("lease <target>").description("mint short-lived AWS credentials; prints ready-to-source export lines").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--ttl <duration>", "credential lifetime, e.g. 15m, 1h, 12h", "1h").option("--json", "print the full credential as JSON").action(async (targetRef, options) => {
|
|
8792
8959
|
const ctx = buildContext();
|
|
8793
8960
|
const org = await resolveOrg(ctx, options.org);
|
|
8794
8961
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -8818,7 +8985,7 @@ function registerAwsCommands(program) {
|
|
|
8818
8985
|
console.log(`export AWS_REGION=${cred.region}`);
|
|
8819
8986
|
}
|
|
8820
8987
|
});
|
|
8821
|
-
aws.command("leases").description("list AWS leases (the ledger — never secret material)").option("--org <slug>").action(async (options) => {
|
|
8988
|
+
aws.command("leases").description("list AWS leases (the ledger — never secret material)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
8822
8989
|
const ctx = buildContext();
|
|
8823
8990
|
const org = await resolveOrg(ctx, options.org);
|
|
8824
8991
|
const { leases } = await ctx.client.listLeases(org.id);
|
|
@@ -8827,9 +8994,10 @@ function registerAwsCommands(program) {
|
|
|
8827
8994
|
console.log(`${l.id}\t${l.status}\texpires ${l.expiresAt}`);
|
|
8828
8995
|
}
|
|
8829
8996
|
});
|
|
8830
|
-
aws.command("revoke <leaseId>").description("mark a lease revoked in the ledger (STS credentials stay valid until they expire)").option("--org <slug>").action(async (leaseId, options) => {
|
|
8997
|
+
aws.command("revoke <leaseId>").description("mark a lease revoked in the ledger (STS credentials stay valid until they expire)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (leaseId, options) => {
|
|
8831
8998
|
const ctx = buildContext();
|
|
8832
8999
|
const org = await resolveOrg(ctx, options.org);
|
|
9000
|
+
await confirmDestructive(options.yes, `Revoke lease ${leaseId}? Credentials already issued stay valid until they expire.`);
|
|
8833
9001
|
await ctx.client.revokeLease(org.id, leaseId);
|
|
8834
9002
|
console.error(`revoked ${leaseId} (issued credentials remain valid until they expire)`);
|
|
8835
9003
|
});
|
|
@@ -8850,7 +9018,7 @@ function usageLine(usage) {
|
|
|
8850
9018
|
*/
|
|
8851
9019
|
function registerBillingCommands(program) {
|
|
8852
9020
|
const billing = program.command("billing").description("plan, usage, and subscription");
|
|
8853
|
-
billing.command("show", { isDefault: true }).description("show the org's plan, what it includes, and current usage").option("--org <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
9021
|
+
billing.command("show", { isDefault: true }).description("show the org's plan, what it includes, and current usage").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (options) => {
|
|
8854
9022
|
const ctx = buildContext();
|
|
8855
9023
|
const ref = await resolveOrg(ctx, options.org);
|
|
8856
9024
|
const info = await ctx.client.getBilling(ref.id);
|
|
@@ -8877,7 +9045,7 @@ function registerBillingCommands(program) {
|
|
|
8877
9045
|
}
|
|
8878
9046
|
});
|
|
8879
9047
|
});
|
|
8880
|
-
billing.command("entitlements").description("list every entitlement this org resolves to").option("--org <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
9048
|
+
billing.command("entitlements").description("list every entitlement this org resolves to").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (options) => {
|
|
8881
9049
|
const ctx = buildContext();
|
|
8882
9050
|
const ref = await resolveOrg(ctx, options.org);
|
|
8883
9051
|
const info = await ctx.client.getBilling(ref.id);
|
|
@@ -8887,7 +9055,7 @@ function registerBillingCommands(program) {
|
|
|
8887
9055
|
col("source", (e) => e.source)
|
|
8888
9056
|
]));
|
|
8889
9057
|
});
|
|
8890
|
-
billing.command("checkout <family>").description(`start a self-serve upgrade (${VISIBLE_PLAN_FAMILY_IDS.join(" | ")}) — prints a URL`).option("--org <slug>").action(async (family, options) => {
|
|
9058
|
+
billing.command("checkout <family>").description(`start a self-serve upgrade (${VISIBLE_PLAN_FAMILY_IDS.join(" | ")}) — prints a URL`).option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (family, options) => {
|
|
8891
9059
|
if (!PLAN_FAMILY_IDS.includes(family)) fail(`unknown plan "${family}" — one of: ${PLAN_FAMILY_IDS.join(", ")}`);
|
|
8892
9060
|
const ctx = buildContext();
|
|
8893
9061
|
const ref = await resolveOrg(ctx, options.org);
|
|
@@ -8895,14 +9063,14 @@ function registerBillingCommands(program) {
|
|
|
8895
9063
|
console.error("open this to complete checkout:");
|
|
8896
9064
|
console.log(url);
|
|
8897
9065
|
});
|
|
8898
|
-
billing.command("portal").description("open the billing portal (prints a URL) to manage payment and invoices").option("--org <slug>").action(async (options) => {
|
|
9066
|
+
billing.command("portal").description("open the billing portal (prints a URL) to manage payment and invoices").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
8899
9067
|
const ctx = buildContext();
|
|
8900
9068
|
const ref = await resolveOrg(ctx, options.org);
|
|
8901
9069
|
const { url } = await ctx.client.openBillingPortal(ref.id);
|
|
8902
9070
|
console.error("open this to manage billing:");
|
|
8903
9071
|
console.log(url);
|
|
8904
9072
|
});
|
|
8905
|
-
billing.command("cancel").description("cancel the subscription and drop back to the Free plan").option("--org <slug>").option("--yes", "skip the confirmation prompt").action(async (options) => {
|
|
9073
|
+
billing.command("cancel").description("cancel the subscription and drop back to the Free plan").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation prompt").action(async (options) => {
|
|
8906
9074
|
const ctx = buildContext();
|
|
8907
9075
|
const ref = await resolveOrg(ctx, options.org);
|
|
8908
9076
|
await confirmDestructive(options.yes, `Cancel ${ref.slug}'s subscription and move it to the Free plan?`);
|
|
@@ -8945,7 +9113,7 @@ async function resolveBranchParent(ctx, opts) {
|
|
|
8945
9113
|
*/
|
|
8946
9114
|
function registerBranchCommands(program) {
|
|
8947
9115
|
const branch = program.command("branch").description("ephemeral per-PR / preview configs layered on an environment");
|
|
8948
|
-
branch.command("create <slug>").description("fork an environment into an ephemeral branch (inherits its secrets)").option("--org <slug>").option("--app <slug>", "application slug (defaults to seekrit.json)").requiredOption("--from <env>", "the environment to branch").option("--name <name>", "display name (defaults to the slug)").option("--ttl <duration>", "lifetime: 12h, 7d, 2w, … or `never`", "7d").option("--no-share", "don't give the parent's other readers access to this branch").action(async (slug, options) => {
|
|
9116
|
+
branch.command("create <slug>").description("fork an environment into an ephemeral branch (inherits its secrets)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "application slug (defaults to seekrit.json)").requiredOption("--from <env>", "the environment to branch").option("--name <name>", "display name (defaults to the slug)").option("--ttl <duration>", "lifetime: 12h, 7d, 2w, … or `never`", "7d").option("--no-share", "don't give the parent's other readers access to this branch").action(async (slug, options) => {
|
|
8949
9117
|
const ctx = buildContext();
|
|
8950
9118
|
const parent = await resolveBranchParent(ctx, {
|
|
8951
9119
|
org: options.org,
|
|
@@ -8983,7 +9151,7 @@ function registerBranchCommands(program) {
|
|
|
8983
9151
|
console.error(created.branch.expiresAt ? `expires ${created.branch.expiresAt}` : "no expiry — delete it explicitly when the PR closes");
|
|
8984
9152
|
if (grants.length > 0) console.error(`shared with ${grants.length} other reader(s)`);
|
|
8985
9153
|
});
|
|
8986
|
-
branch.command("list").alias("ls").description("list branches in an application (or of one environment)").option("--org <slug>").option("--app <slug>", "application slug (defaults to seekrit.json)").option("--env <slug>", "only branches of this environment").option("--json", "print the raw API response").action(async (options) => {
|
|
9154
|
+
branch.command("list").alias("ls").description("list branches in an application (or of one environment)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "application slug (defaults to seekrit.json)").option("--env <slug>", "only branches of this environment").option("--json", "print the raw API response").action(async (options) => {
|
|
8987
9155
|
const ctx = buildContext();
|
|
8988
9156
|
const branches = options.env ? await (async () => {
|
|
8989
9157
|
const parent = await resolveAppEnv(ctx, options);
|
|
@@ -8999,10 +9167,11 @@ function registerBranchCommands(program) {
|
|
|
8999
9167
|
col("id", (b) => b.id)
|
|
9000
9168
|
], "no branches — create one with `seekrit branch create`"));
|
|
9001
9169
|
});
|
|
9002
|
-
branch.command("delete <slug>").alias("rm").description("tear down a branch and everything it overrode").option("--org <slug>").option("--app <slug>", "application slug (defaults to seekrit.json)").action(async (slug, options) => {
|
|
9170
|
+
branch.command("delete <slug>").alias("rm").description("tear down a branch and everything it overrode").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "application slug (defaults to seekrit.json)").option("-y, --yes", "skip the confirmation").action(async (slug, options) => {
|
|
9003
9171
|
const ctx = buildContext();
|
|
9004
9172
|
const app = await resolveApp(ctx, options);
|
|
9005
9173
|
const target = await resolveBranch(ctx, app, slug);
|
|
9174
|
+
await confirmDestructive(options.yes, `Tear down branch ${app.slug}#${target.slug} and every override in it?`);
|
|
9006
9175
|
await ctx.client.deleteBranch(app.orgId, target.id);
|
|
9007
9176
|
console.error(`deleted branch ${app.slug}#${target.slug}`);
|
|
9008
9177
|
});
|
|
@@ -9215,7 +9384,7 @@ function resolveServiceAccountKey(opts) {
|
|
|
9215
9384
|
function registerGcpCommands(program) {
|
|
9216
9385
|
const gcp = program.command("gcp").description("temporary GCP credentials (IAM generateAccessToken, zero-knowledge)");
|
|
9217
9386
|
const target = gcp.command("target").description("manage GCP service-account targets");
|
|
9218
|
-
target.command("add").description("register an impersonable service account to issue temporary tokens from").requiredOption("--name <name>", "display name, e.g. prod-deploy").requiredOption("--service-account <email>", "the service account to impersonate, name@project.iam.gserviceaccount.com").option("--org <slug>").option("--scope <scope>", "OAuth scope to grant (repeatable; default cloud-platform)", collectList$1).option("--delegate <email>", "delegation-chain service account (repeatable)", collectList$1).option("--max-ttl <duration>", "clamp requested token lifetime, e.g. 1h").option("--key-file <path>", "source SA key JSON (else GOOGLE_APPLICATION_CREDENTIALS)").action(async (options) => {
|
|
9387
|
+
target.command("add").description("register an impersonable service account to issue temporary tokens from").requiredOption("--name <name>", "display name, e.g. prod-deploy").requiredOption("--service-account <email>", "the service account to impersonate, name@project.iam.gserviceaccount.com").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--scope <scope>", "OAuth scope to grant (repeatable; default cloud-platform)", collectList$1).option("--delegate <email>", "delegation-chain service account (repeatable)", collectList$1).option("--max-ttl <duration>", "clamp requested token lifetime, e.g. 1h").option("--key-file <path>", "source SA key JSON (else GOOGLE_APPLICATION_CREDENTIALS)").action(async (options) => {
|
|
9219
9388
|
const ctx = buildContext();
|
|
9220
9389
|
const org = await resolveOrg(ctx, options.org);
|
|
9221
9390
|
const config = {
|
|
@@ -9238,7 +9407,7 @@ function registerGcpCommands(program) {
|
|
|
9238
9407
|
console.error("\nGrant the source SA the token-creator role, then `seekrit gcp lease`:\n");
|
|
9239
9408
|
console.log(gcpSetupInstructions(config));
|
|
9240
9409
|
});
|
|
9241
|
-
target.command("list").description("list GCP service-account targets").option("--org <slug>").action(async (options) => {
|
|
9410
|
+
target.command("list").description("list GCP service-account targets").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
9242
9411
|
const ctx = buildContext();
|
|
9243
9412
|
const org = await resolveOrg(ctx, options.org);
|
|
9244
9413
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -9248,7 +9417,7 @@ function registerGcpCommands(program) {
|
|
|
9248
9417
|
console.log(`${t.id}\t${t.name}\t${cfg.serviceAccount}`);
|
|
9249
9418
|
}
|
|
9250
9419
|
});
|
|
9251
|
-
target.command("setup <targetId>").description("reprint the IAM setup for a GCP target").option("--org <slug>").action(async (targetId, options) => {
|
|
9420
|
+
target.command("setup <targetId>").description("reprint the IAM setup for a GCP target").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (targetId, options) => {
|
|
9252
9421
|
const ctx = buildContext();
|
|
9253
9422
|
const org = await resolveOrg(ctx, options.org);
|
|
9254
9423
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -9258,13 +9427,14 @@ function registerGcpCommands(program) {
|
|
|
9258
9427
|
if (cfg.provider !== "gcp") fail("not a gcp target (see `seekrit aws`/`seekrit ssh`)");
|
|
9259
9428
|
console.log(gcpSetupInstructions(cfg));
|
|
9260
9429
|
});
|
|
9261
|
-
target.command("rm <targetId>").description("delete a GCP service-account target").option("--org <slug>").action(async (targetId, options) => {
|
|
9430
|
+
target.command("rm <targetId>").description("delete a GCP service-account target").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (targetId, options) => {
|
|
9262
9431
|
const ctx = buildContext();
|
|
9263
9432
|
const org = await resolveOrg(ctx, options.org);
|
|
9433
|
+
await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
|
|
9264
9434
|
await ctx.client.deleteLeaseTarget(org.id, targetId);
|
|
9265
9435
|
console.error(`deleted ${targetId}`);
|
|
9266
9436
|
});
|
|
9267
|
-
gcp.command("lease <target>").description("mint a short-lived GCP access token; prints ready-to-source export lines").option("--org <slug>").option("--ttl <duration>", "token lifetime, e.g. 15m, 1h", "1h").option("--json", "print the full credential as JSON").action(async (targetRef, options) => {
|
|
9437
|
+
gcp.command("lease <target>").description("mint a short-lived GCP access token; prints ready-to-source export lines").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--ttl <duration>", "token lifetime, e.g. 15m, 1h", "1h").option("--json", "print the full credential as JSON").action(async (targetRef, options) => {
|
|
9268
9438
|
const ctx = buildContext();
|
|
9269
9439
|
const org = await resolveOrg(ctx, options.org);
|
|
9270
9440
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -9289,7 +9459,7 @@ function registerGcpCommands(program) {
|
|
|
9289
9459
|
console.log(`export GOOGLE_OAUTH_ACCESS_TOKEN=${cred.accessToken}`);
|
|
9290
9460
|
}
|
|
9291
9461
|
});
|
|
9292
|
-
gcp.command("leases").description("list GCP leases (the ledger — never secret material)").option("--org <slug>").action(async (options) => {
|
|
9462
|
+
gcp.command("leases").description("list GCP leases (the ledger — never secret material)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
9293
9463
|
const ctx = buildContext();
|
|
9294
9464
|
const org = await resolveOrg(ctx, options.org);
|
|
9295
9465
|
const { leases } = await ctx.client.listLeases(org.id);
|
|
@@ -9298,9 +9468,10 @@ function registerGcpCommands(program) {
|
|
|
9298
9468
|
console.log(`${l.id}\t${l.status}\texpires ${l.expiresAt}`);
|
|
9299
9469
|
}
|
|
9300
9470
|
});
|
|
9301
|
-
gcp.command("revoke <leaseId>").description("mark a lease revoked in the ledger (tokens stay valid until they expire)").option("--org <slug>").action(async (leaseId, options) => {
|
|
9471
|
+
gcp.command("revoke <leaseId>").description("mark a lease revoked in the ledger (tokens stay valid until they expire)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (leaseId, options) => {
|
|
9302
9472
|
const ctx = buildContext();
|
|
9303
9473
|
const org = await resolveOrg(ctx, options.org);
|
|
9474
|
+
await confirmDestructive(options.yes, `Revoke lease ${leaseId}? Tokens already issued stay valid until they expire.`);
|
|
9304
9475
|
await ctx.client.revokeLease(org.id, leaseId);
|
|
9305
9476
|
console.error(`revoked ${leaseId} (issued tokens remain valid until they expire)`);
|
|
9306
9477
|
});
|
|
@@ -9314,7 +9485,7 @@ function registerGcpCommands(program) {
|
|
|
9314
9485
|
*/
|
|
9315
9486
|
function registerGroupCommands(program) {
|
|
9316
9487
|
const group = program.command("group").description("manage shared groups (reusable secret bags)");
|
|
9317
|
-
group.command("list").alias("ls").description("list shared groups in an organization").option("--org <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
9488
|
+
group.command("list").alias("ls").description("list shared groups in an organization").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (options) => {
|
|
9318
9489
|
const ctx = buildContext();
|
|
9319
9490
|
const ref = await resolveOrg(ctx, options.org);
|
|
9320
9491
|
const { groups } = await ctx.client.listGroups(ref.id);
|
|
@@ -9325,7 +9496,7 @@ function registerGroupCommands(program) {
|
|
|
9325
9496
|
col("id", (g) => g.id)
|
|
9326
9497
|
], "no groups — create one with `seekrit group create`"));
|
|
9327
9498
|
});
|
|
9328
|
-
group.command("show <slug>").description("show a group and the environments (value sets) it holds").option("--org <slug>").option("--json", "print the raw API response").action(async (slug, options) => {
|
|
9499
|
+
group.command("show <slug>").description("show a group and the environments (value sets) it holds").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (slug, options) => {
|
|
9329
9500
|
const ctx = buildContext();
|
|
9330
9501
|
const ref = await resolveGroup(ctx, {
|
|
9331
9502
|
org: options.org,
|
|
@@ -9351,7 +9522,7 @@ function registerGroupCommands(program) {
|
|
|
9351
9522
|
], "no environments — create one with `seekrit group env create`");
|
|
9352
9523
|
});
|
|
9353
9524
|
});
|
|
9354
|
-
group.command("create").description("create a shared group").option("--org <slug>").requiredOption("--name <name>").requiredOption("--slug <slug>").action(async (options) => {
|
|
9525
|
+
group.command("create").description("create a shared group").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--name <name>", "display name").requiredOption("--slug <slug>", "url-safe identifier (permanent)").action(async (options) => {
|
|
9355
9526
|
const ctx = buildContext();
|
|
9356
9527
|
const orgRef = await resolveOrg(ctx, options.org);
|
|
9357
9528
|
const created = await ctx.client.createGroup(orgRef.id, {
|
|
@@ -9360,7 +9531,7 @@ function registerGroupCommands(program) {
|
|
|
9360
9531
|
});
|
|
9361
9532
|
console.error(`created group ${created.group.slug} (${created.group.id})`);
|
|
9362
9533
|
});
|
|
9363
|
-
group.command("rename <slug>").description("change a group's display name (the slug is permanent)").option("--org <slug>").requiredOption("--name <name>", "new display name").action(async (slug, options) => {
|
|
9534
|
+
group.command("rename <slug>").description("change a group's display name (the slug is permanent)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--name <name>", "new display name").action(async (slug, options) => {
|
|
9364
9535
|
const ctx = buildContext();
|
|
9365
9536
|
const ref = await resolveGroup(ctx, {
|
|
9366
9537
|
org: options.org,
|
|
@@ -9369,7 +9540,7 @@ function registerGroupCommands(program) {
|
|
|
9369
9540
|
const { group: row } = await ctx.client.updateGroup(ref.orgId, ref.id, { name: options.name });
|
|
9370
9541
|
console.error(`renamed ${row.slug} to "${row.name}"`);
|
|
9371
9542
|
});
|
|
9372
|
-
group.command("rm <slug>").alias("delete").description("delete a group, its environments, and their secrets").option("--org <slug>").option("--yes", "skip the confirmation prompt").action(async (slug, options) => {
|
|
9543
|
+
group.command("rm <slug>").alias("delete").description("delete a group, its environments, and their secrets").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation prompt").action(async (slug, options) => {
|
|
9373
9544
|
const ctx = buildContext();
|
|
9374
9545
|
const ref = await resolveGroup(ctx, {
|
|
9375
9546
|
org: options.org,
|
|
@@ -9381,7 +9552,7 @@ function registerGroupCommands(program) {
|
|
|
9381
9552
|
console.error(`deleted group ${ref.slug}`);
|
|
9382
9553
|
});
|
|
9383
9554
|
const groupEnv = group.command("env").description("manage a group’s environments (per-slug value sets / variants)");
|
|
9384
|
-
groupEnv.command("list").alias("ls").description("list a group's environments").option("--org <slug>").requiredOption("--group <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
9555
|
+
groupEnv.command("list").alias("ls").description("list a group's environments").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--group <slug>", "group slug").option("--json", "print the raw API response").action(async (options) => {
|
|
9385
9556
|
const ctx = buildContext();
|
|
9386
9557
|
const ref = await resolveGroup(ctx, options);
|
|
9387
9558
|
const { environments } = await ctx.client.listGroupEnvs(ref.orgId, ref.id);
|
|
@@ -9392,7 +9563,7 @@ function registerGroupCommands(program) {
|
|
|
9392
9563
|
col("id", (e) => e.id)
|
|
9393
9564
|
], "no environments — create one with `seekrit group env create`"));
|
|
9394
9565
|
});
|
|
9395
|
-
groupEnv.command("create").description("create a group environment (generates its data key locally)").option("--org <slug>").requiredOption("--group <slug>").requiredOption("--name <name>").requiredOption("--slug <slug>").action(async (options) => {
|
|
9566
|
+
groupEnv.command("create").description("create a group environment (generates its data key locally)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--group <slug>", "group slug").requiredOption("--name <name>", "display name").requiredOption("--slug <slug>", "url-safe identifier (permanent)").action(async (options) => {
|
|
9396
9567
|
const ctx = buildContext();
|
|
9397
9568
|
const groupRef = await resolveGroup(ctx, {
|
|
9398
9569
|
org: options.org,
|
|
@@ -9424,7 +9595,7 @@ function registerGroupCommands(program) {
|
|
|
9424
9595
|
*/
|
|
9425
9596
|
function registerHoneyTokenCommands(program) {
|
|
9426
9597
|
const honey = program.command("honey-token").description("plant decoy credentials that alert when anyone tries to use them");
|
|
9427
|
-
honey.command("create").description("mint a decoy credential; prints it once").requiredOption("--name <name>", "display name, e.g. legacy-ci-bait").option("--org <slug>").option("--placement <note>", "where you're planting it (echoed in the alert email)").action(async (options) => {
|
|
9598
|
+
honey.command("create").description("mint a decoy credential; prints it once").requiredOption("--name <name>", "display name, e.g. legacy-ci-bait").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--placement <note>", "where you're planting it (echoed in the alert email)").action(async (options) => {
|
|
9428
9599
|
const ctx = buildContext();
|
|
9429
9600
|
const orgRef = await resolveOrg(ctx, options.org);
|
|
9430
9601
|
const created = await createHoneyToken();
|
|
@@ -9437,7 +9608,7 @@ function registerHoneyTokenCommands(program) {
|
|
|
9437
9608
|
console.error("decoy created — save it now, it is not stored. Plant it somewhere a thief would look, NOT anywhere your own tooling reads: a deploy script that tries it by mistake trips the alarm just as loudly. It grants nothing.");
|
|
9438
9609
|
console.log(created.token);
|
|
9439
9610
|
});
|
|
9440
|
-
honey.command("list").alias("ls").description("list decoy credentials and whether any have been tripped").option("--org <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
9611
|
+
honey.command("list").alias("ls").description("list decoy credentials and whether any have been tripped").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (options) => {
|
|
9441
9612
|
const ctx = buildContext();
|
|
9442
9613
|
const orgRef = await resolveOrg(ctx, options.org);
|
|
9443
9614
|
const { honeyTokens } = await ctx.client.listHoneyTokens(orgRef.id);
|
|
@@ -9450,7 +9621,7 @@ function registerHoneyTokenCommands(program) {
|
|
|
9450
9621
|
col("id", (t) => t.id)
|
|
9451
9622
|
], "no decoys planted — create one with `seekrit honey-token create`"));
|
|
9452
9623
|
});
|
|
9453
|
-
honey.command("delete <honeyTokenId>").alias("rm").description("delete a decoy (stops it alerting)").option("--org <slug>").option("--yes", "skip the confirmation prompt").action(async (honeyTokenId, options) => {
|
|
9624
|
+
honey.command("delete <honeyTokenId>").alias("rm").description("delete a decoy (stops it alerting)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation prompt").action(async (honeyTokenId, options) => {
|
|
9454
9625
|
const ctx = buildContext();
|
|
9455
9626
|
const orgRef = await resolveOrg(ctx, options.org);
|
|
9456
9627
|
await confirmDestructive(options.yes, `Delete ${honeyTokenId}? Wherever you planted it goes back to being unwatched — pull the bait too.`);
|
|
@@ -9474,7 +9645,7 @@ function collectHeader(value, acc = {}) {
|
|
|
9474
9645
|
*/
|
|
9475
9646
|
function registerLogSinkCommands(program) {
|
|
9476
9647
|
const sink = program.command("log-sink").description("stream the audit trail to your own OTLP collector (SIEM)");
|
|
9477
|
-
sink.command("show", { isDefault: true }).description("show the configured log sink and its delivery health").option("--org <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
9648
|
+
sink.command("show", { isDefault: true }).description("show the configured log sink and its delivery health").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (options) => {
|
|
9478
9649
|
const ctx = buildContext();
|
|
9479
9650
|
const ref = await resolveOrg(ctx, options.org);
|
|
9480
9651
|
const { sink: config } = await ctx.client.getLogSink(ref.id);
|
|
@@ -9493,7 +9664,7 @@ function registerLogSinkCommands(program) {
|
|
|
9493
9664
|
]);
|
|
9494
9665
|
});
|
|
9495
9666
|
});
|
|
9496
|
-
sink.command("set <endpoint>").description("point the audit export at an OTLP/HTTP logs endpoint").option("--org <slug>").option("--header <name: value>", "auth header to send (repeatable; values are write-only)", collectHeader).option("--clear-headers", "send no headers at all (drops the stored ones)").option("--disabled", "save the config but stop shipping").action(async (endpoint, options) => {
|
|
9667
|
+
sink.command("set <endpoint>").description("point the audit export at an OTLP/HTTP logs endpoint").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--header <name: value>", "auth header to send (repeatable; values are write-only)", collectHeader).option("--clear-headers", "send no headers at all (drops the stored ones)").option("--disabled", "save the config but stop shipping").action(async (endpoint, options) => {
|
|
9497
9668
|
if (options.header && options.clearHeaders) fail("pass either --header or --clear-headers, not both");
|
|
9498
9669
|
const ctx = buildContext();
|
|
9499
9670
|
const ref = await resolveOrg(ctx, options.org);
|
|
@@ -9505,7 +9676,7 @@ function registerLogSinkCommands(program) {
|
|
|
9505
9676
|
});
|
|
9506
9677
|
console.error(`log sink → ${config.endpoint} (${config.enabled ? "enabled" : "disabled"}) — test it with \`seekrit log-sink test\``);
|
|
9507
9678
|
});
|
|
9508
|
-
sink.command("test").description("send a probe to the configured endpoint and report the result").option("--org <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
9679
|
+
sink.command("test").description("send a probe to the configured endpoint and report the result").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (options) => {
|
|
9509
9680
|
const ctx = buildContext();
|
|
9510
9681
|
const ref = await resolveOrg(ctx, options.org);
|
|
9511
9682
|
const result = await ctx.client.testLogSink(ref.id);
|
|
@@ -9518,7 +9689,7 @@ function registerLogSinkCommands(program) {
|
|
|
9518
9689
|
});
|
|
9519
9690
|
if (!result.ok) process.exitCode = 1;
|
|
9520
9691
|
});
|
|
9521
|
-
sink.command("rm").alias("delete").description("stop exporting the audit trail and forget the endpoint").option("--org <slug>").option("--yes", "skip the confirmation prompt").action(async (options) => {
|
|
9692
|
+
sink.command("rm").alias("delete").description("stop exporting the audit trail and forget the endpoint").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation prompt").action(async (options) => {
|
|
9522
9693
|
const ctx = buildContext();
|
|
9523
9694
|
const ref = await resolveOrg(ctx, options.org);
|
|
9524
9695
|
await confirmDestructive(options.yes, `Remove ${ref.slug}'s audit log export?`);
|
|
@@ -9652,7 +9823,7 @@ function resolveAdminUri(uri) {
|
|
|
9652
9823
|
function registerMongoCommands(program) {
|
|
9653
9824
|
const mongo = program.command("mongodb").description("temporary MongoDB credentials (createUser, zero-knowledge delivery)");
|
|
9654
9825
|
const target = mongo.command("target").description("manage MongoDB targets");
|
|
9655
|
-
target.command("add").description("register a MongoDB cluster to issue temporary credentials from").requiredOption("--name <name>", "display name, e.g. prod-app").requiredOption("--database <db>", "the database leased users get access to, e.g. app").option("--uri <uri>", "admin connection string (else SEEKRIT_MONGODB_ADMIN_URL)").option("--org <slug>").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--role <role@db>", "grant for a custom target (repeatable)", collect$4, []).option("--auth-source <db>", "authentication database (default admin)").option("--max-ttl <duration>", "clamp requested credential lifetime, e.g. 8h").option("--no-tls", "disable TLS to the cluster (TLS is on by default)").action(async (options) => {
|
|
9826
|
+
target.command("add").description("register a MongoDB cluster to issue temporary credentials from").requiredOption("--name <name>", "display name, e.g. prod-app").requiredOption("--database <db>", "the database leased users get access to, e.g. app").option("--uri <uri>", "admin connection string (else SEEKRIT_MONGODB_ADMIN_URL)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--role <role@db>", "grant for a custom target (repeatable)", collect$4, []).option("--auth-source <db>", "authentication database (default admin)").option("--max-ttl <duration>", "clamp requested credential lifetime, e.g. 8h").option("--no-tls", "disable TLS to the cluster (TLS is on by default)").action(async (options) => {
|
|
9656
9827
|
const ctx = buildContext();
|
|
9657
9828
|
const org = await resolveOrg(ctx, options.org);
|
|
9658
9829
|
const adminUri = resolveAdminUri(options.uri);
|
|
@@ -9683,7 +9854,7 @@ function registerMongoCommands(program) {
|
|
|
9683
9854
|
console.error("\nEnsure a provisioning user exists, then `seekrit mongodb lease`:\n");
|
|
9684
9855
|
console.log(mongoAdminSetupInstructions(config));
|
|
9685
9856
|
});
|
|
9686
|
-
target.command("list").description("list MongoDB targets").option("--org <slug>").action(async (options) => {
|
|
9857
|
+
target.command("list").description("list MongoDB targets").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
9687
9858
|
const ctx = buildContext();
|
|
9688
9859
|
const org = await resolveOrg(ctx, options.org);
|
|
9689
9860
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -9694,13 +9865,14 @@ function registerMongoCommands(program) {
|
|
|
9694
9865
|
console.log(`${t.id}\t${t.name}\t${host}\t${cfg.connection.database}\t${cfg.accessLevel ?? "readonly"}`);
|
|
9695
9866
|
}
|
|
9696
9867
|
});
|
|
9697
|
-
target.command("rm <targetId>").description("delete a MongoDB target").option("--org <slug>").action(async (targetId, options) => {
|
|
9868
|
+
target.command("rm <targetId>").description("delete a MongoDB target").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (targetId, options) => {
|
|
9698
9869
|
const ctx = buildContext();
|
|
9699
9870
|
const org = await resolveOrg(ctx, options.org);
|
|
9871
|
+
await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
|
|
9700
9872
|
await ctx.client.deleteLeaseTarget(org.id, targetId);
|
|
9701
9873
|
console.error(`deleted ${targetId}`);
|
|
9702
9874
|
});
|
|
9703
|
-
mongo.command("lease <target>").description("mint short-lived MongoDB credentials; prints a ready-to-use connection URI").option("--org <slug>").option("--ttl <duration>", "credential lifetime, e.g. 30m, 1h, 8h", "1h").option("--json", "print the full credential as JSON").action(async (targetRef, options) => {
|
|
9875
|
+
mongo.command("lease <target>").description("mint short-lived MongoDB credentials; prints a ready-to-use connection URI").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--ttl <duration>", "credential lifetime, e.g. 30m, 1h, 8h", "1h").option("--json", "print the full credential as JSON").action(async (targetRef, options) => {
|
|
9704
9876
|
const ctx = buildContext();
|
|
9705
9877
|
const org = await resolveOrg(ctx, options.org);
|
|
9706
9878
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -9719,7 +9891,7 @@ function registerMongoCommands(program) {
|
|
|
9719
9891
|
if (options.json) console.log(JSON.stringify(cred, null, 2));
|
|
9720
9892
|
else console.log(`export MONGODB_URI='${cred.uri}'`);
|
|
9721
9893
|
});
|
|
9722
|
-
mongo.command("leases").description("list MongoDB leases (the ledger — never secret material)").option("--org <slug>").action(async (options) => {
|
|
9894
|
+
mongo.command("leases").description("list MongoDB leases (the ledger — never secret material)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
9723
9895
|
const ctx = buildContext();
|
|
9724
9896
|
const org = await resolveOrg(ctx, options.org);
|
|
9725
9897
|
const { leases } = await ctx.client.listLeases(org.id);
|
|
@@ -9728,9 +9900,10 @@ function registerMongoCommands(program) {
|
|
|
9728
9900
|
console.log(`${l.id}\t${l.status}\texpires ${l.expiresAt}`);
|
|
9729
9901
|
}
|
|
9730
9902
|
});
|
|
9731
|
-
mongo.command("revoke <leaseId>").description("revoke a lease now (drops the MongoDB user immediately)").option("--org <slug>").action(async (leaseId, options) => {
|
|
9903
|
+
mongo.command("revoke <leaseId>").description("revoke a lease now (drops the MongoDB user immediately)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (leaseId, options) => {
|
|
9732
9904
|
const ctx = buildContext();
|
|
9733
9905
|
const org = await resolveOrg(ctx, options.org);
|
|
9906
|
+
await confirmDestructive(options.yes, `Revoke lease ${leaseId} now? Its MongoDB user is dropped immediately.`);
|
|
9734
9907
|
await ctx.client.revokeLease(org.id, leaseId);
|
|
9735
9908
|
console.error(`revoked ${leaseId} (the MongoDB user has been dropped)`);
|
|
9736
9909
|
});
|
|
@@ -9812,7 +9985,7 @@ function generateUserName$1(prefix = "tmp") {
|
|
|
9812
9985
|
function registerMysqlCommands(program) {
|
|
9813
9986
|
const mysql = program.command("mysql").description("temporary MySQL/MariaDB credentials (short-lived, zero-knowledge)");
|
|
9814
9987
|
const target = mysql.command("target").description("manage provisioning targets");
|
|
9815
|
-
target.command("add").description("register a MySQL/MariaDB server to lease credentials from").requiredOption("--name <name>", "display name, e.g. prod-db").option("--org <slug>").requiredOption("--host <host>", "database host").option("--port <port>", "database port", "3306").requiredOption("--database <name>", "database to grant access to").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--user-host <host>", "host part of created accounts ('name'@'<host>')", "%").option("--executor <mode>", "in_do | remote", "in_do").option("--provisioner-url <url>", "customer provisioner URL (remote executor)").option("--hmac-key <base64>", "shared HMAC key for the remote executor (or set SEEKRIT_PROVISIONER_HMAC_KEY); `seekrit provisioner keygen` mints one").option("--admin-url <url>", "in_do: admin mysql:// connection string (or set SEEKRIT_MYSQL_ADMIN_URL); wrapped locally").option("--create-statement <sql>", "custom CREATE template (repeatable)", collect$3, []).option("--revoke-statement <sql>", "custom REVOKE template (repeatable)", collect$3, []).action(async (options) => {
|
|
9988
|
+
target.command("add").description("register a MySQL/MariaDB server to lease credentials from").requiredOption("--name <name>", "display name, e.g. prod-db").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--host <host>", "database host").option("--port <port>", "database port", "3306").requiredOption("--database <name>", "database to grant access to").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--user-host <host>", "host part of created accounts ('name'@'<host>')", "%").option("--executor <mode>", "in_do | remote", "in_do").option("--provisioner-url <url>", "customer provisioner URL (remote executor)").option("--hmac-key <base64>", "shared HMAC key for the remote executor (or set SEEKRIT_PROVISIONER_HMAC_KEY); `seekrit provisioner keygen` mints one").option("--admin-url <url>", "in_do: admin mysql:// connection string (or set SEEKRIT_MYSQL_ADMIN_URL); wrapped locally").option("--create-statement <sql>", "custom CREATE template (repeatable)", collect$3, []).option("--revoke-statement <sql>", "custom REVOKE template (repeatable)", collect$3, []).action(async (options) => {
|
|
9816
9989
|
const ctx = buildContext();
|
|
9817
9990
|
const org = await resolveOrg(ctx, options.org);
|
|
9818
9991
|
const executor = options.executor === "remote" ? "remote" : "in_do";
|
|
@@ -9854,7 +10027,7 @@ function registerMysqlCommands(program) {
|
|
|
9854
10027
|
});
|
|
9855
10028
|
console.error(`registered ${accessLevel} target ${created.name} (${created.id})`);
|
|
9856
10029
|
});
|
|
9857
|
-
target.command("list").description("list provisioning targets").option("--org <slug>").action(async (options) => {
|
|
10030
|
+
target.command("list").description("list provisioning targets").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
9858
10031
|
const ctx = buildContext();
|
|
9859
10032
|
const org = await resolveOrg(ctx, options.org);
|
|
9860
10033
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -9864,13 +10037,14 @@ function registerMysqlCommands(program) {
|
|
|
9864
10037
|
console.log(`${t.id}\t${t.name}\t${cfg.connection.host}:${cfg.connection.port}/${cfg.connection.database}\t${cfg.accessLevel ?? "custom"}\t${cfg.executor}`);
|
|
9865
10038
|
}
|
|
9866
10039
|
});
|
|
9867
|
-
target.command("rm <targetId>").description("remove a provisioning target").option("--org <slug>").action(async (targetId, options) => {
|
|
10040
|
+
target.command("rm <targetId>").description("remove a provisioning target").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (targetId, options) => {
|
|
9868
10041
|
const ctx = buildContext();
|
|
9869
10042
|
const org = await resolveOrg(ctx, options.org);
|
|
10043
|
+
await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
|
|
9870
10044
|
await ctx.client.deleteLeaseTarget(org.id, targetId);
|
|
9871
10045
|
console.error(`removed ${targetId}`);
|
|
9872
10046
|
});
|
|
9873
|
-
mysql.command("lease <target>").description("mint a temporary credential; prints a ready-to-use connection URL").option("--org <slug>").option("--user <name>", "user name to create (default: a random tmp_ name)").option("--ttl <duration>", "lifetime, e.g. 30m, 1h, 7d", "1h").option("--json", "print the full connection as JSON").action(async (targetRef, options) => {
|
|
10047
|
+
mysql.command("lease <target>").description("mint a temporary credential; prints a ready-to-use connection URL").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--user <name>", "user name to create (default: a random tmp_ name)").option("--ttl <duration>", "lifetime, e.g. 30m, 1h, 7d", "1h").option("--json", "print the full connection as JSON").action(async (targetRef, options) => {
|
|
9874
10048
|
const ctx = buildContext();
|
|
9875
10049
|
const org = await resolveOrg(ctx, options.org);
|
|
9876
10050
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -9896,7 +10070,7 @@ function registerMysqlCommands(program) {
|
|
|
9896
10070
|
}, null, 2));
|
|
9897
10071
|
else console.log(url);
|
|
9898
10072
|
});
|
|
9899
|
-
mysql.command("leases").description("list leases (the ledger — never secret material)").option("--org <slug>").action(async (options) => {
|
|
10073
|
+
mysql.command("leases").description("list leases (the ledger — never secret material)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
9900
10074
|
const ctx = buildContext();
|
|
9901
10075
|
const org = await resolveOrg(ctx, options.org);
|
|
9902
10076
|
const { leases } = await ctx.client.listLeases(org.id);
|
|
@@ -9905,9 +10079,10 @@ function registerMysqlCommands(program) {
|
|
|
9905
10079
|
console.log(`${l.id}\t${l.status}\t${l.resourceRef}\texpires ${l.expiresAt}`);
|
|
9906
10080
|
}
|
|
9907
10081
|
});
|
|
9908
|
-
mysql.command("revoke <leaseId>").description("revoke a lease now (drops the user immediately)").option("--org <slug>").action(async (leaseId, options) => {
|
|
10082
|
+
mysql.command("revoke <leaseId>").description("revoke a lease now (drops the user immediately)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (leaseId, options) => {
|
|
9909
10083
|
const ctx = buildContext();
|
|
9910
10084
|
const org = await resolveOrg(ctx, options.org);
|
|
10085
|
+
await confirmDestructive(options.yes, `Revoke lease ${leaseId} now? Its MySQL user is dropped immediately.`);
|
|
9911
10086
|
await ctx.client.revokeLease(org.id, leaseId);
|
|
9912
10087
|
console.error(`revoked ${leaseId}`);
|
|
9913
10088
|
});
|
|
@@ -9979,20 +10154,20 @@ function registerOrgCommands(program) {
|
|
|
9979
10154
|
]);
|
|
9980
10155
|
});
|
|
9981
10156
|
});
|
|
9982
|
-
org.command("create").description("create an organization (you become its owner)").requiredOption("--name <name>").requiredOption("--slug <slug>").action(async (options) => {
|
|
10157
|
+
org.command("create").description("create an organization (you become its owner)").requiredOption("--name <name>", "display name").requiredOption("--slug <slug>", "url-safe identifier (permanent)").action(async (options) => {
|
|
9983
10158
|
const created = await buildContext().client.createOrg({
|
|
9984
10159
|
name: options.name,
|
|
9985
10160
|
slug: options.slug
|
|
9986
10161
|
});
|
|
9987
10162
|
console.error(`created org ${created.org.slug} (${created.org.id})`);
|
|
9988
10163
|
});
|
|
9989
|
-
org.command("rename").description("change an organization's display name (the slug is permanent)").option("--org <slug>").requiredOption("--name <name>", "new display name").action(async (options) => {
|
|
10164
|
+
org.command("rename").description("change an organization's display name (the slug is permanent)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--name <name>", "new display name").action(async (options) => {
|
|
9990
10165
|
const ctx = buildContext();
|
|
9991
10166
|
const ref = await resolveOrg(ctx, options.org);
|
|
9992
10167
|
const { org: row } = await ctx.client.updateOrg(ref.id, { name: options.name });
|
|
9993
10168
|
console.error(`renamed ${row.slug} to "${row.name}"`);
|
|
9994
10169
|
});
|
|
9995
|
-
org.command("member").description("view organization members").command("list").alias("ls").description("list members, and whether each has finished key setup").option("--org <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
10170
|
+
org.command("member").description("view organization members").command("list").alias("ls").description("list members, and whether each has finished key setup").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (options) => {
|
|
9996
10171
|
const ctx = buildContext();
|
|
9997
10172
|
const ref = await resolveOrg(ctx, options.org);
|
|
9998
10173
|
const { members } = await ctx.client.listMembers(ref.id);
|
|
@@ -10005,7 +10180,7 @@ function registerOrgCommands(program) {
|
|
|
10005
10180
|
], "no members"));
|
|
10006
10181
|
});
|
|
10007
10182
|
const invite = org.command("invite").description("manage pending invitations");
|
|
10008
|
-
invite.command("list").alias("ls").description("list outstanding invitations").option("--org <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
10183
|
+
invite.command("list").alias("ls").description("list outstanding invitations").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (options) => {
|
|
10009
10184
|
const ctx = buildContext();
|
|
10010
10185
|
const ref = await resolveOrg(ctx, options.org);
|
|
10011
10186
|
const { invites } = await ctx.client.listInvites(ref.id);
|
|
@@ -10016,7 +10191,7 @@ function registerOrgCommands(program) {
|
|
|
10016
10191
|
col("id", (i) => i.id)
|
|
10017
10192
|
], "no pending invitations"));
|
|
10018
10193
|
});
|
|
10019
|
-
invite.command("add <email>").description("invite someone to the organization").option("--org <slug>").option("--role <role>", "admin | member", "member").action(async (email, options) => {
|
|
10194
|
+
invite.command("add <email>").description("invite someone to the organization").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--role <role>", "admin | member", "member").action(async (email, options) => {
|
|
10020
10195
|
if (options.role !== "admin" && options.role !== "member") fail("--role must be admin or member");
|
|
10021
10196
|
const ctx = buildContext();
|
|
10022
10197
|
const ref = await resolveOrg(ctx, options.org);
|
|
@@ -10026,13 +10201,13 @@ function registerOrgCommands(program) {
|
|
|
10026
10201
|
});
|
|
10027
10202
|
console.error(`invited ${row.email} as ${row.role} (${row.id}) — they join when they first sign in`);
|
|
10028
10203
|
});
|
|
10029
|
-
invite.command("rm <inviteId>").description("rescind an invitation").option("--org <slug>").action(async (inviteId, options) => {
|
|
10204
|
+
invite.command("rm <inviteId>").description("rescind an invitation").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (inviteId, options) => {
|
|
10030
10205
|
const ctx = buildContext();
|
|
10031
10206
|
const ref = await resolveOrg(ctx, options.org);
|
|
10032
10207
|
await ctx.client.revokeInvite(ref.id, inviteId);
|
|
10033
10208
|
console.error(`${inviteId} revoked`);
|
|
10034
10209
|
});
|
|
10035
|
-
org.command("mfa").description("show or set the org-wide second-factor requirement").option("--org <slug>").option("--set <policy>", "required | optional").option("--json", "print the raw API response").action(async (options) => {
|
|
10210
|
+
org.command("mfa").description("show or set the org-wide second-factor requirement").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--set <policy>", "required | optional").option("--json", "print the raw API response").action(async (options) => {
|
|
10036
10211
|
const ctx = buildContext();
|
|
10037
10212
|
const ref = await resolveOrg(ctx, options.org);
|
|
10038
10213
|
if (options.set !== void 0 && options.set !== "required" && options.set !== "optional") fail("--set must be required or optional");
|
|
@@ -10045,7 +10220,7 @@ function registerOrgCommands(program) {
|
|
|
10045
10220
|
console.log(policy.required ? "required for all members" : "optional");
|
|
10046
10221
|
});
|
|
10047
10222
|
});
|
|
10048
|
-
org.command("tree").description("print the org's applications, environments, and groups as a tree").option("--org <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
10223
|
+
org.command("tree").description("print the org's applications, environments, and groups as a tree").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (options) => {
|
|
10049
10224
|
const ctx = buildContext();
|
|
10050
10225
|
const ref = await resolveOrg(ctx, options.org);
|
|
10051
10226
|
const [{ apps }, { groups }] = await Promise.all([ctx.client.listApps(ref.id), ctx.client.listGroups(ref.id)]);
|
|
@@ -10911,7 +11086,7 @@ function generateRoleName(prefix = "tmp") {
|
|
|
10911
11086
|
function registerPgCommands(program) {
|
|
10912
11087
|
const pg = program.command("pg").description("temporary Postgres credentials (short-lived, zero-knowledge)");
|
|
10913
11088
|
const target = pg.command("target").description("manage provisioning targets");
|
|
10914
|
-
target.command("add").description("register a Postgres cluster to lease credentials from").requiredOption("--name <name>", "display name, e.g. prod-db").option("--org <slug>").requiredOption("--host <host>", "database host").option("--port <port>", "database port", "5432").requiredOption("--database <name>", "database to connect to").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--schema <name>", "schema for preset grants", "public").option("--executor <mode>", "in_do | remote", "in_do").option("--provisioner-url <url>", "customer provisioner URL (remote executor)").option("--hmac-key <base64>", "shared HMAC key for the remote executor (or set SEEKRIT_PROVISIONER_HMAC_KEY); `seekrit provisioner keygen` mints one").option("--admin-url <url>", "in_do: admin postgres:// connection string (or set SEEKRIT_PG_ADMIN_URL); wrapped locally").option("--create-statement <sql>", "custom CREATE template (repeatable)", collect$2, []).option("--revoke-statement <sql>", "custom REVOKE template (repeatable)", collect$2, []).action(async (options) => {
|
|
11089
|
+
target.command("add").description("register a Postgres cluster to lease credentials from").requiredOption("--name <name>", "display name, e.g. prod-db").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--host <host>", "database host").option("--port <port>", "database port", "5432").requiredOption("--database <name>", "database to connect to").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--schema <name>", "schema for preset grants", "public").option("--executor <mode>", "in_do | remote", "in_do").option("--provisioner-url <url>", "customer provisioner URL (remote executor)").option("--hmac-key <base64>", "shared HMAC key for the remote executor (or set SEEKRIT_PROVISIONER_HMAC_KEY); `seekrit provisioner keygen` mints one").option("--admin-url <url>", "in_do: admin postgres:// connection string (or set SEEKRIT_PG_ADMIN_URL); wrapped locally").option("--create-statement <sql>", "custom CREATE template (repeatable)", collect$2, []).option("--revoke-statement <sql>", "custom REVOKE template (repeatable)", collect$2, []).action(async (options) => {
|
|
10915
11090
|
const ctx = buildContext();
|
|
10916
11091
|
const org = await resolveOrg(ctx, options.org);
|
|
10917
11092
|
const executor = options.executor === "remote" ? "remote" : "in_do";
|
|
@@ -10957,7 +11132,7 @@ function registerPgCommands(program) {
|
|
|
10957
11132
|
console.log(bootstrap);
|
|
10958
11133
|
}
|
|
10959
11134
|
});
|
|
10960
|
-
target.command("list").description("list provisioning targets").option("--org <slug>").action(async (options) => {
|
|
11135
|
+
target.command("list").description("list provisioning targets").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
10961
11136
|
const ctx = buildContext();
|
|
10962
11137
|
const org = await resolveOrg(ctx, options.org);
|
|
10963
11138
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -10967,7 +11142,7 @@ function registerPgCommands(program) {
|
|
|
10967
11142
|
console.log(`${t.id}\t${t.name}\t${cfg.connection.host}:${cfg.connection.port}/${cfg.connection.database}\t${cfg.accessLevel ?? "custom"}\t${cfg.executor}`);
|
|
10968
11143
|
}
|
|
10969
11144
|
});
|
|
10970
|
-
target.command("setup-sql <targetId>").description("print the one-time group-role setup SQL for a preset target").option("--org <slug>").action(async (targetId, options) => {
|
|
11145
|
+
target.command("setup-sql <targetId>").description("print the one-time group-role setup SQL for a preset target").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (targetId, options) => {
|
|
10971
11146
|
const ctx = buildContext();
|
|
10972
11147
|
const org = await resolveOrg(ctx, options.org);
|
|
10973
11148
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -10979,13 +11154,14 @@ function registerPgCommands(program) {
|
|
|
10979
11154
|
if (!bootstrap) fail("this is a custom target — it has no generated setup SQL");
|
|
10980
11155
|
console.log(bootstrap);
|
|
10981
11156
|
});
|
|
10982
|
-
target.command("rm <targetId>").description("remove a provisioning target").option("--org <slug>").action(async (targetId, options) => {
|
|
11157
|
+
target.command("rm <targetId>").description("remove a provisioning target").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (targetId, options) => {
|
|
10983
11158
|
const ctx = buildContext();
|
|
10984
11159
|
const org = await resolveOrg(ctx, options.org);
|
|
11160
|
+
await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
|
|
10985
11161
|
await ctx.client.deleteLeaseTarget(org.id, targetId);
|
|
10986
11162
|
console.error(`removed ${targetId}`);
|
|
10987
11163
|
});
|
|
10988
|
-
pg.command("lease <target>").description("mint a temporary credential; prints a ready-to-use connection URL").option("--org <slug>").option("--role <name>", "role name to create (default: a random tmp_ name)").option("--ttl <duration>", "lifetime, e.g. 30m, 1h, 7d", "1h").option("--json", "print the full connection as JSON").action(async (targetRef, options) => {
|
|
11164
|
+
pg.command("lease <target>").description("mint a temporary credential; prints a ready-to-use connection URL").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--role <name>", "role name to create (default: a random tmp_ name)").option("--ttl <duration>", "lifetime, e.g. 30m, 1h, 7d", "1h").option("--json", "print the full connection as JSON").action(async (targetRef, options) => {
|
|
10989
11165
|
const ctx = buildContext();
|
|
10990
11166
|
const org = await resolveOrg(ctx, options.org);
|
|
10991
11167
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -11011,15 +11187,16 @@ function registerPgCommands(program) {
|
|
|
11011
11187
|
}, null, 2));
|
|
11012
11188
|
else console.log(url);
|
|
11013
11189
|
});
|
|
11014
|
-
pg.command("leases").description("list leases (the ledger — never secret material)").option("--org <slug>").action(async (options) => {
|
|
11190
|
+
pg.command("leases").description("list leases (the ledger — never secret material)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
11015
11191
|
const ctx = buildContext();
|
|
11016
11192
|
const org = await resolveOrg(ctx, options.org);
|
|
11017
11193
|
const { leases } = await ctx.client.listLeases(org.id);
|
|
11018
11194
|
for (const l of leases) console.log(`${l.id}\t${l.status}\t${l.resourceRef}\texpires ${l.expiresAt}`);
|
|
11019
11195
|
});
|
|
11020
|
-
pg.command("revoke <leaseId>").description("revoke a lease now (drops the role immediately)").option("--org <slug>").action(async (leaseId, options) => {
|
|
11196
|
+
pg.command("revoke <leaseId>").description("revoke a lease now (drops the role immediately)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (leaseId, options) => {
|
|
11021
11197
|
const ctx = buildContext();
|
|
11022
11198
|
const org = await resolveOrg(ctx, options.org);
|
|
11199
|
+
await confirmDestructive(options.yes, `Revoke lease ${leaseId} now? Its Postgres role is dropped immediately.`);
|
|
11023
11200
|
await ctx.client.revokeLease(org.id, leaseId);
|
|
11024
11201
|
console.error(`revoked ${leaseId}`);
|
|
11025
11202
|
});
|
|
@@ -11516,7 +11693,7 @@ function generateUserName(prefix = "tmp") {
|
|
|
11516
11693
|
function registerRedisCommands(program) {
|
|
11517
11694
|
const redis = program.command("redis").description("temporary Redis credentials (short-lived, zero-knowledge)");
|
|
11518
11695
|
const target = redis.command("target").description("manage provisioning targets");
|
|
11519
|
-
target.command("add").description("register a Redis server to lease credentials from").requiredOption("--name <name>", "display name, e.g. prod-cache").option("--org <slug>").requiredOption("--host <host>", "redis host").option("--port <port>", "redis port", "6379").option("--db <index>", "logical database index (the /<n> in the URL)").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--executor <mode>", "in_do | remote", "in_do").option("--provisioner-url <url>", "customer provisioner URL (remote executor)").option("--hmac-key <base64>", "shared HMAC key for the remote executor (or set SEEKRIT_PROVISIONER_HMAC_KEY); `seekrit provisioner keygen` mints one").option("--admin-url <url>", "in_do: admin redis:// (or rediss://) connection string (or set SEEKRIT_REDIS_ADMIN_URL); wrapped locally").option("--create-statement <cmd>", "custom SETUSER template (repeatable)", collect$1, []).option("--revoke-statement <cmd>", "custom DELUSER template (repeatable)", collect$1, []).action(async (options) => {
|
|
11696
|
+
target.command("add").description("register a Redis server to lease credentials from").requiredOption("--name <name>", "display name, e.g. prod-cache").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--host <host>", "redis host").option("--port <port>", "redis port", "6379").option("--db <index>", "logical database index (the /<n> in the URL)").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--executor <mode>", "in_do | remote", "in_do").option("--provisioner-url <url>", "customer provisioner URL (remote executor)").option("--hmac-key <base64>", "shared HMAC key for the remote executor (or set SEEKRIT_PROVISIONER_HMAC_KEY); `seekrit provisioner keygen` mints one").option("--admin-url <url>", "in_do: admin redis:// (or rediss://) connection string (or set SEEKRIT_REDIS_ADMIN_URL); wrapped locally").option("--create-statement <cmd>", "custom SETUSER template (repeatable)", collect$1, []).option("--revoke-statement <cmd>", "custom DELUSER template (repeatable)", collect$1, []).action(async (options) => {
|
|
11520
11697
|
const ctx = buildContext();
|
|
11521
11698
|
const org = await resolveOrg(ctx, options.org);
|
|
11522
11699
|
const executor = options.executor === "remote" ? "remote" : "in_do";
|
|
@@ -11557,7 +11734,7 @@ function registerRedisCommands(program) {
|
|
|
11557
11734
|
});
|
|
11558
11735
|
console.error(`registered ${accessLevel} target ${created.name} (${created.id})`);
|
|
11559
11736
|
});
|
|
11560
|
-
target.command("list").description("list provisioning targets").option("--org <slug>").action(async (options) => {
|
|
11737
|
+
target.command("list").description("list provisioning targets").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
11561
11738
|
const ctx = buildContext();
|
|
11562
11739
|
const org = await resolveOrg(ctx, options.org);
|
|
11563
11740
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -11568,13 +11745,14 @@ function registerRedisCommands(program) {
|
|
|
11568
11745
|
console.log(`${t.id}\t${t.name}\t${cfg.connection.host}:${cfg.connection.port}/${db}\t${cfg.accessLevel ?? "custom"}\t${cfg.executor}`);
|
|
11569
11746
|
}
|
|
11570
11747
|
});
|
|
11571
|
-
target.command("rm <targetId>").description("remove a provisioning target").option("--org <slug>").action(async (targetId, options) => {
|
|
11748
|
+
target.command("rm <targetId>").description("remove a provisioning target").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (targetId, options) => {
|
|
11572
11749
|
const ctx = buildContext();
|
|
11573
11750
|
const org = await resolveOrg(ctx, options.org);
|
|
11751
|
+
await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
|
|
11574
11752
|
await ctx.client.deleteLeaseTarget(org.id, targetId);
|
|
11575
11753
|
console.error(`removed ${targetId}`);
|
|
11576
11754
|
});
|
|
11577
|
-
redis.command("lease <target>").description("mint a temporary credential; prints a ready-to-use connection URL").option("--org <slug>").option("--user <name>", "ACL user name to create (default: a random tmp_ name)").option("--ttl <duration>", "lifetime, e.g. 30m, 1h, 7d", "1h").option("--json", "print the full connection as JSON").action(async (targetRef, options) => {
|
|
11755
|
+
redis.command("lease <target>").description("mint a temporary credential; prints a ready-to-use connection URL").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--user <name>", "ACL user name to create (default: a random tmp_ name)").option("--ttl <duration>", "lifetime, e.g. 30m, 1h, 7d", "1h").option("--json", "print the full connection as JSON").action(async (targetRef, options) => {
|
|
11578
11756
|
const ctx = buildContext();
|
|
11579
11757
|
const org = await resolveOrg(ctx, options.org);
|
|
11580
11758
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -11600,7 +11778,7 @@ function registerRedisCommands(program) {
|
|
|
11600
11778
|
}, null, 2));
|
|
11601
11779
|
else console.log(url);
|
|
11602
11780
|
});
|
|
11603
|
-
redis.command("leases").description("list leases (the ledger — never secret material)").option("--org <slug>").action(async (options) => {
|
|
11781
|
+
redis.command("leases").description("list leases (the ledger — never secret material)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
11604
11782
|
const ctx = buildContext();
|
|
11605
11783
|
const org = await resolveOrg(ctx, options.org);
|
|
11606
11784
|
const { leases } = await ctx.client.listLeases(org.id);
|
|
@@ -11609,9 +11787,10 @@ function registerRedisCommands(program) {
|
|
|
11609
11787
|
console.log(`${l.id}\t${l.status}\t${l.resourceRef}\texpires ${l.expiresAt}`);
|
|
11610
11788
|
}
|
|
11611
11789
|
});
|
|
11612
|
-
redis.command("revoke <leaseId>").description("revoke a lease now (deletes the ACL user immediately)").option("--org <slug>").action(async (leaseId, options) => {
|
|
11790
|
+
redis.command("revoke <leaseId>").description("revoke a lease now (deletes the ACL user immediately)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (leaseId, options) => {
|
|
11613
11791
|
const ctx = buildContext();
|
|
11614
11792
|
const org = await resolveOrg(ctx, options.org);
|
|
11793
|
+
await confirmDestructive(options.yes, `Revoke lease ${leaseId} now? Its Redis ACL user is deleted immediately.`);
|
|
11615
11794
|
await ctx.client.revokeLease(org.id, leaseId);
|
|
11616
11795
|
console.error(`revoked ${leaseId}`);
|
|
11617
11796
|
});
|
|
@@ -11703,7 +11882,7 @@ function buildConfig$1(kind, options) {
|
|
|
11703
11882
|
}
|
|
11704
11883
|
function registerRotationCommands(program) {
|
|
11705
11884
|
const rotation = program.command("rotation").description("managed rotation of stored secret values (scheduled, zero-knowledge)");
|
|
11706
|
-
rotation.command("enable <secretName>").description("configure rotation for an existing secret").option("--org <slug>").option("--app <slug>").option("--group <slug>", "rotate a secret in a group environment").requiredOption("--env <slug>").requiredOption("--kind <kind>", "generated | postgres | mysql | redis").requiredOption("--every <duration>", "rotation cadence, e.g. 24h, 30d").option("--username <name>", "the EXISTING database account to re-key (db kinds)").option("--target <idOrName>", "registered lease target to rotate against (db kinds)").option("--user-host <host>", "MySQL account host part (default %)").option("--length <n>", "generated value length", "32").option("--alphabet <set>", "generated kind only: alphanumeric | hex | base64url | printable", "alphanumeric").option("--now", "rotate immediately as well as on the schedule").action(async (secretName, options) => {
|
|
11885
|
+
rotation.command("enable <secretName>").description("configure rotation for an existing secret").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "application slug (defaults to seekrit.json)").option("--group <slug>", "rotate a secret in a group environment").requiredOption("--env <slug>", "environment slug").requiredOption("--kind <kind>", "generated | postgres | mysql | redis").requiredOption("--every <duration>", "rotation cadence, e.g. 24h, 30d").option("--username <name>", "the EXISTING database account to re-key (db kinds)").option("--target <idOrName>", "registered lease target to rotate against (db kinds)").option("--user-host <host>", "MySQL account host part (default %)").option("--length <n>", "generated value length", "32").option("--alphabet <set>", "generated kind only: alphanumeric | hex | base64url | printable", "alphanumeric").option("--now", "rotate immediately as well as on the schedule").action(async (secretName, options) => {
|
|
11707
11886
|
const ctx = buildContext();
|
|
11708
11887
|
const target = await resolveEnvTarget(ctx, options);
|
|
11709
11888
|
const config = buildConfig$1(options.kind, options);
|
|
@@ -11733,7 +11912,7 @@ function registerRotationCommands(program) {
|
|
|
11733
11912
|
else console.error(`first rotation: ${created.nextRotateAt}`);
|
|
11734
11913
|
console.log(created.id);
|
|
11735
11914
|
});
|
|
11736
|
-
rotation.command("list").description("list rotation policies (schedules only — never values)").option("--org <slug>").option("--json", "print the full policies as JSON").action(async (options) => {
|
|
11915
|
+
rotation.command("list").description("list rotation policies (schedules only — never values)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the full policies as JSON").action(async (options) => {
|
|
11737
11916
|
const ctx = buildContext();
|
|
11738
11917
|
const org = await resolveOrg(ctx, options.org);
|
|
11739
11918
|
const { rotations } = await ctx.client.listRotations(org.id);
|
|
@@ -11747,43 +11926,44 @@ function registerRotationCommands(program) {
|
|
|
11747
11926
|
}
|
|
11748
11927
|
for (const r of rotations) console.log(rotationLine(r));
|
|
11749
11928
|
});
|
|
11750
|
-
rotation.command("show <rotationOrSecret>").description("show one policy, including the last failure if any").option("--org <slug>").action(async (ref, options) => {
|
|
11929
|
+
rotation.command("show <rotationOrSecret>").description("show one policy, including the last failure if any").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (ref, options) => {
|
|
11751
11930
|
const ctx = buildContext();
|
|
11752
11931
|
const r = await resolveRotation(ctx, (await resolveOrg(ctx, options.org)).id, ref);
|
|
11753
11932
|
console.log(JSON.stringify(r, null, 2));
|
|
11754
11933
|
});
|
|
11755
|
-
rotation.command("rotate <rotationOrSecret>").description("rotate now (the same path the scheduler uses)").option("--org <slug>").action(async (ref, options) => {
|
|
11934
|
+
rotation.command("rotate <rotationOrSecret>").description("rotate now (the same path the scheduler uses)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (ref, options) => {
|
|
11756
11935
|
const ctx = buildContext();
|
|
11757
11936
|
const org = await resolveOrg(ctx, options.org);
|
|
11758
11937
|
const r = await resolveRotation(ctx, org.id, ref);
|
|
11759
11938
|
const { version, rotatedAt } = await ctx.client.rotateSecretNow(org.id, r.id);
|
|
11760
11939
|
console.error(`rotated ${r.secretName} at ${rotatedAt} — now at version ${version}. Read it with \`seekrit secrets get ${r.secretName}\`.`);
|
|
11761
11940
|
});
|
|
11762
|
-
rotation.command("pause <rotationOrSecret>").description("stop rotating, keeping the policy").option("--org <slug>").action(async (ref, options) => {
|
|
11941
|
+
rotation.command("pause <rotationOrSecret>").description("stop rotating, keeping the policy").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (ref, options) => {
|
|
11763
11942
|
const ctx = buildContext();
|
|
11764
11943
|
const org = await resolveOrg(ctx, options.org);
|
|
11765
11944
|
const r = await resolveRotation(ctx, org.id, ref);
|
|
11766
11945
|
await ctx.client.updateRotation(org.id, r.id, { status: "paused" });
|
|
11767
11946
|
console.error(`paused rotation of ${r.secretName}`);
|
|
11768
11947
|
});
|
|
11769
|
-
rotation.command("resume <rotationOrSecret>").description("resume rotating (also clears a failed streak)").option("--org <slug>").action(async (ref, options) => {
|
|
11948
|
+
rotation.command("resume <rotationOrSecret>").description("resume rotating (also clears a failed streak)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (ref, options) => {
|
|
11770
11949
|
const ctx = buildContext();
|
|
11771
11950
|
const org = await resolveOrg(ctx, options.org);
|
|
11772
11951
|
const r = await resolveRotation(ctx, org.id, ref);
|
|
11773
11952
|
const { rotation: updated } = await ctx.client.updateRotation(org.id, r.id, { status: "active" });
|
|
11774
11953
|
console.error(`resumed rotation of ${r.secretName} — next ${updated.nextRotateAt}`);
|
|
11775
11954
|
});
|
|
11776
|
-
rotation.command("set-interval <rotationOrSecret>").description("change the cadence").requiredOption("--every <duration>", "new cadence, e.g. 24h, 30d").option("--org <slug>").action(async (ref, options) => {
|
|
11955
|
+
rotation.command("set-interval <rotationOrSecret>").description("change the cadence").requiredOption("--every <duration>", "new cadence, e.g. 24h, 30d").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (ref, options) => {
|
|
11777
11956
|
const ctx = buildContext();
|
|
11778
11957
|
const org = await resolveOrg(ctx, options.org);
|
|
11779
11958
|
const r = await resolveRotation(ctx, org.id, ref);
|
|
11780
11959
|
const { rotation: updated } = await ctx.client.updateRotation(org.id, r.id, { intervalSeconds: parseDurationSeconds(options.every, "--every") });
|
|
11781
11960
|
console.error(`${updated.secretName} now rotates every ${formatInterval(updated.intervalSeconds)} — next ${updated.nextRotateAt}`);
|
|
11782
11961
|
});
|
|
11783
|
-
rotation.command("disable <rotationOrSecret>").description("stop rotating and remove the policy (the secret is untouched)").option("--org <slug>").action(async (ref, options) => {
|
|
11962
|
+
rotation.command("disable <rotationOrSecret>").description("stop rotating and remove the policy (the secret is untouched)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (ref, options) => {
|
|
11784
11963
|
const ctx = buildContext();
|
|
11785
11964
|
const org = await resolveOrg(ctx, options.org);
|
|
11786
11965
|
const r = await resolveRotation(ctx, org.id, ref);
|
|
11966
|
+
await confirmDestructive(options.yes, `Stop rotating ${r.secretName} and delete its policy? The current value stays as it is.`);
|
|
11787
11967
|
const { rotatorRevoked } = await ctx.client.disableRotation(org.id, r.id);
|
|
11788
11968
|
console.error(`disabled rotation of ${r.secretName}${rotatorRevoked ? " — rotator key access revoked for this environment" : ""}`);
|
|
11789
11969
|
});
|
|
@@ -12002,7 +12182,7 @@ function parseTtlSeconds(input) {
|
|
|
12002
12182
|
function registerSshCommands(program) {
|
|
12003
12183
|
const ssh = program.command("ssh").description("temporary SSH access (short-lived certificates, zero-knowledge)");
|
|
12004
12184
|
const target = ssh.command("target").description("manage SSH CA targets");
|
|
12005
|
-
target.command("add").description("create an SSH certificate authority to issue certs from").requiredOption("--name <name>", "display name, e.g. prod-fleet").option("--org <slug>").option("--host <host>", "default host the printed ssh command connects to").option("--user <login>", "default login user (a cert principal)").option("--principal <name>", "allow-list a principal certs may request (repeatable)", collect, []).option("--extension <name>", "cert extension to grant, e.g. permit-pty (repeatable)", collect, []).option("--max-ttl <duration>", "clamp requested cert lifetime, e.g. 8h").action(async (options) => {
|
|
12185
|
+
target.command("add").description("create an SSH certificate authority to issue certs from").requiredOption("--name <name>", "display name, e.g. prod-fleet").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--host <host>", "default host the printed ssh command connects to").option("--user <login>", "default login user (a cert principal)").option("--principal <name>", "allow-list a principal certs may request (repeatable)", collect, []).option("--extension <name>", "cert extension to grant, e.g. permit-pty (repeatable)", collect, []).option("--max-ttl <duration>", "clamp requested cert lifetime, e.g. 8h").action(async (options) => {
|
|
12006
12186
|
const ctx = buildContext();
|
|
12007
12187
|
const org = await resolveOrg(ctx, options.org);
|
|
12008
12188
|
const ca = await generateSshCaKeyPair(`seekrit-ca:${options.name}`);
|
|
@@ -12029,7 +12209,7 @@ function registerSshCommands(program) {
|
|
|
12029
12209
|
console.error("\nInstall the CA on your hosts, then issue certs with `seekrit ssh lease`:\n");
|
|
12030
12210
|
console.log(sshHostSetupInstructions(config));
|
|
12031
12211
|
});
|
|
12032
|
-
target.command("list").description("list SSH CA targets").option("--org <slug>").action(async (options) => {
|
|
12212
|
+
target.command("list").description("list SSH CA targets").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
12033
12213
|
const ctx = buildContext();
|
|
12034
12214
|
const org = await resolveOrg(ctx, options.org);
|
|
12035
12215
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -12041,7 +12221,7 @@ function registerSshCommands(program) {
|
|
|
12041
12221
|
console.log(`${t.id}\t${t.name}\t${where}\tprincipals=${principals}`);
|
|
12042
12222
|
}
|
|
12043
12223
|
});
|
|
12044
|
-
target.command("setup <targetId>").description("reprint the one-time host setup for an SSH target").option("--org <slug>").action(async (targetId, options) => {
|
|
12224
|
+
target.command("setup <targetId>").description("reprint the one-time host setup for an SSH target").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (targetId, options) => {
|
|
12045
12225
|
const ctx = buildContext();
|
|
12046
12226
|
const org = await resolveOrg(ctx, options.org);
|
|
12047
12227
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -12051,13 +12231,14 @@ function registerSshCommands(program) {
|
|
|
12051
12231
|
if (cfg.provider !== "ssh") fail("not an ssh target (see `seekrit pg`)");
|
|
12052
12232
|
console.log(sshHostSetupInstructions(cfg));
|
|
12053
12233
|
});
|
|
12054
|
-
target.command("rm <targetId>").description("delete an SSH CA target").option("--org <slug>").action(async (targetId, options) => {
|
|
12234
|
+
target.command("rm <targetId>").description("delete an SSH CA target").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (targetId, options) => {
|
|
12055
12235
|
const ctx = buildContext();
|
|
12056
12236
|
const org = await resolveOrg(ctx, options.org);
|
|
12237
|
+
await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
|
|
12057
12238
|
await ctx.client.deleteLeaseTarget(org.id, targetId);
|
|
12058
12239
|
console.error(`deleted ${targetId}`);
|
|
12059
12240
|
});
|
|
12060
|
-
ssh.command("lease <target>").description("mint a short-lived SSH certificate; prints a ready-to-run ssh command").option("--org <slug>").option("--principal <name>", "login user to request (repeatable; default from target)", collect, []).option("--ttl <duration>", "certificate lifetime, e.g. 30m, 1h, 8h", "1h").option("--out <dir>", "directory to write the key + cert (default: a temp dir)").option("--json", "print key/cert paths and the certificate as JSON").action(async (targetRef, options) => {
|
|
12241
|
+
ssh.command("lease <target>").description("mint a short-lived SSH certificate; prints a ready-to-run ssh command").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--principal <name>", "login user to request (repeatable; default from target)", collect, []).option("--ttl <duration>", "certificate lifetime, e.g. 30m, 1h, 8h", "1h").option("--out <dir>", "directory to write the key + cert (default: a temp dir)").option("--json", "print key/cert paths and the certificate as JSON").action(async (targetRef, options) => {
|
|
12061
12242
|
const ctx = buildContext();
|
|
12062
12243
|
const org = await resolveOrg(ctx, options.org);
|
|
12063
12244
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -12095,7 +12276,7 @@ function registerSshCommands(program) {
|
|
|
12095
12276
|
}, null, 2));
|
|
12096
12277
|
else console.log(command);
|
|
12097
12278
|
});
|
|
12098
|
-
ssh.command("leases").description("list SSH leases (the ledger — never secret material)").option("--org <slug>").action(async (options) => {
|
|
12279
|
+
ssh.command("leases").description("list SSH leases (the ledger — never secret material)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
12099
12280
|
const ctx = buildContext();
|
|
12100
12281
|
const org = await resolveOrg(ctx, options.org);
|
|
12101
12282
|
const { leases } = await ctx.client.listLeases(org.id);
|
|
@@ -12104,9 +12285,10 @@ function registerSshCommands(program) {
|
|
|
12104
12285
|
console.log(`${l.id}\t${l.status}\texpires ${l.expiresAt}`);
|
|
12105
12286
|
}
|
|
12106
12287
|
});
|
|
12107
|
-
ssh.command("revoke <leaseId>").description("mark a lease revoked in the ledger (the cert stays valid until it expires)").option("--org <slug>").action(async (leaseId, options) => {
|
|
12288
|
+
ssh.command("revoke <leaseId>").description("mark a lease revoked in the ledger (the cert stays valid until it expires)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (leaseId, options) => {
|
|
12108
12289
|
const ctx = buildContext();
|
|
12109
12290
|
const org = await resolveOrg(ctx, options.org);
|
|
12291
|
+
await confirmDestructive(options.yes, `Revoke lease ${leaseId}? Certificates already issued stay valid until they expire.`);
|
|
12110
12292
|
await ctx.client.revokeLease(org.id, leaseId);
|
|
12111
12293
|
console.error(`revoked ${leaseId} (issued certs remain valid until they expire)`);
|
|
12112
12294
|
});
|
|
@@ -12271,8 +12453,21 @@ function credentialNoun(provider) {
|
|
|
12271
12453
|
if (provider.startsWith("aws-")) return "secret access key";
|
|
12272
12454
|
if (provider === "gcp-secret-manager") return "service-account key JSON";
|
|
12273
12455
|
if (provider === "langgraph-platform") return "LangSmith API key";
|
|
12456
|
+
if (provider === "azure-key-vault") return "client secret";
|
|
12274
12457
|
return "API token";
|
|
12275
12458
|
}
|
|
12459
|
+
/**
|
|
12460
|
+
* Entra takes a verified domain name in place of a tenant id, but never in
|
|
12461
|
+
* place of a client id — so both are held to the GUID, which keeps one rule
|
|
12462
|
+
* and costs nothing: the admin center shows a tenant's GUID on the same page
|
|
12463
|
+
* as the application id it pairs with.
|
|
12464
|
+
*/
|
|
12465
|
+
function assertAzureGuid(value, flag, what) {
|
|
12466
|
+
if (!value) fail(`${flag} is required for azure-key-vault (${what})`);
|
|
12467
|
+
const id = value.trim();
|
|
12468
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)) fail(`${flag} "${id}" is not a GUID — the Entra admin center shows it under ${what}`);
|
|
12469
|
+
return id;
|
|
12470
|
+
}
|
|
12276
12471
|
/** Account-scope config for a connection (never the credential itself). */
|
|
12277
12472
|
function buildConfig(provider, options) {
|
|
12278
12473
|
switch (provider) {
|
|
@@ -12338,6 +12533,15 @@ function buildConfig(provider, options) {
|
|
|
12338
12533
|
...options.langgraphTenant ? { tenantId: options.langgraphTenant.trim() } : {}
|
|
12339
12534
|
};
|
|
12340
12535
|
}
|
|
12536
|
+
case "azure-key-vault": {
|
|
12537
|
+
const cloud = assertMembers(list(options.azureCloud, "public"), AZURE_CLOUDS, "--azure-cloud")[0];
|
|
12538
|
+
return {
|
|
12539
|
+
provider: "azure-key-vault",
|
|
12540
|
+
tenantId: assertAzureGuid(options.azureTenantId, "--azure-tenant-id", "Directory (tenant) ID"),
|
|
12541
|
+
clientId: assertAzureGuid(options.azureClientId, "--azure-client-id", "Application (client) ID"),
|
|
12542
|
+
cloud: cloud ?? "public"
|
|
12543
|
+
};
|
|
12544
|
+
}
|
|
12341
12545
|
}
|
|
12342
12546
|
}
|
|
12343
12547
|
/** Where inside the platform a binding writes. */
|
|
@@ -12540,6 +12744,17 @@ function buildDestination(provider, options) {
|
|
|
12540
12744
|
provider: "langgraph-platform",
|
|
12541
12745
|
deploymentId: assertLanggraphDeploymentId(options.langgraphDeployment)
|
|
12542
12746
|
};
|
|
12747
|
+
case "azure-key-vault": {
|
|
12748
|
+
if (!options.vault) fail("--vault is required for azure-key-vault — the vault name, e.g. acme-prod");
|
|
12749
|
+
const prefix = options.path?.trim();
|
|
12750
|
+
const nameMode = assertMembers(list(options.nameMode, "dash"), AZURE_KEY_VAULT_NAME_MODES, "--name-mode")[0];
|
|
12751
|
+
return {
|
|
12752
|
+
provider: "azure-key-vault",
|
|
12753
|
+
vault: options.vault.trim(),
|
|
12754
|
+
...prefix ? { prefix } : {},
|
|
12755
|
+
nameMode: nameMode ?? "dash"
|
|
12756
|
+
};
|
|
12757
|
+
}
|
|
12543
12758
|
}
|
|
12544
12759
|
}
|
|
12545
12760
|
/** One-line description of a destination, for list output. */
|
|
@@ -12561,6 +12776,7 @@ function describeDestination(destination) {
|
|
|
12561
12776
|
case "bunnyshell": return destination.kind === "environment" ? `environment ${destination.environmentId}` : `project ${destination.projectId} (inherited by new environments)`;
|
|
12562
12777
|
case "gcp-secret-manager": return destination.layout === "json-bundle" ? `${destination.secretId} · json` : `${destination.idPrefix ?? ""}* · one per name`;
|
|
12563
12778
|
case "langgraph-platform": return `deployment ${destination.deploymentId}`;
|
|
12779
|
+
case "azure-key-vault": return `${destination.vault}${destination.prefix ? ` (${destination.prefix}*)` : ""}`;
|
|
12564
12780
|
case "github-actions": switch (destination.kind) {
|
|
12565
12781
|
case "repo": return `${destination.owner}/${destination.repo}`;
|
|
12566
12782
|
case "environment": return `${destination.owner}/${destination.repo} @${destination.environment}`;
|
|
@@ -12576,7 +12792,7 @@ function describeDestination(destination) {
|
|
|
12576
12792
|
* application whose environment the binding reads from.
|
|
12577
12793
|
*/
|
|
12578
12794
|
function destinationOptions(command) {
|
|
12579
|
-
return command.option("--project <id>", "vercel: project id or name · cloudflare-pages / northflank: project name or slug · bunnyshell: project ID (writes variables new environments inherit)").option("--target <list>", "vercel / cloudflare-pages: comma-separated deployment targets", "production").option("--git-branch <branch>", "vercel: restrict preview writes to one branch").option("--script <name>", "cloudflare-workers: Worker script name").option("--store-id <id>", "cloudflare-secrets-store: store ID (32 hex)").option("--scopes <list>", "cloudflare-secrets-store: comma-separated scopes", "workers").option("--railway-project <id>", "railway: project ID (a UUID)").option("--railway-environment <id>", "railway: environment ID (a UUID)").option("--service <id>", "railway: service ID (omit for the environment's shared variables) · render: service ID (srv-…, or crn-… for a cron job)").option("--skip-deploys", "railway: stage values without triggering a redeploy").option("--path <path>", "aws-parameter-store: hierarchy, e.g. /prod/storefront/ · aws-secrets-manager: name prefix").option("--layout <layout>", `aws-secrets-manager: ${AWS_SECRETS_MANAGER_LAYOUTS.join(" | ")}`).option("--secret-name <name>", "aws-secrets-manager: the secret a json-bundle writes to").option("--param-type <type>", `aws-parameter-store: ${AWS_PARAMETER_TYPES.join(" | ")}`).option("--tier <tier>", `aws-parameter-store: ${AWS_PARAMETER_TIERS.join(" | ")}`).option("--kms-key-id <id>", "aws: customer-managed KMS key id, ARN, or alias").option("--env-group <id>", "render: environment group ID (evg-…)").option("--fly-app <name>", "fly: app name, as `fly apps list` shows it").option("--secret-group <id>", "northflank: secret group ID (the slug in its URL)").option("--do-app <id>", "digitalocean: App Platform app ID (the UUID in its URL)").option("--component <name>", "digitalocean: write to one component's variables (omit for app-level)").option("--env-scope <scope>", `digitalocean: ${DIGITALOCEAN_ENV_SCOPES.join(" | ")}`).option("--heroku-app <name>", "heroku: app name, as `heroku apps` shows it (or its UUID)").option("--netlify-site <id>", "netlify: site API ID (the UUID under Project configuration)").option("--no-netlify-secret", "netlify: create readable variables instead of Netlify secrets (write-only)").option("--bunnyshell-environment <id>", "bunnyshell: environment ID (omit to write a project)").option("--no-bunnyshell-secret", "bunnyshell: create variables visible in the dashboard instead of secret ones").option("--gh-repo <owner/name>", "github-actions: repository, e.g. acme/storefront").option("--gh-environment <name>", "github-actions: write to one deployment environment's secrets (needs --gh-repo)").option("--gh-org <login>", "github-actions: write organization secrets instead of a repo's").option("--gh-visibility <v>", `github-actions org secrets: ${GITHUB_ACTIONS_VISIBILITIES.join(" | ")}`).option("--gh-repo-ids <ids>", "github-actions: comma-separated numeric repository IDs for --gh-visibility selected").option("--langgraph-deployment <id>", "langgraph-platform: deployment UUID (the one in its dashboard URL)").option("--gcp-prefix <prefix>", "gcp: prepended to every secret ID, e.g. prod-storefront-").option("--gcp-replication <policy>", `gcp: ${GCP_REPLICATION_POLICIES.join(" | ")}`, "automatic").option("--gcp-locations <list>", "gcp: regions for user-managed replication, e.g. us-east1").option("--gcp-kms-key <name>", "gcp: Cloud KMS key (projects/…/cryptoKeys/…)").option("--gcp-prune-versions", "gcp: destroy the version each push supersedes, keeping one active version");
|
|
12795
|
+
return command.option("--project <id>", "vercel: project id or name · cloudflare-pages / northflank: project name or slug · bunnyshell: project ID (writes variables new environments inherit)").option("--target <list>", "vercel / cloudflare-pages: comma-separated deployment targets", "production").option("--git-branch <branch>", "vercel: restrict preview writes to one branch").option("--script <name>", "cloudflare-workers: Worker script name").option("--store-id <id>", "cloudflare-secrets-store: store ID (32 hex)").option("--scopes <list>", "cloudflare-secrets-store: comma-separated scopes", "workers").option("--railway-project <id>", "railway: project ID (a UUID)").option("--railway-environment <id>", "railway: environment ID (a UUID)").option("--service <id>", "railway: service ID (omit for the environment's shared variables) · render: service ID (srv-…, or crn-… for a cron job)").option("--skip-deploys", "railway: stage values without triggering a redeploy").option("--path <path>", "aws-parameter-store: hierarchy, e.g. /prod/storefront/ · aws-secrets-manager, azure-key-vault: name prefix").option("--layout <layout>", `aws-secrets-manager: ${AWS_SECRETS_MANAGER_LAYOUTS.join(" | ")}`).option("--secret-name <name>", "aws-secrets-manager: the secret a json-bundle writes to").option("--param-type <type>", `aws-parameter-store: ${AWS_PARAMETER_TYPES.join(" | ")}`).option("--tier <tier>", `aws-parameter-store: ${AWS_PARAMETER_TIERS.join(" | ")}`).option("--kms-key-id <id>", "aws: customer-managed KMS key id, ARN, or alias").option("--env-group <id>", "render: environment group ID (evg-…)").option("--fly-app <name>", "fly: app name, as `fly apps list` shows it").option("--secret-group <id>", "northflank: secret group ID (the slug in its URL)").option("--do-app <id>", "digitalocean: App Platform app ID (the UUID in its URL)").option("--component <name>", "digitalocean: write to one component's variables (omit for app-level)").option("--env-scope <scope>", `digitalocean: ${DIGITALOCEAN_ENV_SCOPES.join(" | ")}`).option("--heroku-app <name>", "heroku: app name, as `heroku apps` shows it (or its UUID)").option("--netlify-site <id>", "netlify: site API ID (the UUID under Project configuration)").option("--no-netlify-secret", "netlify: create readable variables instead of Netlify secrets (write-only)").option("--bunnyshell-environment <id>", "bunnyshell: environment ID (omit to write a project)").option("--no-bunnyshell-secret", "bunnyshell: create variables visible in the dashboard instead of secret ones").option("--gh-repo <owner/name>", "github-actions: repository, e.g. acme/storefront").option("--gh-environment <name>", "github-actions: write to one deployment environment's secrets (needs --gh-repo)").option("--gh-org <login>", "github-actions: write organization secrets instead of a repo's").option("--gh-visibility <v>", `github-actions org secrets: ${GITHUB_ACTIONS_VISIBILITIES.join(" | ")}`).option("--gh-repo-ids <ids>", "github-actions: comma-separated numeric repository IDs for --gh-visibility selected").option("--langgraph-deployment <id>", "langgraph-platform: deployment UUID (the one in its dashboard URL)").option("--gcp-prefix <prefix>", "gcp: prepended to every secret ID, e.g. prod-storefront-").option("--gcp-replication <policy>", `gcp: ${GCP_REPLICATION_POLICIES.join(" | ")}`, "automatic").option("--gcp-locations <list>", "gcp: regions for user-managed replication, e.g. us-east1").option("--gcp-kms-key <name>", "gcp: Cloud KMS key (projects/…/cryptoKeys/…)").option("--gcp-prune-versions", "gcp: destroy the version each push supersedes, keeping one active version").option("--vault <name>", "azure-key-vault: vault name, e.g. acme-prod").option("--name-mode <mode>", `azure-key-vault: ${AZURE_KEY_VAULT_NAME_MODES.join(" | ")} — Key Vault stores no underscores, so DATABASE_URL becomes DATABASE-URL unless you reject instead`);
|
|
12580
12796
|
}
|
|
12581
12797
|
/** Find a connection by id or name — nobody keeps `syc_…` ids in their head. */
|
|
12582
12798
|
async function resolveConnection(ctx, orgId, ref) {
|
|
@@ -12588,7 +12804,7 @@ async function resolveConnection(ctx, orgId, ref) {
|
|
|
12588
12804
|
}
|
|
12589
12805
|
function registerSyncCommands(program) {
|
|
12590
12806
|
const sync = program.command("sync").description("push environments to a third-party platform (Vercel, Cloudflare, …)");
|
|
12591
|
-
sync.command("connections").alias("conns").description("list destination accounts seekrit can push to").option("--org <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
12807
|
+
sync.command("connections").alias("conns").description("list destination accounts seekrit can push to").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (options) => {
|
|
12592
12808
|
const ctx = buildContext();
|
|
12593
12809
|
const ref = await resolveOrg(ctx, options.org);
|
|
12594
12810
|
const { connections } = await ctx.client.listSyncConnections(ref.id);
|
|
@@ -12600,7 +12816,7 @@ function registerSyncCommands(program) {
|
|
|
12600
12816
|
col("id", (c) => c.id)
|
|
12601
12817
|
], "no connections — add one with `seekrit sync connect`"));
|
|
12602
12818
|
});
|
|
12603
|
-
sync.command("connect").description("register a destination account (reads its API token from stdin)").option("--org <slug>").requiredOption("--name <name>", "what to call this account, e.g. acme-vercel").option("--provider <kind>", SYNC_PROVIDER_KINDS.join(" | "), "vercel").option("--team-id <id>", "vercel: Team id (omit for a personal account)").option("--token-kind <kind>", `railway: ${RAILWAY_TOKEN_KINDS.join(" | ")}`, "account").option("--account-id <id>", "cloudflare: account ID (32 hex, from the dashboard sidebar) · netlify: team slug or account ID").option("--region <region>", "aws: region ID, e.g. us-east-1").option("--access-key-id <id>", "aws: IAM access key ID (the secret key is read from stdin)").option("--base-url <url>", "github-actions: GitHub Enterprise Server API root (omit for github.com) · langgraph-platform: self-hosted LangSmith control-plane root").option("--project-id <id>", "gcp: project ID or number whose Secret Manager to write").option("--langgraph-region <region>", `langgraph-platform: ${LANGGRAPH_PLATFORM_REGIONS.join(" | ")} (omit for us)`).option("--langgraph-tenant <id>", "langgraph-platform: LangSmith workspace UUID (only an org-scoped key needs it)").action(async (options) => {
|
|
12819
|
+
sync.command("connect").description("register a destination account (reads its API token from stdin)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--name <name>", "what to call this account, e.g. acme-vercel").option("--provider <kind>", SYNC_PROVIDER_KINDS.join(" | "), "vercel").option("--team-id <id>", "vercel: Team id (omit for a personal account)").option("--token-kind <kind>", `railway: ${RAILWAY_TOKEN_KINDS.join(" | ")}`, "account").option("--account-id <id>", "cloudflare: account ID (32 hex, from the dashboard sidebar) · netlify: team slug or account ID").option("--region <region>", "aws: region ID, e.g. us-east-1").option("--access-key-id <id>", "aws: IAM access key ID (the secret key is read from stdin)").option("--azure-tenant-id <id>", "azure-key-vault: Entra Directory (tenant) ID, a GUID").option("--azure-client-id <id>", "azure-key-vault: Application (client) ID of the service principal, a GUID (its client secret is read from stdin)").option("--azure-cloud <cloud>", `azure-key-vault: ${AZURE_CLOUDS.join(" | ")}`, "public").option("--base-url <url>", "github-actions: GitHub Enterprise Server API root (omit for github.com) · langgraph-platform: self-hosted LangSmith control-plane root").option("--project-id <id>", "gcp: project ID or number whose Secret Manager to write").option("--langgraph-region <region>", `langgraph-platform: ${LANGGRAPH_PLATFORM_REGIONS.join(" | ")} (omit for us)`).option("--langgraph-tenant <id>", "langgraph-platform: LangSmith workspace UUID (only an org-scoped key needs it)").action(async (options) => {
|
|
12604
12820
|
const provider = assertProvider(options.provider);
|
|
12605
12821
|
const ctx = buildContext();
|
|
12606
12822
|
const ref = await resolveOrg(ctx, options.org);
|
|
@@ -12617,7 +12833,7 @@ function registerSyncCommands(program) {
|
|
|
12617
12833
|
});
|
|
12618
12834
|
console.error(`connected ${created.connection.name} (${created.connection.id})`);
|
|
12619
12835
|
});
|
|
12620
|
-
destinationOptions(sync.command("verify <connection>").description("check a stored credential against a destination").option("--org <slug>").option("--provider <kind>", SYNC_PROVIDER_KINDS.join(" | "), "vercel")).option("--json", "print the raw API response").action(async (connection, options) => {
|
|
12836
|
+
destinationOptions(sync.command("verify <connection>").description("check a stored credential against a destination").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--provider <kind>", SYNC_PROVIDER_KINDS.join(" | "), "vercel")).option("--json", "print the raw API response").action(async (connection, options) => {
|
|
12621
12837
|
const provider = assertProvider(options.provider);
|
|
12622
12838
|
const ctx = buildContext();
|
|
12623
12839
|
const ref = await resolveOrg(ctx, options.org);
|
|
@@ -12626,7 +12842,7 @@ function registerSyncCommands(program) {
|
|
|
12626
12842
|
emit(options, result, () => printFields([["result", result.ok ? "ok" : "failed"], ["error", result.error ?? null]]));
|
|
12627
12843
|
if (!result.ok) process.exitCode = 1;
|
|
12628
12844
|
});
|
|
12629
|
-
sync.command("disconnect <connection>").description("delete a destination account, its bindings, and its keypair").option("--org <slug>").option("--yes", "skip the confirmation prompt").action(async (connection, options) => {
|
|
12845
|
+
sync.command("disconnect <connection>").description("delete a destination account, its bindings, and its keypair").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation prompt").action(async (connection, options) => {
|
|
12630
12846
|
const ctx = buildContext();
|
|
12631
12847
|
const ref = await resolveOrg(ctx, options.org);
|
|
12632
12848
|
const conn = await resolveConnection(ctx, ref.id, connection);
|
|
@@ -12636,7 +12852,7 @@ function registerSyncCommands(program) {
|
|
|
12636
12852
|
await ctx.client.deleteSyncConnection(ref.id, conn.id);
|
|
12637
12853
|
console.error(`disconnected ${conn.name}`);
|
|
12638
12854
|
});
|
|
12639
|
-
sync.command("bindings").description("list which environments are syncing where").option("--org <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
12855
|
+
sync.command("bindings").description("list which environments are syncing where").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (options) => {
|
|
12640
12856
|
const ctx = buildContext();
|
|
12641
12857
|
const ref = await resolveOrg(ctx, options.org);
|
|
12642
12858
|
const [{ bindings }, { connections }] = await Promise.all([ctx.client.listSyncBindings(ref.id), ctx.client.listSyncConnections(ref.id)]);
|
|
@@ -12651,7 +12867,7 @@ function registerSyncCommands(program) {
|
|
|
12651
12867
|
col("id", (b) => b.id)
|
|
12652
12868
|
], "nothing is syncing — enable it with `seekrit sync enable`"));
|
|
12653
12869
|
});
|
|
12654
|
-
destinationOptions(sync.command("enable").description("start syncing one environment to a destination (lets seekrit decrypt it)").option("--org <slug>").requiredOption("--connection <name>", "destination account, by name or id").option("--app <slug>", "application slug (defaults to seekrit.json)").requiredOption("--env <slug>", "the environment to push").option("--provider <kind>", SYNC_PROVIDER_KINDS.join(" | "), "vercel")).option("--prefix <prefix>", "prepend this to every destination key name").option("--include <globs>", "comma-separated name globs to push (default: all)").option("--exclude <globs>", "comma-separated name globs to skip").option("--on-delete <action>", "delete | retain — what happens when a secret is removed", "delete").option("--mode <mode>", "auto (push on write) | manual", "auto").option("--acknowledge-decryption", "confirm that seekrit's servers may decrypt this environment to push it").action(async (options) => {
|
|
12870
|
+
destinationOptions(sync.command("enable").description("start syncing one environment to a destination (lets seekrit decrypt it)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--connection <name>", "destination account, by name or id").option("--app <slug>", "application slug (defaults to seekrit.json)").requiredOption("--env <slug>", "the environment to push").option("--provider <kind>", SYNC_PROVIDER_KINDS.join(" | "), "vercel")).option("--prefix <prefix>", "prepend this to every destination key name").option("--include <globs>", "comma-separated name globs to push (default: all)").option("--exclude <globs>", "comma-separated name globs to skip").option("--on-delete <action>", "delete | retain — what happens when a secret is removed", "delete").option("--mode <mode>", "auto (push on write) | manual", "auto").option("--acknowledge-decryption", "confirm that seekrit's servers may decrypt this environment to push it").action(async (options) => {
|
|
12655
12871
|
const provider = assertProvider(options.provider);
|
|
12656
12872
|
if (options.onDelete !== "delete" && options.onDelete !== "retain") fail("--on-delete must be delete or retain");
|
|
12657
12873
|
if (options.mode !== "auto" && options.mode !== "manual") fail("--mode must be auto or manual");
|
|
@@ -12690,26 +12906,26 @@ function registerSyncCommands(program) {
|
|
|
12690
12906
|
console.error(`syncing ${target.appSlug}/${target.envSlug} → ${conn.name} ${describeDestination(destination)} (${binding.id})`);
|
|
12691
12907
|
if (binding.mode === "manual") console.error("mode is manual — push with `seekrit sync run`");
|
|
12692
12908
|
});
|
|
12693
|
-
sync.command("pause <bindingId>").description("stop pushing on this binding without deleting it").option("--org <slug>").action(async (bindingId, options) => {
|
|
12909
|
+
sync.command("pause <bindingId>").description("stop pushing on this binding without deleting it").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (bindingId, options) => {
|
|
12694
12910
|
const ctx = buildContext();
|
|
12695
12911
|
const ref = await resolveOrg(ctx, options.org);
|
|
12696
12912
|
await ctx.client.updateSyncBinding(ref.id, bindingId, { enabled: false });
|
|
12697
12913
|
console.error(`${bindingId} paused`);
|
|
12698
12914
|
});
|
|
12699
|
-
sync.command("resume <bindingId>").description("start pushing on a paused binding again").option("--org <slug>").action(async (bindingId, options) => {
|
|
12915
|
+
sync.command("resume <bindingId>").description("start pushing on a paused binding again").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (bindingId, options) => {
|
|
12700
12916
|
const ctx = buildContext();
|
|
12701
12917
|
const ref = await resolveOrg(ctx, options.org);
|
|
12702
12918
|
await ctx.client.updateSyncBinding(ref.id, bindingId, { enabled: true });
|
|
12703
12919
|
console.error(`${bindingId} resumed`);
|
|
12704
12920
|
});
|
|
12705
|
-
sync.command("disable <bindingId>").alias("rm").description("stop syncing an environment and revoke seekrit's key for it").option("--org <slug>").option("--yes", "skip the confirmation prompt").action(async (bindingId, options) => {
|
|
12921
|
+
sync.command("disable <bindingId>").alias("rm").description("stop syncing an environment and revoke seekrit's key for it").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation prompt").action(async (bindingId, options) => {
|
|
12706
12922
|
const ctx = buildContext();
|
|
12707
12923
|
const ref = await resolveOrg(ctx, options.org);
|
|
12708
12924
|
await confirmDestructive(options.yes, `Delete binding ${bindingId}? Values already pushed stay on the destination.`);
|
|
12709
12925
|
await ctx.client.deleteSyncBinding(ref.id, bindingId);
|
|
12710
12926
|
console.error(`${bindingId} deleted`);
|
|
12711
12927
|
});
|
|
12712
|
-
sync.command("run <bindingId>").description("push now, synchronously, and report what landed").option("--org <slug>").option("--json", "print the raw API response").action(async (bindingId, options) => {
|
|
12928
|
+
sync.command("run <bindingId>").description("push now, synchronously, and report what landed").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (bindingId, options) => {
|
|
12713
12929
|
const ctx = buildContext();
|
|
12714
12930
|
const ref = await resolveOrg(ctx, options.org);
|
|
12715
12931
|
const { run } = await ctx.client.runSyncBinding(ref.id, bindingId);
|
|
@@ -12728,7 +12944,7 @@ function registerSyncCommands(program) {
|
|
|
12728
12944
|
});
|
|
12729
12945
|
if (run.status !== "succeeded") process.exitCode = 1;
|
|
12730
12946
|
});
|
|
12731
|
-
sync.command("runs").description("show the sync run history").option("--org <slug>").option("--binding <id>", "only runs of this binding").option("--json", "print the raw API response").action(async (options) => {
|
|
12947
|
+
sync.command("runs").description("show the sync run history").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--binding <id>", "only runs of this binding").option("--json", "print the raw API response").action(async (options) => {
|
|
12732
12948
|
const ctx = buildContext();
|
|
12733
12949
|
const ref = await resolveOrg(ctx, options.org);
|
|
12734
12950
|
const { runs } = await ctx.client.listSyncRuns(ref.id, options.binding);
|
|
@@ -12894,6 +13110,7 @@ async function runLogout() {
|
|
|
12894
13110
|
console.error("not signed in");
|
|
12895
13111
|
return;
|
|
12896
13112
|
}
|
|
13113
|
+
if (!sessionToken) console.error(config.token ? "signed out — the stored service token was removed from this machine" : "signed out");
|
|
12897
13114
|
if (sessionToken) {
|
|
12898
13115
|
const api = new SeekritClient({
|
|
12899
13116
|
baseUrl: process.env.SEEKRIT_API_URL ?? config.apiUrl ?? "https://api.seekrit.dev",
|
|
@@ -13111,9 +13328,10 @@ withTarget(secrets.command("restore <name> <version>").description("roll a secre
|
|
|
13111
13328
|
const { secret, restoredFrom } = await ctx.client.restoreSecret(orgId, envId, name, parseVersion(version));
|
|
13112
13329
|
console.error(`${name} restored from v${restoredFrom} — now v${secret.version}`);
|
|
13113
13330
|
});
|
|
13114
|
-
withTarget(secrets.command("rm <name>").description("delete a secret")).action(async (name, options) => {
|
|
13331
|
+
withTarget(secrets.command("rm <name>").description("delete a secret and its whole version history").option("-y, --yes", "skip the confirmation")).action(async (name, options) => {
|
|
13115
13332
|
const ctx = buildContext();
|
|
13116
|
-
const { orgId, envId } = await resolveEnvTarget(ctx, options);
|
|
13333
|
+
const { orgId, envId, label } = await resolveEnvTarget(ctx, options);
|
|
13334
|
+
await confirmDestructive(options.yes, `Delete ${name} from ${label}, including every earlier version? This cannot be undone.`);
|
|
13117
13335
|
await ctx.client.deleteSecret(orgId, envId, name);
|
|
13118
13336
|
console.error(`${name} deleted`);
|
|
13119
13337
|
});
|
|
@@ -13198,8 +13416,7 @@ async function materializeForRun(options) {
|
|
|
13198
13416
|
branch
|
|
13199
13417
|
}, dotenvVars);
|
|
13200
13418
|
} catch (err) {
|
|
13201
|
-
|
|
13202
|
-
console.error(`seekrit: continuing without seekrit-managed secrets: ${message}`);
|
|
13419
|
+
console.error(`seekrit: continuing without seekrit-managed secrets: ${describeError(err)}`);
|
|
13203
13420
|
const values = {};
|
|
13204
13421
|
const provenance = {};
|
|
13205
13422
|
overlayEnvFiles(values, provenance, envFiles);
|
|
@@ -13319,7 +13536,7 @@ async function reapStragglers(pids, signal) {
|
|
|
13319
13536
|
process.kill(pid, "SIGKILL");
|
|
13320
13537
|
} catch {}
|
|
13321
13538
|
}
|
|
13322
|
-
program.command("run").description("run a command with decrypted secrets injected (process env > .env > app > group)").passThroughOptions().option("--org <slug>").option("--app <slug>", "application slug (token path infers this)").option("--env <slug>", "environment slug (token path infers this)").option("--branch <slug>", "read a branch of that environment (or $SEEKRIT_BRANCH)").option("--with <group=env>", "override one group’s slice for this run", collectKv).option("--env-file <path>", "a .env file to overlay (repeatable; default .env)", collectList).option("--explain", "print where each variable resolved from (to stderr)").option("--no-interpolate", "leave ${OTHER_SECRET} references as literal text").option("--cache", "keep a last-known-good copy of the encrypted response and fall back to it when the API is unreachable (off by default; SEEKRIT_CACHE=1)").option("--cache-dir <path>", "where to keep it (default: $XDG_CACHE_HOME/seekrit)").option("--cache-max-age <duration>", "how stale a cached copy may be and still be used, e.g. 15m, 24h, 7d (default: 24h)").argument("<command...>", "command to run (prefix with -- to pass flags)").action(async (commandParts, options) => {
|
|
13539
|
+
program.command("run").description("run a command with decrypted secrets injected (process env > .env > app > group)").passThroughOptions().option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "application slug (token path infers this)").option("--env <slug>", "environment slug (token path infers this)").option("--branch <slug>", "read a branch of that environment (or $SEEKRIT_BRANCH)").option("--with <group=env>", "override one group’s slice for this run", collectKv).option("--env-file <path>", "a .env file to overlay (repeatable; default .env)", collectList).option("--explain", "print where each variable resolved from (to stderr)").option("--no-interpolate", "leave ${OTHER_SECRET} references as literal text").option("--cache", "keep a last-known-good copy of the encrypted response and fall back to it when the API is unreachable (off by default; SEEKRIT_CACHE=1)").option("--cache-dir <path>", "where to keep it (default: $XDG_CACHE_HOME/seekrit)").option("--cache-max-age <duration>", "how stale a cached copy may be and still be used, e.g. 15m, 24h, 7d (default: 24h)").argument("<command...>", "command to run (prefix with -- to pass flags)").action(async (commandParts, options) => {
|
|
13323
13540
|
const [cmd, ...args] = commandParts;
|
|
13324
13541
|
if (!cmd) fail("no command given");
|
|
13325
13542
|
const { values, provenance, interpolated, unresolvedRefs } = await materializeForRun(options);
|
|
@@ -13371,7 +13588,7 @@ program.command("run").description("run a command with decrypted secrets injecte
|
|
|
13371
13588
|
});
|
|
13372
13589
|
child.on("error", (err) => fail(`failed to start ${cmd}: ${err.message}`));
|
|
13373
13590
|
});
|
|
13374
|
-
program.command("export").description("print decrypted secrets (dotenv, json, or shell)").option("--org <slug>").option("--app <slug>", "application slug (token path infers this)").option("--env <slug>", "environment slug (token path infers this)").option("--branch <slug>", "read a branch of that environment (or $SEEKRIT_BRANCH)").option("--with <group=env>", "override one group’s slice", collectKv).option("--env-file <path>", "a .env file to overlay (repeatable; default .env)", collectList).option("--explain", "print where each variable resolved from (to stderr)").option("--no-interpolate", "leave ${OTHER_SECRET} references as literal text").option("--cache", "keep a last-known-good copy of the encrypted response and fall back to it when the API is unreachable (off by default; SEEKRIT_CACHE=1)").option("--cache-dir <path>", "where to keep it (default: $XDG_CACHE_HOME/seekrit)").option("--cache-max-age <duration>", "how stale a cached copy may be and still be used, e.g. 15m, 24h, 7d (default: 24h)").option("--format <format>", "dotenv | json | shell", "dotenv").action(async (options) => {
|
|
13591
|
+
program.command("export").description("print decrypted secrets (dotenv, json, or shell)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "application slug (token path infers this)").option("--env <slug>", "environment slug (token path infers this)").option("--branch <slug>", "read a branch of that environment (or $SEEKRIT_BRANCH)").option("--with <group=env>", "override one group’s slice", collectKv).option("--env-file <path>", "a .env file to overlay (repeatable; default .env)", collectList).option("--explain", "print where each variable resolved from (to stderr)").option("--no-interpolate", "leave ${OTHER_SECRET} references as literal text").option("--cache", "keep a last-known-good copy of the encrypted response and fall back to it when the API is unreachable (off by default; SEEKRIT_CACHE=1)").option("--cache-dir <path>", "where to keep it (default: $XDG_CACHE_HOME/seekrit)").option("--cache-max-age <duration>", "how stale a cached copy may be and still be used, e.g. 15m, 24h, 7d (default: 24h)").option("--format <format>", "dotenv | json | shell", "dotenv").action(async (options) => {
|
|
13375
13592
|
if (![
|
|
13376
13593
|
"dotenv",
|
|
13377
13594
|
"json",
|
|
@@ -13387,7 +13604,7 @@ program.command("export").description("print decrypted secrets (dotenv, json, or
|
|
|
13387
13604
|
registerAccessCommands(program);
|
|
13388
13605
|
registerHoneyTokenCommands(program);
|
|
13389
13606
|
const token = program.command("token").description("manage service tokens (CI, docker, agents)");
|
|
13390
|
-
token.command("create").description("create a service token (runtime, or --admin for provisioning); prints it once").requiredOption("--name <name>", "display name, e.g. ci-deploy").option("--org <slug>").option("--app <slug>", "application to bind the token to (runtime tokens)").option("--env <slug>", "environment to bind the token to (runtime tokens)").option("--admin", "mint an org-scoped admin token that can provision structure (no env binding required)").option("--allow <group=env>", "also grant an alternate group slice (for `run --with`)", collectKv).option("--no-grant", "skip auto-granting the env + composed group keys").action(async (options) => {
|
|
13607
|
+
token.command("create").description("create a service token (runtime, or --admin for provisioning); prints it once").requiredOption("--name <name>", "display name, e.g. ci-deploy").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "application to bind the token to (runtime tokens)").option("--env <slug>", "environment to bind the token to (runtime tokens)").option("--admin", "mint an org-scoped admin token that can provision structure (no env binding required)").option("--allow <group=env>", "also grant an alternate group slice (for `run --with`)", collectKv).option("--no-grant", "skip auto-granting the env + composed group keys").action(async (options) => {
|
|
13391
13608
|
const ctx = buildContext();
|
|
13392
13609
|
const role = options.admin ? "admin" : "member";
|
|
13393
13610
|
const boundToEnv = Boolean(options.app || options.env);
|
|
@@ -13436,7 +13653,7 @@ token.command("create").description("create a service token (runtime, or --admin
|
|
|
13436
13653
|
console.error(`${role} token created${granted ? " and granted" : ""} for ${scope} — save it now, it is not stored:`);
|
|
13437
13654
|
console.log(created.token);
|
|
13438
13655
|
});
|
|
13439
|
-
token.command("list").alias("ls").description("list service tokens").option("--org <slug>").option("--json", "print the raw API response").action(async (options) => {
|
|
13656
|
+
token.command("list").alias("ls").description("list service tokens").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--json", "print the raw API response").action(async (options) => {
|
|
13440
13657
|
const ctx = buildContext();
|
|
13441
13658
|
const orgRef = await resolveOrg(ctx, options.org);
|
|
13442
13659
|
const { tokens } = await ctx.client.listTokens(orgRef.id);
|
|
@@ -13450,15 +13667,17 @@ token.command("list").alias("ls").description("list service tokens").option("--o
|
|
|
13450
13667
|
col("id", (t) => t.id)
|
|
13451
13668
|
], "no service tokens — create one with `seekrit token create`"));
|
|
13452
13669
|
});
|
|
13453
|
-
token.command("revoke <tokenId>").description("revoke a service token").option("--org <slug>").action(async (tokenId, options) => {
|
|
13670
|
+
token.command("revoke <tokenId>").description("revoke a service token").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (tokenId, options) => {
|
|
13454
13671
|
const ctx = buildContext();
|
|
13455
13672
|
const orgRef = await resolveOrg(ctx, options.org);
|
|
13673
|
+
await confirmDestructive(options.yes, `Revoke ${tokenId}? Anything still presenting it stops resolving immediately.`);
|
|
13456
13674
|
await ctx.client.revokeToken(orgRef.id, tokenId);
|
|
13457
13675
|
console.error(`${tokenId} revoked`);
|
|
13458
13676
|
});
|
|
13459
|
-
token.command("delete <tokenId>").description("permanently delete a revoked service token (revoke it first)").option("--org <slug>").action(async (tokenId, options) => {
|
|
13677
|
+
token.command("delete <tokenId>").description("permanently delete a revoked service token (revoke it first)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("-y, --yes", "skip the confirmation").action(async (tokenId, options) => {
|
|
13460
13678
|
const ctx = buildContext();
|
|
13461
13679
|
const orgRef = await resolveOrg(ctx, options.org);
|
|
13680
|
+
await confirmDestructive(options.yes, `Permanently delete ${tokenId}? Its audit history keeps the id, but the token row is gone.`);
|
|
13462
13681
|
await ctx.client.deleteToken(orgRef.id, tokenId);
|
|
13463
13682
|
console.error(`${tokenId} deleted`);
|
|
13464
13683
|
});
|
|
@@ -13489,7 +13708,7 @@ registerSyncCommands(program);
|
|
|
13489
13708
|
registerBillingCommands(program);
|
|
13490
13709
|
const argv = process.argv.map((arg) => arg === "-v" ? "--version" : arg);
|
|
13491
13710
|
program.parseAsync(argv).catch((err) => {
|
|
13492
|
-
fail(
|
|
13711
|
+
fail(describeError(err));
|
|
13493
13712
|
});
|
|
13494
13713
|
//#endregion
|
|
13495
13714
|
export { verifyMessage as A, toBase64 as B, isServiceToken as C, importVerifyingKey as D, importSigningKey as E, kmsBlobKeyRef as F, kmsDecrypt as I, kmsEncrypt as L, generateMysqlCredential as M, generateDataKey as N, signMessage as O, generateEncryptKeyMaterial as P, wrapDek as R, createServiceToken as S, generateSigningKeyMaterial as T, parseBranchTtl as V, isTokenAuth as _, ensureM2mAdminToken as a, writeProjectConfig as b, kmsResolveKey as c, resolveAppEnv as d, resolveBranch as f, getDek as g, resolveOrg as h, materializeEnv as i, generatePostgresCredential as j, signatureKeyRef as k, kmsResolveRecipient as l, resolveGroup as m, fetchDecryptedSecrets as n, kmsCallerIdentity as o, resolveEnvTarget as p, fetchDecryptedVersion as r, kmsRecoverMaterial as s, encryptAndSetSecret as t, resolveApp as u, tryBuildContext as v, parseServiceToken as w, version as x, setFailThrows as y, generateDek as z };
|