@vaultic-dev/cli 0.1.1 → 0.1.2

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 +30 -77
  2. package/package.json +10 -3
package/dist/index.js CHANGED
@@ -236,7 +236,10 @@ var SecretSummarySchema = z.object({
236
236
  /** freeform, normalized (lowercase + trim) tags assigned to this secret (spec: "Tags on
237
237
  * secrets + tag-based search"). Visible to anyone who can see the secret at all — not
238
238
  * sensitive, same as key names. */
239
- tags: z.array(z.string()).optional()
239
+ tags: z.array(z.string()).optional(),
240
+ /** number of entries in this secret's comment thread (see SecretComment) — surfaced on the
241
+ * row so the comment/mention feature is discoverable without opening the panel first. */
242
+ commentCount: z.number().int().optional()
240
243
  });
241
244
  var SecretRevealedSchema = SecretSummarySchema.extend({
242
245
  value: z.string()
@@ -268,6 +271,20 @@ var RotateSecretRequestSchema = z.object({
268
271
  * `secrets get KEY --previous`. Omit for no time limit. */
269
272
  graceWindow: z.string().optional()
270
273
  });
274
+ var ExecuteRotationRequestSchema = z.object({
275
+ /** rotation provider id, e.g. "postgres" — must be one the server implements */
276
+ provider: z.string(),
277
+ /** the secret's current (pre-rotation) resolved value, e.g. a connection string */
278
+ currentValue: z.string(),
279
+ /** provider-specific flags, e.g. { role, adminConnString, newPassword } for postgres */
280
+ options: z.record(z.string().optional()).optional()
281
+ });
282
+ var ExecuteRotationResponseSchema = z.object({
283
+ /** the new value to store as the secret's next version */
284
+ newValue: z.string(),
285
+ /** one-line human-readable description of what was rotated, printed by the CLI */
286
+ summary: z.string()
287
+ });
271
288
  function parseDurationMs(spec) {
272
289
  const match = /^(\d+)(s|m|h|d)$/.exec(spec.trim());
273
290
  if (!match)
@@ -1243,89 +1260,23 @@ async function syncCommand(opts) {
1243
1260
  import readline from "readline";
1244
1261
 
1245
1262
  // src/rotation/postgres.ts
1246
- import { randomBytes as randomBytes2 } from "crypto";
1247
- import { Client } from "pg";
1248
- var ROLE_NAME_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
1249
- function generatePassword(bytes = 24) {
1250
- return randomBytes2(bytes).toString("base64url");
1251
- }
1252
- function sqlLiteral(value) {
1253
- return `'${value.replace(/'/g, "''")}'`;
1254
- }
1255
1263
  var postgresRotationPlugin = {
1256
1264
  name: "postgres",
1257
1265
  describe() {
1258
- return "Rotates a PostgreSQL role's password via `ALTER ROLE ... WITH PASSWORD ...`. Treats the secret's current value as a postgres://user:password@host:port/db connection string. Options: --role <name> (defaults to the connection string's user), --admin-conn-string <url> (connect with different credentials than the value being rotated, e.g. a superuser, when the target role can't alter its own password), --new-password <value> (defaults to a random 32-character password).";
1266
+ return "Rotates a PostgreSQL role's password via `ALTER ROLE ... WITH PASSWORD ...`, executed server-side (the CLI never connects to the database directly). Treats the secret's current value as a postgres://user:password@host:port/db connection string. Options: --role <name> (defaults to the connection string's user), --admin-conn-string <url> (connect with different credentials than the value being rotated, e.g. a superuser, when the target role can't alter its own password), --new-password <value> (defaults to a random 32-character password).";
1259
1267
  },
1260
1268
  async rotate(ctx) {
1261
- let targetUrl;
1262
- try {
1263
- targetUrl = new URL(ctx.currentValue);
1264
- } catch {
1265
- throw new Error(
1266
- `Current value for "${ctx.key}" isn't a valid URL \u2014 expected a postgres://user:password@host:port/db connection string.`
1267
- );
1268
- }
1269
- if (targetUrl.protocol !== "postgres:" && targetUrl.protocol !== "postgresql:") {
1270
- throw new Error(
1271
- `Current value for "${ctx.key}" has scheme "${targetUrl.protocol}", expected postgres:// or postgresql://.`
1272
- );
1273
- }
1274
- const role = ctx.options.role ?? decodeURIComponent(targetUrl.username);
1275
- if (!role) throw new Error(`Could not determine the role to rotate for "${ctx.key}"; pass --role explicitly.`);
1276
- if (!ROLE_NAME_REGEX.test(role)) {
1277
- throw new Error(`Refusing to rotate role "${role}": expected [A-Za-z_][A-Za-z0-9_]* (use --role to override).`);
1278
- }
1279
- const connectionString = ctx.options.adminConnString ?? ctx.currentValue;
1280
- const newPassword = ctx.options.newPassword ?? generatePassword();
1281
- const client = new Client({ connectionString });
1282
- await client.connect();
1283
- try {
1284
- await client.query(`ALTER ROLE "${role}" WITH PASSWORD ${sqlLiteral(newPassword)}`);
1285
- } finally {
1286
- await client.end();
1287
- }
1288
- const newUrl = new URL(targetUrl.toString());
1289
- newUrl.password = newPassword;
1290
- return {
1291
- newValue: newUrl.toString(),
1292
- summary: `Rotated PostgreSQL role "${role}"'s password (ALTER ROLE) on ${targetUrl.hostname}:${targetUrl.port || "5432"}/${targetUrl.pathname.replace(/^\//, "")}`
1293
- };
1294
- }
1295
- };
1296
-
1297
- // src/rotation/types.ts
1298
- var RotationNotImplementedError = class extends Error {
1299
- };
1300
-
1301
- // src/rotation/stripe.ts
1302
- var stripeRotationPlugin = {
1303
- name: "stripe",
1304
- describe() {
1305
- return "STUB \u2014 not exercised against a live Stripe account in this environment. Documents the interface for restricted-key rotation (create new key with same scopes, store it, revoke the old one after the grace window). See packages/cli/src/rotation/stripe.ts for what a real implementation needs.";
1306
- },
1307
- async rotate() {
1308
- throw new RotationNotImplementedError(
1309
- "The stripe rotation plugin is a documented stub (no Stripe credentials available in this environment to build/verify a real integration) \u2014 see `packages/cli/src/rotation/stripe.ts`."
1310
- );
1311
- }
1312
- };
1313
-
1314
- // src/rotation/aws.ts
1315
- var awsIamRotationPlugin = {
1316
- name: "aws-iam",
1317
- describe() {
1318
- return "STUB \u2014 not exercised against a live AWS account in this environment. Documents the interface for IAM access-key rotation (CreateAccessKey for a new key while the old one stays active, then deactivate/delete the old key after the grace window). See packages/cli/src/rotation/aws.ts for what a real implementation needs.";
1319
- },
1320
- async rotate() {
1321
- throw new RotationNotImplementedError(
1322
- "The aws-iam rotation plugin is a documented stub (no AWS credentials available in this environment to build/verify a real integration) \u2014 see `packages/cli/src/rotation/aws.ts`."
1323
- );
1269
+ const response = await ctx.api.post(`${ctx.envBase}/secrets/${ctx.key}/rotate/execute`, {
1270
+ provider: "postgres",
1271
+ currentValue: ctx.currentValue,
1272
+ options: ctx.options
1273
+ });
1274
+ return { newValue: response.newValue, summary: response.summary };
1324
1275
  }
1325
1276
  };
1326
1277
 
1327
1278
  // src/rotation/index.ts
1328
- var PLUGINS = [postgresRotationPlugin, stripeRotationPlugin, awsIamRotationPlugin];
1279
+ var PLUGINS = [postgresRotationPlugin];
1329
1280
  function getRotationPlugin(name) {
1330
1281
  const plugin = PLUGINS.find((p) => p.name === name);
1331
1282
  if (!plugin) {
@@ -1443,7 +1394,9 @@ async function secretsRotateCommand(key, value, opts) {
1443
1394
  const result = await plugin.rotate({
1444
1395
  key,
1445
1396
  currentValue: current.value,
1446
- options: { role: opts.role, adminConnString: opts.adminConnString, newPassword: opts.newPassword }
1397
+ options: { role: opts.role, adminConnString: opts.adminConnString, newPassword: opts.newPassword },
1398
+ api,
1399
+ envBase: envBase(cfg, env2)
1447
1400
  });
1448
1401
  console.log(result.summary);
1449
1402
  resolvedValue = result.newValue;
@@ -2121,7 +2074,7 @@ secrets.command("delete").argument("<key>").option("-e, --env <environment>").ac
2121
2074
  secrets.command("history").argument("<key>").option("-e, --env <environment>").option("--reveal").action(wrap(secretsHistoryCommand));
2122
2075
  secrets.command("rollback").argument("<key>").requiredOption("--version <version>").option("-e, --env <environment>").action(wrap(secretsRollbackCommand));
2123
2076
  secrets.command("rename").argument("<oldKey>").argument("<newKey>").option("-e, --env <environment>").action(wrap(secretsRenameCommand));
2124
- secrets.command("rotate").description("Set a new value, keeping the old one fetchable via 'secrets get KEY --previous'").argument("<key>").argument("[value]", "new value, or '-' to read from stdin (omit entirely when using --provider)").option("-e, --env <environment>").option("--from-file <path>", "read the new value from a file").option("--grace <duration>", 'how long the previous value stays fetchable, e.g. "1h", "30m" (omit for no limit)').option("--provider <name>", "have a rotation plugin compute the new value (postgres | stripe | aws-iam)").option("--role <role>", "[postgres] role to rotate (defaults to the connection string's user)").option("--admin-conn-string <url>", "[postgres] connect with different credentials than the value being rotated").option("--new-password <password>", "[postgres] use this password instead of a random one").action(wrap(secretsRotateCommand));
2077
+ secrets.command("rotate").description("Set a new value, keeping the old one fetchable via 'secrets get KEY --previous'").argument("<key>").argument("[value]", "new value, or '-' to read from stdin (omit entirely when using --provider)").option("-e, --env <environment>").option("--from-file <path>", "read the new value from a file").option("--grace <duration>", 'how long the previous value stays fetchable, e.g. "1h", "30m" (omit for no limit)').option("--provider <name>", "have a rotation plugin compute the new value (postgres)").option("--role <role>", "[postgres] role to rotate (defaults to the connection string's user)").option("--admin-conn-string <url>", "[postgres] connect with different credentials than the value being rotated").option("--new-password <password>", "[postgres] use this password instead of a random one").action(wrap(secretsRotateCommand));
2125
2078
  program.command("rotation-providers").description("List available `secrets rotate --provider` plugins").action(wrap(rotationProvidersCommand));
2126
2079
  var override = secrets.command("override").description("Manage your personal, server-side value overrides");
2127
2080
  override.command("set").argument("<key>").argument("[value]", "value, or '-' to read from stdin").option("-e, --env <environment>").option("--from-file <path>", "read the value from a file").action(wrap(secretsOverrideSetCommand));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaultic-dev/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "vaultic": "./dist/index.js"
@@ -11,6 +11,15 @@
11
11
  "engines": {
12
12
  "node": ">=20"
13
13
  },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/vaultic-dev/vaultic.git",
17
+ "directory": "packages/cli"
18
+ },
19
+ "homepage": "https://github.com/vaultic-dev/vaultic#readme",
20
+ "bugs": {
21
+ "url": "https://github.com/vaultic-dev/vaultic/issues"
22
+ },
14
23
  "publishConfig": {
15
24
  "access": "public"
16
25
  },
@@ -26,7 +35,6 @@
26
35
  "commander": "^12.1.0",
27
36
  "js-yaml": "^4.1.0",
28
37
  "libsodium-wrappers": "^0.8.4",
29
- "pg": "^8.13.1",
30
38
  "prompts": "^2.4.2",
31
39
  "zod": "^3.23.8"
32
40
  },
@@ -34,7 +42,6 @@
34
42
  "@types/js-yaml": "^4.0.9",
35
43
  "@types/libsodium-wrappers": "^0.8.2",
36
44
  "@types/node": "^22.7.5",
37
- "@types/pg": "^8.11.10",
38
45
  "@types/prompts": "^2.4.9",
39
46
  "tsup": "^8.3.0",
40
47
  "tsx": "^4.19.1",