@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.
Files changed (2) hide show
  1. package/dist/index.js +373 -154
  2. 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.46.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
- /** Prompt without echoing input (for passphrases). */
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
- const message = err instanceof Error ? err.message : String(err);
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(err instanceof Error ? err.message : String(err));
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 };