@seekrit/cli 0.9.0 → 0.11.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 (3) hide show
  1. package/README.md +24 -2
  2. package/dist/index.js +77 -7
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -86,6 +86,28 @@ seekrit token revoke <tokenId>
86
86
 
87
87
  See the [service tokens guide](https://seekrit.dev/docs/guides/service-tokens).
88
88
 
89
+ ## Docker
90
+
91
+ The CLI also ships as a container image — the full toolchain on a Node runtime,
92
+ for CI runners and agent sandboxes. Multi-arch images (`linux/amd64` +
93
+ `linux/arm64`) are on Docker Hub as
94
+ [`seekritdev/cli`](https://hub.docker.com/r/seekritdev/cli):
95
+
96
+ ```sh
97
+ docker run --rm \
98
+ -e SEEKRIT_TOKEN=skt_… \
99
+ -v "$PWD:/work" -w /work \
100
+ seekritdev/cli run -- ./deploy.sh
101
+ ```
102
+
103
+ The entrypoint is `seekrit`, so pass subcommands directly (e.g.
104
+ `docker run seekritdev/cli secrets list`). It runs unprivileged (`node`) and
105
+ writes config under `$HOME/.config`. Tags: `:latest`, `:<version>` /
106
+ `:<major>.<minor>`, and `:edge` (latest `main`).
107
+
108
+ For a Node-free runtime image (inject secrets and exec, nothing else), prefer
109
+ the [`seekrit-run`](../run) launcher — `seekritdev/run`.
110
+
89
111
  ## Configuration
90
112
 
91
113
  Credentials and defaults are read from (highest precedence first) environment
@@ -121,8 +143,8 @@ See the root [README](../../README.md) for the full end-to-end local loop.
121
143
  ### Build
122
144
 
123
145
  The CLI is bundled with [tsdown](https://tsdown.dev). Workspace packages
124
- (`@seekrit/*`) are inlined so the published package's only runtime dependency is
125
- `commander`.
146
+ (`@seekrit/*`) are inlined, so the published package's only runtime dependencies
147
+ are `commander`, `zod`, and `@modelcontextprotocol/sdk`.
126
148
 
127
149
  ```sh
128
150
  pnpm build # bundle to dist/index.js
package/dist/index.js CHANGED
@@ -586,7 +586,7 @@ async function unwrapDek(wrapped, privateKey) {
586
586
  }
587
587
  //#endregion
588
588
  //#region package.json
589
- var version = "0.9.0";
589
+ var version = "0.11.0";
590
590
  const PROJECT_FILE = "seekrit.json";
591
591
  function globalConfigPath() {
592
592
  return join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "seekrit", "config.json");
@@ -814,6 +814,19 @@ var SeekritClient = class {
814
814
  const qs = params.size > 0 ? `?${params}` : "";
815
815
  return this.request("GET", `/v1/orgs/${orgId}/audit${qs}`);
816
816
  }
817
+ getLogSink(orgId) {
818
+ return this.request("GET", `/v1/orgs/${orgId}/log-sink`);
819
+ }
820
+ setLogSink(orgId, input) {
821
+ return this.request("PUT", `/v1/orgs/${orgId}/log-sink`, input);
822
+ }
823
+ deleteLogSink(orgId) {
824
+ return this.request("DELETE", `/v1/orgs/${orgId}/log-sink`);
825
+ }
826
+ /** Send a synthetic record to the configured endpoint to verify connectivity. */
827
+ testLogSink(orgId) {
828
+ return this.request("POST", `/v1/orgs/${orgId}/log-sink/test`);
829
+ }
817
830
  };
818
831
  //#endregion
819
832
  //#region src/io.ts
@@ -940,6 +953,49 @@ function formatSecrets(values, format) {
940
953
  }
941
954
  }
942
955
  //#endregion
956
+ //#region src/provisioner.ts
957
+ /**
958
+ * `seekrit provisioner` — helpers for the self-hosted **remote executor**
959
+ * (`seekrit-provisioner`), the daemon that runs a target's provisioning SQL
960
+ * inside the customer's own network so the control plane never sees the database
961
+ * admin credential.
962
+ *
963
+ * The only stateful command is `keygen`: the shared HMAC key authenticates the
964
+ * signed commands the broker sends the daemon. The same base64 value is given to
965
+ * BOTH `--hmac-key` when registering a remote target AND the daemon's
966
+ * `SEEKRIT_PROVISIONER_HMAC_KEY`.
967
+ */
968
+ function registerProvisionerCommands(program) {
969
+ program.command("provisioner").description("self-hosted remote executor (seekrit-provisioner) helpers").command("keygen").description("generate a shared HMAC key for a remote provisioning target").action(() => {
970
+ const key = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(32));
971
+ console.log(toBase64(key));
972
+ console.error("Give this key to BOTH `--hmac-key` when registering a remote target AND the\ndaemon's SEEKRIT_PROVISIONER_HMAC_KEY. Keep it secret; it authenticates the\nbroker's commands to your provisioner.");
973
+ });
974
+ }
975
+ /**
976
+ * Resolve the "admin secret" a lease target registration wraps to the broker.
977
+ * Its meaning depends on the executor:
978
+ *
979
+ * - **remote** — the shared HMAC key (base64). The real database admin
980
+ * credential is NOT sent to seekrit; it is configured on the daemon instead.
981
+ * Preferred source is `--hmac-key` / `SEEKRIT_PROVISIONER_HMAC_KEY`, with the
982
+ * older `--admin-url` / provider admin-url env kept as a fallback.
983
+ * - **in_do** — the database admin connection string, which the broker decrypts
984
+ * transiently to run the SQL itself.
985
+ *
986
+ * `fail()` never returns, so the result is always a non-empty string.
987
+ */
988
+ function resolveLeaseAdminSecret(opts) {
989
+ if (opts.executor === "remote") {
990
+ const key = opts.hmacKey ?? process.env.SEEKRIT_PROVISIONER_HMAC_KEY ?? opts.adminUrl ?? process.env[opts.adminUrlEnv];
991
+ if (!key) fail("the remote executor needs the shared HMAC key — pass --hmac-key or set SEEKRIT_PROVISIONER_HMAC_KEY (mint one with `seekrit provisioner keygen`)");
992
+ return key.trim();
993
+ }
994
+ const adminUrl = opts.adminUrl ?? process.env[opts.adminUrlEnv];
995
+ if (!adminUrl) fail(`provide the admin connection string via --admin-url or ${opts.adminUrlEnv}`);
996
+ return adminUrl;
997
+ }
998
+ //#endregion
943
999
  //#region src/target.ts
944
1000
  /** Resolve the target org from a flag, the committed config, or a lone org. */
945
1001
  async function resolveOrg(ctx, orgSlug) {
@@ -1061,7 +1117,7 @@ function generateUserName(prefix = "tmp") {
1061
1117
  function registerMysqlCommands(program) {
1062
1118
  const mysql = program.command("mysql").description("temporary MySQL/MariaDB credentials (short-lived, zero-knowledge)");
1063
1119
  const target = mysql.command("target").description("manage provisioning targets");
1064
- 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("--admin-url <url>", "admin mysql:// connection string (or set SEEKRIT_MYSQL_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) => {
1120
+ 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$2, []).option("--revoke-statement <sql>", "custom REVOKE template (repeatable)", collect$2, []).action(async (options) => {
1065
1121
  const ctx = buildContext();
1066
1122
  const org = await resolveOrg(ctx, options.org);
1067
1123
  const executor = options.executor === "remote" ? "remote" : "in_do";
@@ -1072,8 +1128,12 @@ function registerMysqlCommands(program) {
1072
1128
  "custom"
1073
1129
  ].includes(options.access)) fail("--access must be readonly, readwrite, or custom");
1074
1130
  const accessLevel = options.access;
1075
- const adminSecret = options.adminUrl ?? process.env.SEEKRIT_MYSQL_ADMIN_URL;
1076
- if (!adminSecret) fail(executor === "remote" ? "provide the shared HMAC key via --admin-url or SEEKRIT_MYSQL_ADMIN_URL" : "provide the admin connection string via --admin-url or SEEKRIT_MYSQL_ADMIN_URL");
1131
+ const adminSecret = resolveLeaseAdminSecret({
1132
+ executor,
1133
+ hmacKey: options.hmacKey,
1134
+ adminUrl: options.adminUrl,
1135
+ adminUrlEnv: "SEEKRIT_MYSQL_ADMIN_URL"
1136
+ });
1077
1137
  const config = {
1078
1138
  provider: "mysql",
1079
1139
  executor,
@@ -1466,6 +1526,11 @@ z.object({
1466
1526
  expiresAt: z.iso.datetime().nullish()
1467
1527
  });
1468
1528
  z.object({ prefs: z.partialRecord(z.enum(NOTIFICATION_TYPES), z.boolean()) });
1529
+ z.object({
1530
+ endpoint: z.url().max(2048),
1531
+ headers: z.record(z.string().min(1).max(256), z.string().max(4096)).optional(),
1532
+ enabled: z.boolean().default(true)
1533
+ });
1469
1534
  z.object({
1470
1535
  cursor: z.string().optional(),
1471
1536
  limit: z.coerce.number().int().min(1).max(200).default(50),
@@ -1505,7 +1570,7 @@ function generateRoleName(prefix = "tmp") {
1505
1570
  function registerPgCommands(program) {
1506
1571
  const pg = program.command("pg").description("temporary Postgres credentials (short-lived, zero-knowledge)");
1507
1572
  const target = pg.command("target").description("manage provisioning targets");
1508
- 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("--admin-url <url>", "admin postgres:// connection string (or set SEEKRIT_PG_ADMIN_URL); wrapped locally").option("--create-statement <sql>", "custom CREATE template (repeatable)", collect$1, []).option("--revoke-statement <sql>", "custom REVOKE template (repeatable)", collect$1, []).action(async (options) => {
1573
+ 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$1, []).option("--revoke-statement <sql>", "custom REVOKE template (repeatable)", collect$1, []).action(async (options) => {
1509
1574
  const ctx = buildContext();
1510
1575
  const org = await resolveOrg(ctx, options.org);
1511
1576
  const executor = options.executor === "remote" ? "remote" : "in_do";
@@ -1516,8 +1581,12 @@ function registerPgCommands(program) {
1516
1581
  "custom"
1517
1582
  ].includes(options.access)) fail("--access must be readonly, readwrite, or custom");
1518
1583
  const accessLevel = options.access;
1519
- const adminSecret = options.adminUrl ?? process.env.SEEKRIT_PG_ADMIN_URL;
1520
- if (!adminSecret) fail(executor === "remote" ? "provide the shared HMAC key via --admin-url or SEEKRIT_PG_ADMIN_URL" : "provide the admin connection string via --admin-url or SEEKRIT_PG_ADMIN_URL");
1584
+ const adminSecret = resolveLeaseAdminSecret({
1585
+ executor,
1586
+ hmacKey: options.hmacKey,
1587
+ adminUrl: options.adminUrl,
1588
+ adminUrlEnv: "SEEKRIT_PG_ADMIN_URL"
1589
+ });
1521
1590
  const config = {
1522
1591
  provider: "postgres",
1523
1592
  executor,
@@ -2219,6 +2288,7 @@ token.command("revoke <tokenId>").description("revoke a service token").option("
2219
2288
  });
2220
2289
  registerPgCommands(program);
2221
2290
  registerMysqlCommands(program);
2291
+ registerProvisionerCommands(program);
2222
2292
  registerSshCommands(program);
2223
2293
  program.command("mcp").description("run an MCP server over stdio so AI agents can drive seekrit").action(async () => {
2224
2294
  const { runMcpServer } = await import("./mcp-BEcV_KpM.js");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seekrit/cli",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "End-to-end encrypted secrets manager CLI — inject decrypted secrets into any command.",
5
5
  "type": "module",
6
6
  "publishConfig": {