@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.
- package/dist/index.js +30 -77
- 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
|
|
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
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
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
|
|
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
|
|
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.
|
|
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",
|