@seekrit/cli 0.47.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 +225 -150
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -4986,7 +4986,7 @@ async function createAgentTaskToken() {
4986
4986
  }
4987
4987
  //#endregion
4988
4988
  //#region package.json
4989
- var version = "0.47.0";
4989
+ var version = "1.0.0";
4990
4990
  //#endregion
4991
4991
  //#region ../../packages/api-client/src/index.ts
4992
4992
  var SeekritApiError = class extends Error {
@@ -5765,7 +5765,18 @@ function parseDurationSeconds(input, flag) {
5765
5765
  d: 86400
5766
5766
  }[m[2] || "s"] ?? 1);
5767
5767
  }
5768
- /** 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
+ */
5769
5780
  function promptHidden(question) {
5770
5781
  const muted = new Writable({ write(_chunk, _encoding, callback) {
5771
5782
  callback();
@@ -5776,8 +5787,15 @@ function promptHidden(question) {
5776
5787
  output: muted,
5777
5788
  terminal: true
5778
5789
  });
5779
- 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
+ });
5780
5797
  rl.question("", (answer) => {
5798
+ answered = true;
5781
5799
  rl.close();
5782
5800
  process.stderr.write("\n");
5783
5801
  resolve(answer);
@@ -5853,9 +5871,23 @@ const CLI_CLIENT = `cli/${version}`;
5853
5871
  * `flag > env > .env` credential resolution. Empty for every command but
5854
5872
  * `seekrit run`, which loads `.env` before authenticating.
5855
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
+ }
5856
5888
  function tryBuildContext(dotenvVars = {}) {
5857
5889
  const config = readGlobalConfig();
5858
- const fromEnv = (key) => process.env[key] ?? dotenvVars[key];
5890
+ const fromEnv = (key) => present(process.env[key]) ?? present(dotenvVars[key]);
5859
5891
  const apiUrl = fromEnv("SEEKRIT_API_URL") ?? config.apiUrl ?? "https://api.seekrit.dev";
5860
5892
  const token = fromEnv("SEEKRIT_TOKEN") ?? config.token ?? config.sessionToken;
5861
5893
  const devUser = fromEnv("SEEKRIT_DEV_USER") ?? config.devUser;
@@ -5888,6 +5920,24 @@ function isTokenAuth(ctx) {
5888
5920
  return ctx.auth.type === "bearer" && isServiceToken(ctx.auth.token);
5889
5921
  }
5890
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
+ /**
5891
5941
  * Recover the calling principal's private key:
5892
5942
  * - service tokens carry their private key in the token string;
5893
5943
  * - users fetch their passphrase-encrypted key from the API and unlock it.
@@ -6150,7 +6200,7 @@ async function resolvePrincipal(ctx, orgId, options) {
6150
6200
  }
6151
6201
  /** The `--org/--app/--group/--env` selection every grant command shares. */
6152
6202
  function withEnvTarget(cmd) {
6153
- 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");
6154
6204
  }
6155
6205
  /**
6156
6206
  * Environment key grants — who can decrypt what.
@@ -6185,7 +6235,7 @@ function registerAccessCommands(program) {
6185
6235
  col("id", (g) => g.principalId)
6186
6236
  ], "nobody holds a key for this environment"));
6187
6237
  });
6188
- 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) => {
6189
6239
  if (!options.user === !options.token) fail("pass exactly one of --user or --token");
6190
6240
  const ctx = buildContext();
6191
6241
  const { orgId, envId, label } = await resolveEnvTarget(ctx, options);
@@ -6231,7 +6281,7 @@ function registerAccountCommands(program) {
6231
6281
  col("id", (s) => `${s.id}${s.id === currentSessionId ? " (this one)" : ""}`)
6232
6282
  ], options.all ? "no CLI sessions" : "no active CLI sessions (try --all)"));
6233
6283
  });
6234
- 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) => {
6235
6285
  const ctx = buildContext();
6236
6286
  const self = ctx.auth.type === "bearer" && isCliSessionToken(ctx.auth.token) && parseCliSessionToken(ctx.auth.token).sessionId === sessionId;
6237
6287
  await confirmDestructive(options.yes, self ? `${sessionId} is the session you are using right now — sign it out?` : `Sign out ${sessionId}?`);
@@ -6962,7 +7012,7 @@ async function kmsRecoverMaterial(ctx, orgId, keyId, version) {
6962
7012
  }
6963
7013
  function registerKmsCommands(program) {
6964
7014
  const kms = program.command("kms").description("managed keys for application-layer encryption & signing (client-side)");
6965
- 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) => {
6966
7016
  if (options.purpose !== "encrypt" && options.purpose !== "sign") fail("--purpose must be encrypt or sign");
6967
7017
  if (options.app && options.group) fail("pass at most one of --app or --group");
6968
7018
  const ctx = buildContext();
@@ -7015,7 +7065,7 @@ function registerKmsCommands(program) {
7015
7065
  const { key } = await ctx.client.createKmsKey(org.id, input);
7016
7066
  console.error(`created ${key.purpose} key ${key.name} (${key.id}), ${grants.length} grant(s)`);
7017
7067
  });
7018
- 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) => {
7019
7069
  const ctx = buildContext();
7020
7070
  const org = await resolveOrg(ctx, options.org);
7021
7071
  const { keys } = await ctx.client.listKmsKeys(org.id);
@@ -7029,7 +7079,7 @@ function registerKmsCommands(program) {
7029
7079
  console.log(`${k.name}\t${k.purpose}\tv${k.currentVersion}\t${scope}\t${k.id}${state}`);
7030
7080
  }
7031
7081
  });
7032
- 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) => {
7033
7083
  if (!options.user === !options.token) fail("pass exactly one of --user or --token");
7034
7084
  const ctx = buildContext();
7035
7085
  const org = await resolveOrg(ctx, options.org);
@@ -7043,19 +7093,20 @@ function registerKmsCommands(program) {
7043
7093
  });
7044
7094
  console.error(`granted ${key.name} to ${recipient.principalId}`);
7045
7095
  });
7046
- 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) => {
7047
7097
  if (!options.user === !options.token) fail("pass exactly one of --user or --token");
7048
7098
  const ctx = buildContext();
7049
7099
  const org = await resolveOrg(ctx, options.org);
7050
7100
  const key = await kmsResolveKey(ctx, org.id, options.key);
7051
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.`);
7052
7103
  await ctx.client.revokeKmsKey(org.id, key.id, {
7053
7104
  principalType: recipient.principalType,
7054
7105
  principalId: recipient.principalId
7055
7106
  });
7056
7107
  console.error(`revoked ${recipient.principalId} from ${key.name}`);
7057
7108
  });
7058
- 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) => {
7059
7110
  const ctx = buildContext();
7060
7111
  const org = await resolveOrg(ctx, options.org);
7061
7112
  const key = await kmsResolveKey(ctx, org.id, options.key);
@@ -7089,21 +7140,23 @@ function registerKmsCommands(program) {
7089
7140
  });
7090
7141
  console.error(`rotated ${rotated.name} to v${rotated.currentVersion} (${grants.length} grantees)`);
7091
7142
  });
7092
- 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) => {
7093
7144
  const ctx = buildContext();
7094
7145
  const org = await resolveOrg(ctx, options.org);
7095
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.`);
7096
7148
  await ctx.client.disableKmsKey(org.id, key.id);
7097
7149
  console.error(`disabled ${key.name}`);
7098
7150
  });
7099
- 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) => {
7100
7152
  const ctx = buildContext();
7101
7153
  const org = await resolveOrg(ctx, options.org);
7102
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.`);
7103
7156
  await ctx.client.deleteKmsKey(org.id, key.id);
7104
7157
  console.error(`deleted ${key.name}`);
7105
7158
  });
7106
- 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) => {
7107
7160
  const ctx = buildContext();
7108
7161
  const org = await resolveOrg(ctx, options.org);
7109
7162
  const key = await kmsResolveKey(ctx, org.id, options.key);
@@ -7116,7 +7169,7 @@ function registerKmsCommands(program) {
7116
7169
  }, plaintext, options.context ?? "");
7117
7170
  console.log(blob);
7118
7171
  });
7119
- 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) => {
7120
7173
  const ctx = buildContext();
7121
7174
  const org = await resolveOrg(ctx, options.org);
7122
7175
  const key = await kmsResolveKey(ctx, org.id, options.key);
@@ -7125,7 +7178,7 @@ function registerKmsCommands(program) {
7125
7178
  const { material } = await kmsRecoverMaterial(ctx, org.id, key.id, ref.version);
7126
7179
  console.log(await kmsDecrypt(material, blob, options.context ?? ""));
7127
7180
  });
7128
- 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) => {
7129
7182
  const ctx = buildContext();
7130
7183
  const org = await resolveOrg(ctx, options.org);
7131
7184
  const key = await kmsResolveKey(ctx, org.id, options.key);
@@ -7140,7 +7193,7 @@ function registerKmsCommands(program) {
7140
7193
  wrapped: dk.wrapped
7141
7194
  }));
7142
7195
  });
7143
- 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) => {
7144
7197
  const ctx = buildContext();
7145
7198
  const org = await resolveOrg(ctx, options.org);
7146
7199
  const key = await kmsResolveKey(ctx, org.id, options.key);
@@ -7149,7 +7202,7 @@ function registerKmsCommands(program) {
7149
7202
  const { material } = await kmsRecoverMaterial(ctx, org.id, key.id, ref.version);
7150
7203
  console.log(toBase64(await decryptDataKey(material, wrapped)));
7151
7204
  });
7152
- 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) => {
7153
7206
  const ctx = buildContext();
7154
7207
  const org = await resolveOrg(ctx, options.org);
7155
7208
  const key = await kmsResolveKey(ctx, org.id, options.key);
@@ -7162,7 +7215,7 @@ function registerKmsCommands(program) {
7162
7215
  version: currentVersion
7163
7216
  }, message));
7164
7217
  });
7165
- 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) => {
7166
7219
  const ctx = buildContext();
7167
7220
  const org = await resolveOrg(ctx, options.org);
7168
7221
  const key = await kmsResolveKey(ctx, org.id, options.key);
@@ -7264,7 +7317,7 @@ async function buildRecoveryConfig(ctx, orgId, thresholdRaw, custodianRefs) {
7264
7317
  }
7265
7318
  function registerRecoveryCommands(program) {
7266
7319
  const recovery = program.command("recovery").description("customer-controlled M-of-N recovery (zero-knowledge)");
7267
- 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) => {
7268
7321
  const ctx = buildContext();
7269
7322
  const org = await resolveOrg(ctx, options.org);
7270
7323
  const { recovery: status } = await ctx.client.getRecovery(org.id);
@@ -7278,7 +7331,7 @@ function registerRecoveryCommands(program) {
7278
7331
  for (const cst of status.custodians) console.log(` - ${cst.label ?? cst.principalId} (${cst.principalType}, share #${cst.shareIndex})`);
7279
7332
  if (status.coverage.unprotectedEnvIds.length > 0) console.log(`${status.coverage.unprotectedEnvIds.length} environment(s) not yet protected — run \`seekrit recovery sync\``);
7280
7333
  });
7281
- 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) => {
7282
7335
  const ctx = buildContext();
7283
7336
  const org = await resolveOrg(ctx, options.org);
7284
7337
  const config = await buildRecoveryConfig(ctx, org.id, options.threshold, options.custodian);
@@ -7291,12 +7344,12 @@ function registerRecoveryCommands(program) {
7291
7344
  console.error(`recovery-protected ${wrapped} environment(s) you can decrypt`);
7292
7345
  if (skipped > 0) console.error(`${skipped} environment(s) need another admin to run \`seekrit recovery sync\``);
7293
7346
  });
7294
- 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) => {
7295
7348
  const ctx = buildContext();
7296
7349
  const { wrapped, skipped } = await syncRecoveryGrants(ctx, (await resolveOrg(ctx, options.org)).id);
7297
7350
  console.error(`recovery-protected ${wrapped} environment(s); skipped ${skipped} you cannot decrypt`);
7298
7351
  });
7299
- 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) => {
7300
7353
  const ctx = buildContext();
7301
7354
  const org = await resolveOrg(ctx, options.org);
7302
7355
  const config = await buildRecoveryConfig(ctx, org.id, options.threshold, options.custodian);
@@ -7309,13 +7362,14 @@ function registerRecoveryCommands(program) {
7309
7362
  console.error(`re-wrapped ${wrapped} environment(s) you can decrypt to the new recovery key`);
7310
7363
  if (skipped > 0) console.error(`${skipped} environment(s) still need another admin to run \`seekrit recovery sync\``);
7311
7364
  });
7312
- 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) => {
7313
7366
  const ctx = buildContext();
7314
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.`);
7315
7369
  await ctx.client.disableRecovery(org.id);
7316
7370
  console.error("recovery disabled; recovery grants removed");
7317
7371
  });
7318
- 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) => {
7319
7373
  const ctx = buildContext();
7320
7374
  const org = await resolveOrg(ctx, options.org);
7321
7375
  const target = options.targetUser || options.targetToken ? await kmsResolveRecipient(ctx, org.id, {
@@ -7332,7 +7386,7 @@ function registerRecoveryCommands(program) {
7332
7386
  console.error(` custodians run: seekrit recovery approve ${request.id}`);
7333
7387
  console.error(` then the target: seekrit recovery complete ${request.id}`);
7334
7388
  });
7335
- 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) => {
7336
7390
  const ctx = buildContext();
7337
7391
  const org = await resolveOrg(ctx, options.org);
7338
7392
  const { request } = await ctx.client.getRecoveryRequest(org.id, requestId);
@@ -7345,7 +7399,7 @@ function registerRecoveryCommands(program) {
7345
7399
  });
7346
7400
  console.error(`contributed share #${myShare.shareIndex}: ${res.contributed}/${res.threshold} collected${res.quorumReached ? " — quorum reached" : ""}`);
7347
7401
  });
7348
- 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) => {
7349
7403
  const ctx = buildContext();
7350
7404
  const org = await resolveOrg(ctx, options.org);
7351
7405
  const { request, contributions, quorumReached } = await ctx.client.getRecoveryRequest(org.id, requestId);
@@ -7369,9 +7423,10 @@ function registerRecoveryCommands(program) {
7369
7423
  });
7370
7424
  console.error(`recovery complete: restored access to ${restored.length} environment(s)`);
7371
7425
  });
7372
- 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) => {
7373
7427
  const ctx = buildContext();
7374
7428
  const org = await resolveOrg(ctx, options.org);
7429
+ await confirmDestructive(options.yes, `Cancel recovery request ${requestId}? Approvals already collected are discarded.`);
7375
7430
  await ctx.client.cancelRecoveryRequest(org.id, requestId);
7376
7431
  console.error(`recovery request ${requestId} canceled`);
7377
7432
  });
@@ -7385,7 +7440,7 @@ function registerRecoveryCommands(program) {
7385
7440
  */
7386
7441
  function registerAppCommands(program) {
7387
7442
  const app = program.command("app").description("manage applications");
7388
- 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) => {
7389
7444
  const ctx = buildContext();
7390
7445
  const ref = await resolveOrg(ctx, options.org);
7391
7446
  const { apps } = await ctx.client.listApps(ref.id);
@@ -7396,7 +7451,7 @@ function registerAppCommands(program) {
7396
7451
  col("id", (a) => a.id)
7397
7452
  ], "no applications — create one with `seekrit app create`"));
7398
7453
  });
7399
- 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) => {
7400
7455
  const ctx = buildContext();
7401
7456
  const ref = await resolveApp(ctx, {
7402
7457
  org: options.org,
@@ -7432,7 +7487,7 @@ function registerAppCommands(program) {
7432
7487
  }
7433
7488
  });
7434
7489
  });
7435
- 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) => {
7436
7491
  const ctx = buildContext();
7437
7492
  const orgRef = await resolveOrg(ctx, options.org);
7438
7493
  const created = await ctx.client.createApp(orgRef.id, {
@@ -7441,7 +7496,7 @@ function registerAppCommands(program) {
7441
7496
  });
7442
7497
  console.error(`created app ${created.app.slug} (${created.app.id})`);
7443
7498
  });
7444
- 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) => {
7445
7500
  const ctx = buildContext();
7446
7501
  const ref = await resolveApp(ctx, {
7447
7502
  org: options.org,
@@ -7450,7 +7505,7 @@ function registerAppCommands(program) {
7450
7505
  const { app: row } = await ctx.client.updateApp(ref.orgId, ref.id, { name: options.name });
7451
7506
  console.error(`renamed ${row.slug} to "${row.name}"`);
7452
7507
  });
7453
- 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) => {
7454
7509
  const ctx = buildContext();
7455
7510
  const ref = await resolveApp(ctx, {
7456
7511
  org: options.org,
@@ -7462,7 +7517,7 @@ function registerAppCommands(program) {
7462
7517
  console.error(`deleted app ${ref.slug}`);
7463
7518
  });
7464
7519
  const env = program.command("env").description("manage environments");
7465
- 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) => {
7466
7521
  const ctx = buildContext();
7467
7522
  const ref = await resolveApp(ctx, options);
7468
7523
  const { environments } = await ctx.client.getApp(ref.orgId, ref.id);
@@ -7473,7 +7528,7 @@ function registerAppCommands(program) {
7473
7528
  col("id", (e) => e.id)
7474
7529
  ], "no environments — create one with `seekrit env create`"));
7475
7530
  });
7476
- 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) => {
7477
7532
  const ctx = buildContext();
7478
7533
  const target = await resolveAppEnv(ctx, options);
7479
7534
  const [{ environment }, { groups }, { secrets }, { branches }] = await Promise.all([
@@ -7513,7 +7568,7 @@ function registerAppCommands(program) {
7513
7568
  }
7514
7569
  });
7515
7570
  });
7516
- 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) => {
7517
7572
  const ctx = buildContext();
7518
7573
  const orgRef = await resolveOrg(ctx, options.org);
7519
7574
  const { apps } = await ctx.client.listApps(orgRef.id);
@@ -7532,7 +7587,7 @@ function registerAppCommands(program) {
7532
7587
  });
7533
7588
  console.error(`created environment ${created.environment.slug} (${created.environment.id})`);
7534
7589
  });
7535
- 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) => {
7536
7591
  const ctx = buildContext();
7537
7592
  const target = await resolveEnvTarget(ctx, options);
7538
7593
  const { secrets } = await ctx.client.listSecrets(target.orgId, target.envId);
@@ -7541,7 +7596,7 @@ function registerAppCommands(program) {
7541
7596
  console.error(`deleted ${target.label}`);
7542
7597
  });
7543
7598
  const envGroups = env.command("groups").description("compose shared groups into an application environment");
7544
- 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) => {
7545
7600
  const ctx = buildContext();
7546
7601
  const target = await resolveAppEnv(ctx, options);
7547
7602
  const group = await resolveGroup(ctx, {
@@ -7554,7 +7609,7 @@ function registerAppCommands(program) {
7554
7609
  });
7555
7610
  console.error(`composed ${group.slug} into ${target.appSlug}/${target.envSlug}`);
7556
7611
  });
7557
- 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) => {
7558
7613
  const ctx = buildContext();
7559
7614
  const target = await resolveAppEnv(ctx, options);
7560
7615
  const { groups } = await ctx.client.listEnvGroups(target.orgId, target.envId);
@@ -7564,13 +7619,14 @@ function registerAppCommands(program) {
7564
7619
  col("name", (g) => g.name)
7565
7620
  ], "no groups composed into this environment"));
7566
7621
  });
7567
- 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) => {
7568
7623
  const ctx = buildContext();
7569
7624
  const target = await resolveAppEnv(ctx, options);
7570
7625
  const group = await resolveGroup(ctx, {
7571
7626
  org: options.org,
7572
7627
  group: options.group
7573
7628
  });
7629
+ await confirmDestructive(options.yes, `Remove ${group.slug} from ${target.appSlug}/${target.envSlug}? Everything it contributed disappears from that environment.`);
7574
7630
  await ctx.client.unlinkEnvGroup(target.orgId, target.envId, group.id);
7575
7631
  console.error(`removed ${group.slug} from ${target.appSlug}/${target.envSlug}`);
7576
7632
  });
@@ -8593,7 +8649,7 @@ async function decryptArchive(archive, key, filter) {
8593
8649
  }
8594
8650
  function registerArchiveCommands(program) {
8595
8651
  const archive = program.command("archive").description("export the whole org as one signed file, and open it offline");
8596
- 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) => {
8597
8653
  const ctx = buildContext();
8598
8654
  const ref = await resolveOrg(ctx, options.org);
8599
8655
  const input = {
@@ -8706,7 +8762,7 @@ function registerArchiveCommands(program) {
8706
8762
  } catch {}
8707
8763
  fail("none of the recovery shares in this archive unwrap with that key");
8708
8764
  });
8709
- 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) => {
8710
8766
  if (!options.out && !options.stdout) fail("choose a destination: --out <dir> to write files, or --stdout to print plaintext");
8711
8767
  if (![
8712
8768
  "dotenv",
@@ -8764,7 +8820,7 @@ function parseLimit(raw) {
8764
8820
  */
8765
8821
  function registerAuditCommands(program) {
8766
8822
  const audit = program.command("audit").description("read the org audit trail");
8767
- 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) => {
8768
8824
  if (options.action && !AUDIT_ACTIONS.includes(options.action)) fail(`unknown action "${options.action}" — see \`seekrit audit actions\``);
8769
8825
  const ctx = buildContext();
8770
8826
  const ref = await resolveOrg(ctx, options.org);
@@ -8847,7 +8903,7 @@ function resolveBaseCredential(opts) {
8847
8903
  function registerAwsCommands(program) {
8848
8904
  const aws = program.command("aws").description("temporary AWS credentials (STS AssumeRole, zero-knowledge)");
8849
8905
  const target = aws.command("target").description("manage AWS role targets");
8850
- 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) => {
8851
8907
  const ctx = buildContext();
8852
8908
  const org = await resolveOrg(ctx, options.org);
8853
8909
  const sessionPolicy = options.sessionPolicy ? readFileSync(options.sessionPolicy, "utf8").trim() : void 0;
@@ -8872,7 +8928,7 @@ function registerAwsCommands(program) {
8872
8928
  console.error("\nEnsure the role trusts the admin principal, then `seekrit aws lease`:\n");
8873
8929
  console.log(awsTrustPolicyInstructions(config));
8874
8930
  });
8875
- 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) => {
8876
8932
  const ctx = buildContext();
8877
8933
  const org = await resolveOrg(ctx, options.org);
8878
8934
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -8882,7 +8938,7 @@ function registerAwsCommands(program) {
8882
8938
  console.log(`${t.id}\t${t.name}\t${cfg.region}\t${cfg.roleArn}`);
8883
8939
  }
8884
8940
  });
8885
- 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) => {
8886
8942
  const ctx = buildContext();
8887
8943
  const org = await resolveOrg(ctx, options.org);
8888
8944
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -8892,13 +8948,14 @@ function registerAwsCommands(program) {
8892
8948
  if (cfg.provider !== "aws") fail("not an aws target (see `seekrit pg`/`seekrit ssh`)");
8893
8949
  console.log(awsTrustPolicyInstructions(cfg));
8894
8950
  });
8895
- 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) => {
8896
8952
  const ctx = buildContext();
8897
8953
  const org = await resolveOrg(ctx, options.org);
8954
+ await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
8898
8955
  await ctx.client.deleteLeaseTarget(org.id, targetId);
8899
8956
  console.error(`deleted ${targetId}`);
8900
8957
  });
8901
- 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) => {
8902
8959
  const ctx = buildContext();
8903
8960
  const org = await resolveOrg(ctx, options.org);
8904
8961
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -8928,7 +8985,7 @@ function registerAwsCommands(program) {
8928
8985
  console.log(`export AWS_REGION=${cred.region}`);
8929
8986
  }
8930
8987
  });
8931
- 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) => {
8932
8989
  const ctx = buildContext();
8933
8990
  const org = await resolveOrg(ctx, options.org);
8934
8991
  const { leases } = await ctx.client.listLeases(org.id);
@@ -8937,9 +8994,10 @@ function registerAwsCommands(program) {
8937
8994
  console.log(`${l.id}\t${l.status}\texpires ${l.expiresAt}`);
8938
8995
  }
8939
8996
  });
8940
- 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) => {
8941
8998
  const ctx = buildContext();
8942
8999
  const org = await resolveOrg(ctx, options.org);
9000
+ await confirmDestructive(options.yes, `Revoke lease ${leaseId}? Credentials already issued stay valid until they expire.`);
8943
9001
  await ctx.client.revokeLease(org.id, leaseId);
8944
9002
  console.error(`revoked ${leaseId} (issued credentials remain valid until they expire)`);
8945
9003
  });
@@ -8960,7 +9018,7 @@ function usageLine(usage) {
8960
9018
  */
8961
9019
  function registerBillingCommands(program) {
8962
9020
  const billing = program.command("billing").description("plan, usage, and subscription");
8963
- 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) => {
8964
9022
  const ctx = buildContext();
8965
9023
  const ref = await resolveOrg(ctx, options.org);
8966
9024
  const info = await ctx.client.getBilling(ref.id);
@@ -8987,7 +9045,7 @@ function registerBillingCommands(program) {
8987
9045
  }
8988
9046
  });
8989
9047
  });
8990
- 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) => {
8991
9049
  const ctx = buildContext();
8992
9050
  const ref = await resolveOrg(ctx, options.org);
8993
9051
  const info = await ctx.client.getBilling(ref.id);
@@ -8997,7 +9055,7 @@ function registerBillingCommands(program) {
8997
9055
  col("source", (e) => e.source)
8998
9056
  ]));
8999
9057
  });
9000
- 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) => {
9001
9059
  if (!PLAN_FAMILY_IDS.includes(family)) fail(`unknown plan "${family}" — one of: ${PLAN_FAMILY_IDS.join(", ")}`);
9002
9060
  const ctx = buildContext();
9003
9061
  const ref = await resolveOrg(ctx, options.org);
@@ -9005,14 +9063,14 @@ function registerBillingCommands(program) {
9005
9063
  console.error("open this to complete checkout:");
9006
9064
  console.log(url);
9007
9065
  });
9008
- 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) => {
9009
9067
  const ctx = buildContext();
9010
9068
  const ref = await resolveOrg(ctx, options.org);
9011
9069
  const { url } = await ctx.client.openBillingPortal(ref.id);
9012
9070
  console.error("open this to manage billing:");
9013
9071
  console.log(url);
9014
9072
  });
9015
- 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) => {
9016
9074
  const ctx = buildContext();
9017
9075
  const ref = await resolveOrg(ctx, options.org);
9018
9076
  await confirmDestructive(options.yes, `Cancel ${ref.slug}'s subscription and move it to the Free plan?`);
@@ -9055,7 +9113,7 @@ async function resolveBranchParent(ctx, opts) {
9055
9113
  */
9056
9114
  function registerBranchCommands(program) {
9057
9115
  const branch = program.command("branch").description("ephemeral per-PR / preview configs layered on an environment");
9058
- 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) => {
9059
9117
  const ctx = buildContext();
9060
9118
  const parent = await resolveBranchParent(ctx, {
9061
9119
  org: options.org,
@@ -9093,7 +9151,7 @@ function registerBranchCommands(program) {
9093
9151
  console.error(created.branch.expiresAt ? `expires ${created.branch.expiresAt}` : "no expiry — delete it explicitly when the PR closes");
9094
9152
  if (grants.length > 0) console.error(`shared with ${grants.length} other reader(s)`);
9095
9153
  });
9096
- 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) => {
9097
9155
  const ctx = buildContext();
9098
9156
  const branches = options.env ? await (async () => {
9099
9157
  const parent = await resolveAppEnv(ctx, options);
@@ -9109,10 +9167,11 @@ function registerBranchCommands(program) {
9109
9167
  col("id", (b) => b.id)
9110
9168
  ], "no branches — create one with `seekrit branch create`"));
9111
9169
  });
9112
- 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) => {
9113
9171
  const ctx = buildContext();
9114
9172
  const app = await resolveApp(ctx, options);
9115
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?`);
9116
9175
  await ctx.client.deleteBranch(app.orgId, target.id);
9117
9176
  console.error(`deleted branch ${app.slug}#${target.slug}`);
9118
9177
  });
@@ -9325,7 +9384,7 @@ function resolveServiceAccountKey(opts) {
9325
9384
  function registerGcpCommands(program) {
9326
9385
  const gcp = program.command("gcp").description("temporary GCP credentials (IAM generateAccessToken, zero-knowledge)");
9327
9386
  const target = gcp.command("target").description("manage GCP service-account targets");
9328
- 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) => {
9329
9388
  const ctx = buildContext();
9330
9389
  const org = await resolveOrg(ctx, options.org);
9331
9390
  const config = {
@@ -9348,7 +9407,7 @@ function registerGcpCommands(program) {
9348
9407
  console.error("\nGrant the source SA the token-creator role, then `seekrit gcp lease`:\n");
9349
9408
  console.log(gcpSetupInstructions(config));
9350
9409
  });
9351
- 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) => {
9352
9411
  const ctx = buildContext();
9353
9412
  const org = await resolveOrg(ctx, options.org);
9354
9413
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -9358,7 +9417,7 @@ function registerGcpCommands(program) {
9358
9417
  console.log(`${t.id}\t${t.name}\t${cfg.serviceAccount}`);
9359
9418
  }
9360
9419
  });
9361
- 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) => {
9362
9421
  const ctx = buildContext();
9363
9422
  const org = await resolveOrg(ctx, options.org);
9364
9423
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -9368,13 +9427,14 @@ function registerGcpCommands(program) {
9368
9427
  if (cfg.provider !== "gcp") fail("not a gcp target (see `seekrit aws`/`seekrit ssh`)");
9369
9428
  console.log(gcpSetupInstructions(cfg));
9370
9429
  });
9371
- 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) => {
9372
9431
  const ctx = buildContext();
9373
9432
  const org = await resolveOrg(ctx, options.org);
9433
+ await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
9374
9434
  await ctx.client.deleteLeaseTarget(org.id, targetId);
9375
9435
  console.error(`deleted ${targetId}`);
9376
9436
  });
9377
- 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) => {
9378
9438
  const ctx = buildContext();
9379
9439
  const org = await resolveOrg(ctx, options.org);
9380
9440
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -9399,7 +9459,7 @@ function registerGcpCommands(program) {
9399
9459
  console.log(`export GOOGLE_OAUTH_ACCESS_TOKEN=${cred.accessToken}`);
9400
9460
  }
9401
9461
  });
9402
- 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) => {
9403
9463
  const ctx = buildContext();
9404
9464
  const org = await resolveOrg(ctx, options.org);
9405
9465
  const { leases } = await ctx.client.listLeases(org.id);
@@ -9408,9 +9468,10 @@ function registerGcpCommands(program) {
9408
9468
  console.log(`${l.id}\t${l.status}\texpires ${l.expiresAt}`);
9409
9469
  }
9410
9470
  });
9411
- 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) => {
9412
9472
  const ctx = buildContext();
9413
9473
  const org = await resolveOrg(ctx, options.org);
9474
+ await confirmDestructive(options.yes, `Revoke lease ${leaseId}? Tokens already issued stay valid until they expire.`);
9414
9475
  await ctx.client.revokeLease(org.id, leaseId);
9415
9476
  console.error(`revoked ${leaseId} (issued tokens remain valid until they expire)`);
9416
9477
  });
@@ -9424,7 +9485,7 @@ function registerGcpCommands(program) {
9424
9485
  */
9425
9486
  function registerGroupCommands(program) {
9426
9487
  const group = program.command("group").description("manage shared groups (reusable secret bags)");
9427
- 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) => {
9428
9489
  const ctx = buildContext();
9429
9490
  const ref = await resolveOrg(ctx, options.org);
9430
9491
  const { groups } = await ctx.client.listGroups(ref.id);
@@ -9435,7 +9496,7 @@ function registerGroupCommands(program) {
9435
9496
  col("id", (g) => g.id)
9436
9497
  ], "no groups — create one with `seekrit group create`"));
9437
9498
  });
9438
- 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) => {
9439
9500
  const ctx = buildContext();
9440
9501
  const ref = await resolveGroup(ctx, {
9441
9502
  org: options.org,
@@ -9461,7 +9522,7 @@ function registerGroupCommands(program) {
9461
9522
  ], "no environments — create one with `seekrit group env create`");
9462
9523
  });
9463
9524
  });
9464
- 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) => {
9465
9526
  const ctx = buildContext();
9466
9527
  const orgRef = await resolveOrg(ctx, options.org);
9467
9528
  const created = await ctx.client.createGroup(orgRef.id, {
@@ -9470,7 +9531,7 @@ function registerGroupCommands(program) {
9470
9531
  });
9471
9532
  console.error(`created group ${created.group.slug} (${created.group.id})`);
9472
9533
  });
9473
- 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) => {
9474
9535
  const ctx = buildContext();
9475
9536
  const ref = await resolveGroup(ctx, {
9476
9537
  org: options.org,
@@ -9479,7 +9540,7 @@ function registerGroupCommands(program) {
9479
9540
  const { group: row } = await ctx.client.updateGroup(ref.orgId, ref.id, { name: options.name });
9480
9541
  console.error(`renamed ${row.slug} to "${row.name}"`);
9481
9542
  });
9482
- 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) => {
9483
9544
  const ctx = buildContext();
9484
9545
  const ref = await resolveGroup(ctx, {
9485
9546
  org: options.org,
@@ -9491,7 +9552,7 @@ function registerGroupCommands(program) {
9491
9552
  console.error(`deleted group ${ref.slug}`);
9492
9553
  });
9493
9554
  const groupEnv = group.command("env").description("manage a group’s environments (per-slug value sets / variants)");
9494
- 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) => {
9495
9556
  const ctx = buildContext();
9496
9557
  const ref = await resolveGroup(ctx, options);
9497
9558
  const { environments } = await ctx.client.listGroupEnvs(ref.orgId, ref.id);
@@ -9502,7 +9563,7 @@ function registerGroupCommands(program) {
9502
9563
  col("id", (e) => e.id)
9503
9564
  ], "no environments — create one with `seekrit group env create`"));
9504
9565
  });
9505
- 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) => {
9506
9567
  const ctx = buildContext();
9507
9568
  const groupRef = await resolveGroup(ctx, {
9508
9569
  org: options.org,
@@ -9534,7 +9595,7 @@ function registerGroupCommands(program) {
9534
9595
  */
9535
9596
  function registerHoneyTokenCommands(program) {
9536
9597
  const honey = program.command("honey-token").description("plant decoy credentials that alert when anyone tries to use them");
9537
- 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) => {
9538
9599
  const ctx = buildContext();
9539
9600
  const orgRef = await resolveOrg(ctx, options.org);
9540
9601
  const created = await createHoneyToken();
@@ -9547,7 +9608,7 @@ function registerHoneyTokenCommands(program) {
9547
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.");
9548
9609
  console.log(created.token);
9549
9610
  });
9550
- 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) => {
9551
9612
  const ctx = buildContext();
9552
9613
  const orgRef = await resolveOrg(ctx, options.org);
9553
9614
  const { honeyTokens } = await ctx.client.listHoneyTokens(orgRef.id);
@@ -9560,7 +9621,7 @@ function registerHoneyTokenCommands(program) {
9560
9621
  col("id", (t) => t.id)
9561
9622
  ], "no decoys planted — create one with `seekrit honey-token create`"));
9562
9623
  });
9563
- 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) => {
9564
9625
  const ctx = buildContext();
9565
9626
  const orgRef = await resolveOrg(ctx, options.org);
9566
9627
  await confirmDestructive(options.yes, `Delete ${honeyTokenId}? Wherever you planted it goes back to being unwatched — pull the bait too.`);
@@ -9584,7 +9645,7 @@ function collectHeader(value, acc = {}) {
9584
9645
  */
9585
9646
  function registerLogSinkCommands(program) {
9586
9647
  const sink = program.command("log-sink").description("stream the audit trail to your own OTLP collector (SIEM)");
9587
- 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) => {
9588
9649
  const ctx = buildContext();
9589
9650
  const ref = await resolveOrg(ctx, options.org);
9590
9651
  const { sink: config } = await ctx.client.getLogSink(ref.id);
@@ -9603,7 +9664,7 @@ function registerLogSinkCommands(program) {
9603
9664
  ]);
9604
9665
  });
9605
9666
  });
9606
- 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) => {
9607
9668
  if (options.header && options.clearHeaders) fail("pass either --header or --clear-headers, not both");
9608
9669
  const ctx = buildContext();
9609
9670
  const ref = await resolveOrg(ctx, options.org);
@@ -9615,7 +9676,7 @@ function registerLogSinkCommands(program) {
9615
9676
  });
9616
9677
  console.error(`log sink → ${config.endpoint} (${config.enabled ? "enabled" : "disabled"}) — test it with \`seekrit log-sink test\``);
9617
9678
  });
9618
- 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) => {
9619
9680
  const ctx = buildContext();
9620
9681
  const ref = await resolveOrg(ctx, options.org);
9621
9682
  const result = await ctx.client.testLogSink(ref.id);
@@ -9628,7 +9689,7 @@ function registerLogSinkCommands(program) {
9628
9689
  });
9629
9690
  if (!result.ok) process.exitCode = 1;
9630
9691
  });
9631
- 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) => {
9632
9693
  const ctx = buildContext();
9633
9694
  const ref = await resolveOrg(ctx, options.org);
9634
9695
  await confirmDestructive(options.yes, `Remove ${ref.slug}'s audit log export?`);
@@ -9762,7 +9823,7 @@ function resolveAdminUri(uri) {
9762
9823
  function registerMongoCommands(program) {
9763
9824
  const mongo = program.command("mongodb").description("temporary MongoDB credentials (createUser, zero-knowledge delivery)");
9764
9825
  const target = mongo.command("target").description("manage MongoDB targets");
9765
- 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) => {
9766
9827
  const ctx = buildContext();
9767
9828
  const org = await resolveOrg(ctx, options.org);
9768
9829
  const adminUri = resolveAdminUri(options.uri);
@@ -9793,7 +9854,7 @@ function registerMongoCommands(program) {
9793
9854
  console.error("\nEnsure a provisioning user exists, then `seekrit mongodb lease`:\n");
9794
9855
  console.log(mongoAdminSetupInstructions(config));
9795
9856
  });
9796
- 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) => {
9797
9858
  const ctx = buildContext();
9798
9859
  const org = await resolveOrg(ctx, options.org);
9799
9860
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -9804,13 +9865,14 @@ function registerMongoCommands(program) {
9804
9865
  console.log(`${t.id}\t${t.name}\t${host}\t${cfg.connection.database}\t${cfg.accessLevel ?? "readonly"}`);
9805
9866
  }
9806
9867
  });
9807
- 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) => {
9808
9869
  const ctx = buildContext();
9809
9870
  const org = await resolveOrg(ctx, options.org);
9871
+ await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
9810
9872
  await ctx.client.deleteLeaseTarget(org.id, targetId);
9811
9873
  console.error(`deleted ${targetId}`);
9812
9874
  });
9813
- 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) => {
9814
9876
  const ctx = buildContext();
9815
9877
  const org = await resolveOrg(ctx, options.org);
9816
9878
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -9829,7 +9891,7 @@ function registerMongoCommands(program) {
9829
9891
  if (options.json) console.log(JSON.stringify(cred, null, 2));
9830
9892
  else console.log(`export MONGODB_URI='${cred.uri}'`);
9831
9893
  });
9832
- 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) => {
9833
9895
  const ctx = buildContext();
9834
9896
  const org = await resolveOrg(ctx, options.org);
9835
9897
  const { leases } = await ctx.client.listLeases(org.id);
@@ -9838,9 +9900,10 @@ function registerMongoCommands(program) {
9838
9900
  console.log(`${l.id}\t${l.status}\texpires ${l.expiresAt}`);
9839
9901
  }
9840
9902
  });
9841
- 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) => {
9842
9904
  const ctx = buildContext();
9843
9905
  const org = await resolveOrg(ctx, options.org);
9906
+ await confirmDestructive(options.yes, `Revoke lease ${leaseId} now? Its MongoDB user is dropped immediately.`);
9844
9907
  await ctx.client.revokeLease(org.id, leaseId);
9845
9908
  console.error(`revoked ${leaseId} (the MongoDB user has been dropped)`);
9846
9909
  });
@@ -9922,7 +9985,7 @@ function generateUserName$1(prefix = "tmp") {
9922
9985
  function registerMysqlCommands(program) {
9923
9986
  const mysql = program.command("mysql").description("temporary MySQL/MariaDB credentials (short-lived, zero-knowledge)");
9924
9987
  const target = mysql.command("target").description("manage provisioning targets");
9925
- 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) => {
9926
9989
  const ctx = buildContext();
9927
9990
  const org = await resolveOrg(ctx, options.org);
9928
9991
  const executor = options.executor === "remote" ? "remote" : "in_do";
@@ -9964,7 +10027,7 @@ function registerMysqlCommands(program) {
9964
10027
  });
9965
10028
  console.error(`registered ${accessLevel} target ${created.name} (${created.id})`);
9966
10029
  });
9967
- 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) => {
9968
10031
  const ctx = buildContext();
9969
10032
  const org = await resolveOrg(ctx, options.org);
9970
10033
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -9974,13 +10037,14 @@ function registerMysqlCommands(program) {
9974
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}`);
9975
10038
  }
9976
10039
  });
9977
- 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) => {
9978
10041
  const ctx = buildContext();
9979
10042
  const org = await resolveOrg(ctx, options.org);
10043
+ await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
9980
10044
  await ctx.client.deleteLeaseTarget(org.id, targetId);
9981
10045
  console.error(`removed ${targetId}`);
9982
10046
  });
9983
- 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) => {
9984
10048
  const ctx = buildContext();
9985
10049
  const org = await resolveOrg(ctx, options.org);
9986
10050
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -10006,7 +10070,7 @@ function registerMysqlCommands(program) {
10006
10070
  }, null, 2));
10007
10071
  else console.log(url);
10008
10072
  });
10009
- 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) => {
10010
10074
  const ctx = buildContext();
10011
10075
  const org = await resolveOrg(ctx, options.org);
10012
10076
  const { leases } = await ctx.client.listLeases(org.id);
@@ -10015,9 +10079,10 @@ function registerMysqlCommands(program) {
10015
10079
  console.log(`${l.id}\t${l.status}\t${l.resourceRef}\texpires ${l.expiresAt}`);
10016
10080
  }
10017
10081
  });
10018
- 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) => {
10019
10083
  const ctx = buildContext();
10020
10084
  const org = await resolveOrg(ctx, options.org);
10085
+ await confirmDestructive(options.yes, `Revoke lease ${leaseId} now? Its MySQL user is dropped immediately.`);
10021
10086
  await ctx.client.revokeLease(org.id, leaseId);
10022
10087
  console.error(`revoked ${leaseId}`);
10023
10088
  });
@@ -10089,20 +10154,20 @@ function registerOrgCommands(program) {
10089
10154
  ]);
10090
10155
  });
10091
10156
  });
10092
- 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) => {
10093
10158
  const created = await buildContext().client.createOrg({
10094
10159
  name: options.name,
10095
10160
  slug: options.slug
10096
10161
  });
10097
10162
  console.error(`created org ${created.org.slug} (${created.org.id})`);
10098
10163
  });
10099
- 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) => {
10100
10165
  const ctx = buildContext();
10101
10166
  const ref = await resolveOrg(ctx, options.org);
10102
10167
  const { org: row } = await ctx.client.updateOrg(ref.id, { name: options.name });
10103
10168
  console.error(`renamed ${row.slug} to "${row.name}"`);
10104
10169
  });
10105
- 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) => {
10106
10171
  const ctx = buildContext();
10107
10172
  const ref = await resolveOrg(ctx, options.org);
10108
10173
  const { members } = await ctx.client.listMembers(ref.id);
@@ -10115,7 +10180,7 @@ function registerOrgCommands(program) {
10115
10180
  ], "no members"));
10116
10181
  });
10117
10182
  const invite = org.command("invite").description("manage pending invitations");
10118
- 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) => {
10119
10184
  const ctx = buildContext();
10120
10185
  const ref = await resolveOrg(ctx, options.org);
10121
10186
  const { invites } = await ctx.client.listInvites(ref.id);
@@ -10126,7 +10191,7 @@ function registerOrgCommands(program) {
10126
10191
  col("id", (i) => i.id)
10127
10192
  ], "no pending invitations"));
10128
10193
  });
10129
- 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) => {
10130
10195
  if (options.role !== "admin" && options.role !== "member") fail("--role must be admin or member");
10131
10196
  const ctx = buildContext();
10132
10197
  const ref = await resolveOrg(ctx, options.org);
@@ -10136,13 +10201,13 @@ function registerOrgCommands(program) {
10136
10201
  });
10137
10202
  console.error(`invited ${row.email} as ${row.role} (${row.id}) — they join when they first sign in`);
10138
10203
  });
10139
- 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) => {
10140
10205
  const ctx = buildContext();
10141
10206
  const ref = await resolveOrg(ctx, options.org);
10142
10207
  await ctx.client.revokeInvite(ref.id, inviteId);
10143
10208
  console.error(`${inviteId} revoked`);
10144
10209
  });
10145
- 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) => {
10146
10211
  const ctx = buildContext();
10147
10212
  const ref = await resolveOrg(ctx, options.org);
10148
10213
  if (options.set !== void 0 && options.set !== "required" && options.set !== "optional") fail("--set must be required or optional");
@@ -10155,7 +10220,7 @@ function registerOrgCommands(program) {
10155
10220
  console.log(policy.required ? "required for all members" : "optional");
10156
10221
  });
10157
10222
  });
10158
- 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) => {
10159
10224
  const ctx = buildContext();
10160
10225
  const ref = await resolveOrg(ctx, options.org);
10161
10226
  const [{ apps }, { groups }] = await Promise.all([ctx.client.listApps(ref.id), ctx.client.listGroups(ref.id)]);
@@ -11021,7 +11086,7 @@ function generateRoleName(prefix = "tmp") {
11021
11086
  function registerPgCommands(program) {
11022
11087
  const pg = program.command("pg").description("temporary Postgres credentials (short-lived, zero-knowledge)");
11023
11088
  const target = pg.command("target").description("manage provisioning targets");
11024
- 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) => {
11025
11090
  const ctx = buildContext();
11026
11091
  const org = await resolveOrg(ctx, options.org);
11027
11092
  const executor = options.executor === "remote" ? "remote" : "in_do";
@@ -11067,7 +11132,7 @@ function registerPgCommands(program) {
11067
11132
  console.log(bootstrap);
11068
11133
  }
11069
11134
  });
11070
- 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) => {
11071
11136
  const ctx = buildContext();
11072
11137
  const org = await resolveOrg(ctx, options.org);
11073
11138
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -11077,7 +11142,7 @@ function registerPgCommands(program) {
11077
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}`);
11078
11143
  }
11079
11144
  });
11080
- 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) => {
11081
11146
  const ctx = buildContext();
11082
11147
  const org = await resolveOrg(ctx, options.org);
11083
11148
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -11089,13 +11154,14 @@ function registerPgCommands(program) {
11089
11154
  if (!bootstrap) fail("this is a custom target — it has no generated setup SQL");
11090
11155
  console.log(bootstrap);
11091
11156
  });
11092
- 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) => {
11093
11158
  const ctx = buildContext();
11094
11159
  const org = await resolveOrg(ctx, options.org);
11160
+ await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
11095
11161
  await ctx.client.deleteLeaseTarget(org.id, targetId);
11096
11162
  console.error(`removed ${targetId}`);
11097
11163
  });
11098
- 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) => {
11099
11165
  const ctx = buildContext();
11100
11166
  const org = await resolveOrg(ctx, options.org);
11101
11167
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -11121,15 +11187,16 @@ function registerPgCommands(program) {
11121
11187
  }, null, 2));
11122
11188
  else console.log(url);
11123
11189
  });
11124
- 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) => {
11125
11191
  const ctx = buildContext();
11126
11192
  const org = await resolveOrg(ctx, options.org);
11127
11193
  const { leases } = await ctx.client.listLeases(org.id);
11128
11194
  for (const l of leases) console.log(`${l.id}\t${l.status}\t${l.resourceRef}\texpires ${l.expiresAt}`);
11129
11195
  });
11130
- 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) => {
11131
11197
  const ctx = buildContext();
11132
11198
  const org = await resolveOrg(ctx, options.org);
11199
+ await confirmDestructive(options.yes, `Revoke lease ${leaseId} now? Its Postgres role is dropped immediately.`);
11133
11200
  await ctx.client.revokeLease(org.id, leaseId);
11134
11201
  console.error(`revoked ${leaseId}`);
11135
11202
  });
@@ -11626,7 +11693,7 @@ function generateUserName(prefix = "tmp") {
11626
11693
  function registerRedisCommands(program) {
11627
11694
  const redis = program.command("redis").description("temporary Redis credentials (short-lived, zero-knowledge)");
11628
11695
  const target = redis.command("target").description("manage provisioning targets");
11629
- 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) => {
11630
11697
  const ctx = buildContext();
11631
11698
  const org = await resolveOrg(ctx, options.org);
11632
11699
  const executor = options.executor === "remote" ? "remote" : "in_do";
@@ -11667,7 +11734,7 @@ function registerRedisCommands(program) {
11667
11734
  });
11668
11735
  console.error(`registered ${accessLevel} target ${created.name} (${created.id})`);
11669
11736
  });
11670
- 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) => {
11671
11738
  const ctx = buildContext();
11672
11739
  const org = await resolveOrg(ctx, options.org);
11673
11740
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -11678,13 +11745,14 @@ function registerRedisCommands(program) {
11678
11745
  console.log(`${t.id}\t${t.name}\t${cfg.connection.host}:${cfg.connection.port}/${db}\t${cfg.accessLevel ?? "custom"}\t${cfg.executor}`);
11679
11746
  }
11680
11747
  });
11681
- 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) => {
11682
11749
  const ctx = buildContext();
11683
11750
  const org = await resolveOrg(ctx, options.org);
11751
+ await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
11684
11752
  await ctx.client.deleteLeaseTarget(org.id, targetId);
11685
11753
  console.error(`removed ${targetId}`);
11686
11754
  });
11687
- 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) => {
11688
11756
  const ctx = buildContext();
11689
11757
  const org = await resolveOrg(ctx, options.org);
11690
11758
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -11710,7 +11778,7 @@ function registerRedisCommands(program) {
11710
11778
  }, null, 2));
11711
11779
  else console.log(url);
11712
11780
  });
11713
- 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) => {
11714
11782
  const ctx = buildContext();
11715
11783
  const org = await resolveOrg(ctx, options.org);
11716
11784
  const { leases } = await ctx.client.listLeases(org.id);
@@ -11719,9 +11787,10 @@ function registerRedisCommands(program) {
11719
11787
  console.log(`${l.id}\t${l.status}\t${l.resourceRef}\texpires ${l.expiresAt}`);
11720
11788
  }
11721
11789
  });
11722
- 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) => {
11723
11791
  const ctx = buildContext();
11724
11792
  const org = await resolveOrg(ctx, options.org);
11793
+ await confirmDestructive(options.yes, `Revoke lease ${leaseId} now? Its Redis ACL user is deleted immediately.`);
11725
11794
  await ctx.client.revokeLease(org.id, leaseId);
11726
11795
  console.error(`revoked ${leaseId}`);
11727
11796
  });
@@ -11813,7 +11882,7 @@ function buildConfig$1(kind, options) {
11813
11882
  }
11814
11883
  function registerRotationCommands(program) {
11815
11884
  const rotation = program.command("rotation").description("managed rotation of stored secret values (scheduled, zero-knowledge)");
11816
- 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) => {
11817
11886
  const ctx = buildContext();
11818
11887
  const target = await resolveEnvTarget(ctx, options);
11819
11888
  const config = buildConfig$1(options.kind, options);
@@ -11843,7 +11912,7 @@ function registerRotationCommands(program) {
11843
11912
  else console.error(`first rotation: ${created.nextRotateAt}`);
11844
11913
  console.log(created.id);
11845
11914
  });
11846
- 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) => {
11847
11916
  const ctx = buildContext();
11848
11917
  const org = await resolveOrg(ctx, options.org);
11849
11918
  const { rotations } = await ctx.client.listRotations(org.id);
@@ -11857,43 +11926,44 @@ function registerRotationCommands(program) {
11857
11926
  }
11858
11927
  for (const r of rotations) console.log(rotationLine(r));
11859
11928
  });
11860
- 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) => {
11861
11930
  const ctx = buildContext();
11862
11931
  const r = await resolveRotation(ctx, (await resolveOrg(ctx, options.org)).id, ref);
11863
11932
  console.log(JSON.stringify(r, null, 2));
11864
11933
  });
11865
- 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) => {
11866
11935
  const ctx = buildContext();
11867
11936
  const org = await resolveOrg(ctx, options.org);
11868
11937
  const r = await resolveRotation(ctx, org.id, ref);
11869
11938
  const { version, rotatedAt } = await ctx.client.rotateSecretNow(org.id, r.id);
11870
11939
  console.error(`rotated ${r.secretName} at ${rotatedAt} — now at version ${version}. Read it with \`seekrit secrets get ${r.secretName}\`.`);
11871
11940
  });
11872
- 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) => {
11873
11942
  const ctx = buildContext();
11874
11943
  const org = await resolveOrg(ctx, options.org);
11875
11944
  const r = await resolveRotation(ctx, org.id, ref);
11876
11945
  await ctx.client.updateRotation(org.id, r.id, { status: "paused" });
11877
11946
  console.error(`paused rotation of ${r.secretName}`);
11878
11947
  });
11879
- 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) => {
11880
11949
  const ctx = buildContext();
11881
11950
  const org = await resolveOrg(ctx, options.org);
11882
11951
  const r = await resolveRotation(ctx, org.id, ref);
11883
11952
  const { rotation: updated } = await ctx.client.updateRotation(org.id, r.id, { status: "active" });
11884
11953
  console.error(`resumed rotation of ${r.secretName} — next ${updated.nextRotateAt}`);
11885
11954
  });
11886
- 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) => {
11887
11956
  const ctx = buildContext();
11888
11957
  const org = await resolveOrg(ctx, options.org);
11889
11958
  const r = await resolveRotation(ctx, org.id, ref);
11890
11959
  const { rotation: updated } = await ctx.client.updateRotation(org.id, r.id, { intervalSeconds: parseDurationSeconds(options.every, "--every") });
11891
11960
  console.error(`${updated.secretName} now rotates every ${formatInterval(updated.intervalSeconds)} — next ${updated.nextRotateAt}`);
11892
11961
  });
11893
- 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) => {
11894
11963
  const ctx = buildContext();
11895
11964
  const org = await resolveOrg(ctx, options.org);
11896
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.`);
11897
11967
  const { rotatorRevoked } = await ctx.client.disableRotation(org.id, r.id);
11898
11968
  console.error(`disabled rotation of ${r.secretName}${rotatorRevoked ? " — rotator key access revoked for this environment" : ""}`);
11899
11969
  });
@@ -12112,7 +12182,7 @@ function parseTtlSeconds(input) {
12112
12182
  function registerSshCommands(program) {
12113
12183
  const ssh = program.command("ssh").description("temporary SSH access (short-lived certificates, zero-knowledge)");
12114
12184
  const target = ssh.command("target").description("manage SSH CA targets");
12115
- 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) => {
12116
12186
  const ctx = buildContext();
12117
12187
  const org = await resolveOrg(ctx, options.org);
12118
12188
  const ca = await generateSshCaKeyPair(`seekrit-ca:${options.name}`);
@@ -12139,7 +12209,7 @@ function registerSshCommands(program) {
12139
12209
  console.error("\nInstall the CA on your hosts, then issue certs with `seekrit ssh lease`:\n");
12140
12210
  console.log(sshHostSetupInstructions(config));
12141
12211
  });
12142
- 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) => {
12143
12213
  const ctx = buildContext();
12144
12214
  const org = await resolveOrg(ctx, options.org);
12145
12215
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -12151,7 +12221,7 @@ function registerSshCommands(program) {
12151
12221
  console.log(`${t.id}\t${t.name}\t${where}\tprincipals=${principals}`);
12152
12222
  }
12153
12223
  });
12154
- 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) => {
12155
12225
  const ctx = buildContext();
12156
12226
  const org = await resolveOrg(ctx, options.org);
12157
12227
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -12161,13 +12231,14 @@ function registerSshCommands(program) {
12161
12231
  if (cfg.provider !== "ssh") fail("not an ssh target (see `seekrit pg`)");
12162
12232
  console.log(sshHostSetupInstructions(cfg));
12163
12233
  });
12164
- 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) => {
12165
12235
  const ctx = buildContext();
12166
12236
  const org = await resolveOrg(ctx, options.org);
12237
+ await confirmDestructive(options.yes, `Remove target ${targetId}? New leases against it stop working.`);
12167
12238
  await ctx.client.deleteLeaseTarget(org.id, targetId);
12168
12239
  console.error(`deleted ${targetId}`);
12169
12240
  });
12170
- 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) => {
12171
12242
  const ctx = buildContext();
12172
12243
  const org = await resolveOrg(ctx, options.org);
12173
12244
  const { targets } = await ctx.client.listLeaseTargets(org.id);
@@ -12205,7 +12276,7 @@ function registerSshCommands(program) {
12205
12276
  }, null, 2));
12206
12277
  else console.log(command);
12207
12278
  });
12208
- 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) => {
12209
12280
  const ctx = buildContext();
12210
12281
  const org = await resolveOrg(ctx, options.org);
12211
12282
  const { leases } = await ctx.client.listLeases(org.id);
@@ -12214,9 +12285,10 @@ function registerSshCommands(program) {
12214
12285
  console.log(`${l.id}\t${l.status}\texpires ${l.expiresAt}`);
12215
12286
  }
12216
12287
  });
12217
- 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) => {
12218
12289
  const ctx = buildContext();
12219
12290
  const org = await resolveOrg(ctx, options.org);
12291
+ await confirmDestructive(options.yes, `Revoke lease ${leaseId}? Certificates already issued stay valid until they expire.`);
12220
12292
  await ctx.client.revokeLease(org.id, leaseId);
12221
12293
  console.error(`revoked ${leaseId} (issued certs remain valid until they expire)`);
12222
12294
  });
@@ -12732,7 +12804,7 @@ async function resolveConnection(ctx, orgId, ref) {
12732
12804
  }
12733
12805
  function registerSyncCommands(program) {
12734
12806
  const sync = program.command("sync").description("push environments to a third-party platform (Vercel, Cloudflare, …)");
12735
- 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) => {
12736
12808
  const ctx = buildContext();
12737
12809
  const ref = await resolveOrg(ctx, options.org);
12738
12810
  const { connections } = await ctx.client.listSyncConnections(ref.id);
@@ -12744,7 +12816,7 @@ function registerSyncCommands(program) {
12744
12816
  col("id", (c) => c.id)
12745
12817
  ], "no connections — add one with `seekrit sync connect`"));
12746
12818
  });
12747
- 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("--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) => {
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) => {
12748
12820
  const provider = assertProvider(options.provider);
12749
12821
  const ctx = buildContext();
12750
12822
  const ref = await resolveOrg(ctx, options.org);
@@ -12761,7 +12833,7 @@ function registerSyncCommands(program) {
12761
12833
  });
12762
12834
  console.error(`connected ${created.connection.name} (${created.connection.id})`);
12763
12835
  });
12764
- 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) => {
12765
12837
  const provider = assertProvider(options.provider);
12766
12838
  const ctx = buildContext();
12767
12839
  const ref = await resolveOrg(ctx, options.org);
@@ -12770,7 +12842,7 @@ function registerSyncCommands(program) {
12770
12842
  emit(options, result, () => printFields([["result", result.ok ? "ok" : "failed"], ["error", result.error ?? null]]));
12771
12843
  if (!result.ok) process.exitCode = 1;
12772
12844
  });
12773
- 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) => {
12774
12846
  const ctx = buildContext();
12775
12847
  const ref = await resolveOrg(ctx, options.org);
12776
12848
  const conn = await resolveConnection(ctx, ref.id, connection);
@@ -12780,7 +12852,7 @@ function registerSyncCommands(program) {
12780
12852
  await ctx.client.deleteSyncConnection(ref.id, conn.id);
12781
12853
  console.error(`disconnected ${conn.name}`);
12782
12854
  });
12783
- 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) => {
12784
12856
  const ctx = buildContext();
12785
12857
  const ref = await resolveOrg(ctx, options.org);
12786
12858
  const [{ bindings }, { connections }] = await Promise.all([ctx.client.listSyncBindings(ref.id), ctx.client.listSyncConnections(ref.id)]);
@@ -12795,7 +12867,7 @@ function registerSyncCommands(program) {
12795
12867
  col("id", (b) => b.id)
12796
12868
  ], "nothing is syncing — enable it with `seekrit sync enable`"));
12797
12869
  });
12798
- 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) => {
12799
12871
  const provider = assertProvider(options.provider);
12800
12872
  if (options.onDelete !== "delete" && options.onDelete !== "retain") fail("--on-delete must be delete or retain");
12801
12873
  if (options.mode !== "auto" && options.mode !== "manual") fail("--mode must be auto or manual");
@@ -12834,26 +12906,26 @@ function registerSyncCommands(program) {
12834
12906
  console.error(`syncing ${target.appSlug}/${target.envSlug} → ${conn.name} ${describeDestination(destination)} (${binding.id})`);
12835
12907
  if (binding.mode === "manual") console.error("mode is manual — push with `seekrit sync run`");
12836
12908
  });
12837
- 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) => {
12838
12910
  const ctx = buildContext();
12839
12911
  const ref = await resolveOrg(ctx, options.org);
12840
12912
  await ctx.client.updateSyncBinding(ref.id, bindingId, { enabled: false });
12841
12913
  console.error(`${bindingId} paused`);
12842
12914
  });
12843
- 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) => {
12844
12916
  const ctx = buildContext();
12845
12917
  const ref = await resolveOrg(ctx, options.org);
12846
12918
  await ctx.client.updateSyncBinding(ref.id, bindingId, { enabled: true });
12847
12919
  console.error(`${bindingId} resumed`);
12848
12920
  });
12849
- 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) => {
12850
12922
  const ctx = buildContext();
12851
12923
  const ref = await resolveOrg(ctx, options.org);
12852
12924
  await confirmDestructive(options.yes, `Delete binding ${bindingId}? Values already pushed stay on the destination.`);
12853
12925
  await ctx.client.deleteSyncBinding(ref.id, bindingId);
12854
12926
  console.error(`${bindingId} deleted`);
12855
12927
  });
12856
- 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) => {
12857
12929
  const ctx = buildContext();
12858
12930
  const ref = await resolveOrg(ctx, options.org);
12859
12931
  const { run } = await ctx.client.runSyncBinding(ref.id, bindingId);
@@ -12872,7 +12944,7 @@ function registerSyncCommands(program) {
12872
12944
  });
12873
12945
  if (run.status !== "succeeded") process.exitCode = 1;
12874
12946
  });
12875
- 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) => {
12876
12948
  const ctx = buildContext();
12877
12949
  const ref = await resolveOrg(ctx, options.org);
12878
12950
  const { runs } = await ctx.client.listSyncRuns(ref.id, options.binding);
@@ -13038,6 +13110,7 @@ async function runLogout() {
13038
13110
  console.error("not signed in");
13039
13111
  return;
13040
13112
  }
13113
+ if (!sessionToken) console.error(config.token ? "signed out — the stored service token was removed from this machine" : "signed out");
13041
13114
  if (sessionToken) {
13042
13115
  const api = new SeekritClient({
13043
13116
  baseUrl: process.env.SEEKRIT_API_URL ?? config.apiUrl ?? "https://api.seekrit.dev",
@@ -13255,9 +13328,10 @@ withTarget(secrets.command("restore <name> <version>").description("roll a secre
13255
13328
  const { secret, restoredFrom } = await ctx.client.restoreSecret(orgId, envId, name, parseVersion(version));
13256
13329
  console.error(`${name} restored from v${restoredFrom} — now v${secret.version}`);
13257
13330
  });
13258
- 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) => {
13259
13332
  const ctx = buildContext();
13260
- 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.`);
13261
13335
  await ctx.client.deleteSecret(orgId, envId, name);
13262
13336
  console.error(`${name} deleted`);
13263
13337
  });
@@ -13342,8 +13416,7 @@ async function materializeForRun(options) {
13342
13416
  branch
13343
13417
  }, dotenvVars);
13344
13418
  } catch (err) {
13345
- const message = err instanceof Error ? err.message : String(err);
13346
- console.error(`seekrit: continuing without seekrit-managed secrets: ${message}`);
13419
+ console.error(`seekrit: continuing without seekrit-managed secrets: ${describeError(err)}`);
13347
13420
  const values = {};
13348
13421
  const provenance = {};
13349
13422
  overlayEnvFiles(values, provenance, envFiles);
@@ -13463,7 +13536,7 @@ async function reapStragglers(pids, signal) {
13463
13536
  process.kill(pid, "SIGKILL");
13464
13537
  } catch {}
13465
13538
  }
13466
- 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) => {
13467
13540
  const [cmd, ...args] = commandParts;
13468
13541
  if (!cmd) fail("no command given");
13469
13542
  const { values, provenance, interpolated, unresolvedRefs } = await materializeForRun(options);
@@ -13515,7 +13588,7 @@ program.command("run").description("run a command with decrypted secrets injecte
13515
13588
  });
13516
13589
  child.on("error", (err) => fail(`failed to start ${cmd}: ${err.message}`));
13517
13590
  });
13518
- 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) => {
13519
13592
  if (![
13520
13593
  "dotenv",
13521
13594
  "json",
@@ -13531,7 +13604,7 @@ program.command("export").description("print decrypted secrets (dotenv, json, or
13531
13604
  registerAccessCommands(program);
13532
13605
  registerHoneyTokenCommands(program);
13533
13606
  const token = program.command("token").description("manage service tokens (CI, docker, agents)");
13534
- 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) => {
13535
13608
  const ctx = buildContext();
13536
13609
  const role = options.admin ? "admin" : "member";
13537
13610
  const boundToEnv = Boolean(options.app || options.env);
@@ -13580,7 +13653,7 @@ token.command("create").description("create a service token (runtime, or --admin
13580
13653
  console.error(`${role} token created${granted ? " and granted" : ""} for ${scope} — save it now, it is not stored:`);
13581
13654
  console.log(created.token);
13582
13655
  });
13583
- 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) => {
13584
13657
  const ctx = buildContext();
13585
13658
  const orgRef = await resolveOrg(ctx, options.org);
13586
13659
  const { tokens } = await ctx.client.listTokens(orgRef.id);
@@ -13594,15 +13667,17 @@ token.command("list").alias("ls").description("list service tokens").option("--o
13594
13667
  col("id", (t) => t.id)
13595
13668
  ], "no service tokens — create one with `seekrit token create`"));
13596
13669
  });
13597
- 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) => {
13598
13671
  const ctx = buildContext();
13599
13672
  const orgRef = await resolveOrg(ctx, options.org);
13673
+ await confirmDestructive(options.yes, `Revoke ${tokenId}? Anything still presenting it stops resolving immediately.`);
13600
13674
  await ctx.client.revokeToken(orgRef.id, tokenId);
13601
13675
  console.error(`${tokenId} revoked`);
13602
13676
  });
13603
- 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) => {
13604
13678
  const ctx = buildContext();
13605
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.`);
13606
13681
  await ctx.client.deleteToken(orgRef.id, tokenId);
13607
13682
  console.error(`${tokenId} deleted`);
13608
13683
  });
@@ -13633,7 +13708,7 @@ registerSyncCommands(program);
13633
13708
  registerBillingCommands(program);
13634
13709
  const argv = process.argv.map((arg) => arg === "-v" ? "--version" : arg);
13635
13710
  program.parseAsync(argv).catch((err) => {
13636
- fail(err instanceof Error ? err.message : String(err));
13711
+ fail(describeError(err));
13637
13712
  });
13638
13713
  //#endregion
13639
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 };