super-secret 0.0.0-stage → 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.
- package/LICENSE +21 -0
- package/README.md +219 -2
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +249 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +65 -0
- package/dist/manifest.js +163 -0
- package/dist/manifest.js.map +1 -0
- package/dist/operator.d.ts +16 -0
- package/dist/operator.js +63 -0
- package/dist/operator.js.map +1 -0
- package/dist/pipeline.d.ts +51 -0
- package/dist/pipeline.js +421 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/render.d.ts +25 -0
- package/dist/render.js +84 -0
- package/dist/render.js.map +1 -0
- package/dist/resolve.d.ts +7 -0
- package/dist/resolve.js +17 -0
- package/dist/resolve.js.map +1 -0
- package/dist/rotators/cloudflareToken.d.ts +13 -0
- package/dist/rotators/cloudflareToken.js +54 -0
- package/dist/rotators/cloudflareToken.js.map +1 -0
- package/dist/rotators/generated.d.ts +14 -0
- package/dist/rotators/generated.js +31 -0
- package/dist/rotators/generated.js.map +1 -0
- package/dist/rotators/index.d.ts +11 -0
- package/dist/rotators/index.js +28 -0
- package/dist/rotators/index.js.map +1 -0
- package/dist/rotators/manual.d.ts +20 -0
- package/dist/rotators/manual.js +64 -0
- package/dist/rotators/manual.js.map +1 -0
- package/dist/rotators/probes.d.ts +61 -0
- package/dist/rotators/probes.js +99 -0
- package/dist/rotators/probes.js.map +1 -0
- package/dist/rotators/static.d.ts +6 -0
- package/dist/rotators/static.js +23 -0
- package/dist/rotators/static.js.map +1 -0
- package/dist/rotators/stripe.d.ts +33 -0
- package/dist/rotators/stripe.js +143 -0
- package/dist/rotators/stripe.js.map +1 -0
- package/dist/state.d.ts +21 -0
- package/dist/state.js +28 -0
- package/dist/state.js.map +1 -0
- package/dist/store/envFile.d.ts +21 -0
- package/dist/store/envFile.js +86 -0
- package/dist/store/envFile.js.map +1 -0
- package/dist/store/index.d.ts +3 -0
- package/dist/store/index.js +4 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/memory.d.ts +3 -0
- package/dist/store/memory.js +16 -0
- package/dist/store/memory.js.map +1 -0
- package/dist/store/onePassword.d.ts +111 -0
- package/dist/store/onePassword.js +227 -0
- package/dist/store/onePassword.js.map +1 -0
- package/dist/targets/all.d.ts +4 -0
- package/dist/targets/all.js +5 -0
- package/dist/targets/all.js.map +1 -0
- package/dist/targets/cloudflare.d.ts +27 -0
- package/dist/targets/cloudflare.js +55 -0
- package/dist/targets/cloudflare.js.map +1 -0
- package/dist/targets/envFile.d.ts +20 -0
- package/dist/targets/envFile.js +28 -0
- package/dist/targets/envFile.js.map +1 -0
- package/dist/targets/github.d.ts +25 -0
- package/dist/targets/github.js +44 -0
- package/dist/targets/github.js.map +1 -0
- package/dist/targets/index.d.ts +9 -0
- package/dist/targets/index.js +22 -0
- package/dist/targets/index.js.map +1 -0
- package/dist/targets/railway.d.ts +53 -0
- package/dist/targets/railway.js +170 -0
- package/dist/targets/railway.js.map +1 -0
- package/dist/types.d.ts +115 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +41 -0
- package/dist/util.js +149 -0
- package/dist/util.js.map +1 -0
- package/package.json +56 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Matthew Preucil
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,220 @@
|
|
|
1
|
-
#
|
|
1
|
+
# super-secret
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Import, push and rotate a project's secrets with 1Password as the store.
|
|
4
|
+
One manifest per repository says which env vars exist, where each one is
|
|
5
|
+
consumed (a Railway service, a Cloudflare Worker, GitHub Actions, a local
|
|
6
|
+
`.env`) and how it is rotated. Dry run is the default; nothing is written
|
|
7
|
+
without `--apply`; values never appear in output.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx super-secret status # what is where, and whether it matches the store
|
|
11
|
+
npx super-secret import --from env:.env --apply # one-time: the current values into the vault
|
|
12
|
+
npx super-secret push --apply # the store's values to every target, redeploy what changed
|
|
13
|
+
npx super-secret plan # what a rotation would do
|
|
14
|
+
npx super-secret rotate worker-token --apply # provision → store → push → deploy → probe → revoke
|
|
15
|
+
npx super-secret verify --previous --expect-dead # the backup slot must be dead afterwards
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Node 22.6 or newer. No runtime dependencies; the CLIs it drives are
|
|
19
|
+
[`op`](https://developer.1password.com/docs/cli/), `gh` and `wrangler`, whichever the manifest uses.
|
|
20
|
+
|
|
21
|
+
## The manifest
|
|
22
|
+
|
|
23
|
+
`super-secret.config.ts` (or `.mjs`, or `super-secret.json` for projects that are
|
|
24
|
+
not TypeScript) in the repository root, or anywhere with `--manifest`:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { defineManifest } from 'super-secret';
|
|
28
|
+
|
|
29
|
+
export default defineManifest({
|
|
30
|
+
store: { kind: 'op', vault: 'my-app', item: 'production', account: 'my.1password.com' },
|
|
31
|
+
targets: {
|
|
32
|
+
cloudflare: { kind: 'cloudflare', cwd: 'worker' },
|
|
33
|
+
github: { kind: 'github', repo: 'me/my-app' },
|
|
34
|
+
railway: {
|
|
35
|
+
kind: 'railway',
|
|
36
|
+
project: '${RAILWAY_PROJECT_ID}', // read from the store
|
|
37
|
+
environment: '${RAILWAY_ENVIRONMENT_ID}',
|
|
38
|
+
services: ['api', 'worker'],
|
|
39
|
+
token: '${RAILWAY_ACCOUNT_TOKEN}',
|
|
40
|
+
},
|
|
41
|
+
env: { kind: 'env-file', path: '.env' }, // the developer's local copy, kept in step
|
|
42
|
+
},
|
|
43
|
+
secrets: [
|
|
44
|
+
{
|
|
45
|
+
id: 'worker-token',
|
|
46
|
+
description: 'Shared bearer token the API checks on every worker request',
|
|
47
|
+
keys: ['WORKER_TOKEN'],
|
|
48
|
+
targets: { cloudflare: true, railway: true, env: true },
|
|
49
|
+
rotator: { kind: 'generated', length: 48 },
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'railway-account-token',
|
|
53
|
+
keys: ['RAILWAY_ACCOUNT_TOKEN'],
|
|
54
|
+
targets: { github: true, env: true },
|
|
55
|
+
rotator: {
|
|
56
|
+
kind: 'manual',
|
|
57
|
+
create: 'Railway → Account settings → Tokens → Create token',
|
|
58
|
+
retire: 'Railway → Account settings → Tokens → delete the previous token',
|
|
59
|
+
probe: { kind: 'railway-account' },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'stripe',
|
|
64
|
+
keys: ['STRIPE_SECRET_KEY', 'STRIPE_WEBHOOK_SECRET'],
|
|
65
|
+
targets: { cloudflare: true },
|
|
66
|
+
rotator: { kind: 'stripe-webhook-secret', url: 'https://api.example.com/stripe/webhook' },
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
A secret is a set of env-var names (`keys`) with one rotator and a map of the
|
|
73
|
+
targets that receive them. `true` keeps the names; `{ STORE_KEY: 'TARGET_KEY' }`
|
|
74
|
+
renames on the way (the same token called `RAILWAY_USAGE_TOKEN` by the app and
|
|
75
|
+
`RAILWAY_ACCOUNT_TOKEN` by CI).
|
|
76
|
+
|
|
77
|
+
Relative paths (`cwd`, `path`, `stateDir`) resolve against the manifest's directory, or against `root` when set (`root: '..'` for a manifest kept in `infra/`).
|
|
78
|
+
|
|
79
|
+
Config strings may read `${KEY}` from the store or the operator env, so IDs
|
|
80
|
+
and tokens live in the vault, not in the manifest.
|
|
81
|
+
|
|
82
|
+
### Rotator kinds
|
|
83
|
+
|
|
84
|
+
| kind | provision | revoke |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `generated` | a random value (`length`, `alphabet`: alnum, hex, base64url) | nothing: the old value dies when every consumer has the new one |
|
|
87
|
+
| `static` | never: identifiers, usernames, public DSNs | |
|
|
88
|
+
| `manual` | the operator mints it where `create` says and pastes it (hidden, or on stdin); checked against `pattern` and `probe` | `retire` is queued as a manual step |
|
|
89
|
+
| `stripe-secret-key` | manual roll in the Dashboard, paste; checked against the same account and mode | manual expire |
|
|
90
|
+
| `stripe-webhook-secret` | a twin endpoint at `url`; its secret ships | the old endpoint is deleted |
|
|
91
|
+
| `cloudflare-api-token` | the token rolls itself in place (needs "API Tokens: Edit") | none |
|
|
92
|
+
|
|
93
|
+
Probes (`probe` on `generated` and `manual`): `{ kind: 'http', url, headers, bearer, basic, expect }`,
|
|
94
|
+
or a preset: `railway-account`, `railway-project`, `github-token`, `stripe-key`,
|
|
95
|
+
`anthropic`, `openai`, `openrouter`, `sentry`, `resend`, `synoptic`, `cloudflare-token`.
|
|
96
|
+
|
|
97
|
+
A TypeScript manifest may pass a `Rotator` or `Target` object instead of a spec for anything
|
|
98
|
+
provider-specific (see `src/types.ts`).
|
|
99
|
+
|
|
100
|
+
### Targets
|
|
101
|
+
|
|
102
|
+
| kind | writes | reads back | deploy |
|
|
103
|
+
|---|---|---|---|
|
|
104
|
+
| `railway` | GraphQL `variableCollectionUpsert` per service, `skipDeploys`; optional registry credentials (`registryCredentials: { username, password }` store keys) | values per service | `serviceInstanceRedeploy`, waits for the deployment to settle (`deploy: false` to skip) |
|
|
105
|
+
| `cloudflare` | `wrangler secret bulk` in `cwd` | names only | none needed |
|
|
106
|
+
| `github` | `gh secret set` (stdin), optional `environment` | names only | |
|
|
107
|
+
| `env-file` | a local env file (0600, timestamped backup) | values | |
|
|
108
|
+
|
|
109
|
+
Readable targets get only the keys that differ from the store. Write-only
|
|
110
|
+
targets get every bound key on `push`; `status` shows present/missing for them.
|
|
111
|
+
|
|
112
|
+
## The store
|
|
113
|
+
|
|
114
|
+
One Secure Note per deployment target in one vault, with three sections:
|
|
115
|
+
|
|
116
|
+
| section | field | holds |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `current` | `<KEY>` | the value in use (reference: `op://vault/item/current/KEY`) |
|
|
119
|
+
| `previous` | `<KEY>` | the value it replaced, the backup slot |
|
|
120
|
+
| `meta` | `<KEY>_rotated_at` | when `current` took over |
|
|
121
|
+
|
|
122
|
+
Every write moves the live value to `previous` first. `layout: 'flat'` uses
|
|
123
|
+
`KEY`, `KEY_previous`, `KEY_rotated_at` without sections instead (an existing
|
|
124
|
+
vault laid out that way keeps working). `store: { kind: 'env', file }` is the
|
|
125
|
+
bootstrap store for a project that has not moved to 1Password yet.
|
|
126
|
+
|
|
127
|
+
A second item, `operator` by default, holds what the tooling itself
|
|
128
|
+
authenticates with (a Railway account token, `GH_TOKEN`, `CLOUDFLARE_API_TOKEN`,
|
|
129
|
+
provider admin keys). The shell env wins; otherwise they are read from that
|
|
130
|
+
item, so CI needs exactly one secret, `OP_SERVICE_ACCOUNT_TOKEN`.
|
|
131
|
+
`import --operator NAME… --apply` fills it from the shell env.
|
|
132
|
+
|
|
133
|
+
`op` needs a session: the desktop app's CLI integration locally (set
|
|
134
|
+
`store.account` when several accounts are signed in), `OP_SERVICE_ACCOUNT_TOKEN`
|
|
135
|
+
in CI. New items are created through a 0600 JSON template so values stay off
|
|
136
|
+
argv; `op item edit` only takes assignments on argv, which is acceptable on a
|
|
137
|
+
single-user machine or an ephemeral runner.
|
|
138
|
+
|
|
139
|
+
## What a rotation does
|
|
140
|
+
|
|
141
|
+
For each selected secret, in manifest order: **provision** the new value;
|
|
142
|
+
**queue** the old credential in `.super-secret/pending-revocations.json`
|
|
143
|
+
(git-ignore it) so a crash never leaves it live and forgotten; **store** it,
|
|
144
|
+
moving the old value to `previous`; **push** it to every bound target without
|
|
145
|
+
deploying. Then once: **deploy** each touched target; **probe** every new value;
|
|
146
|
+
**revoke** the queued credentials. A failed probe stops before revocation;
|
|
147
|
+
`revoke-pending --apply` drains the queue later, `--ack-manual` clears manual
|
|
148
|
+
steps you have done by hand.
|
|
149
|
+
|
|
150
|
+
`--no-deploy`, `--no-revoke`, `--targets a,b` and `--skip-manual` narrow a run.
|
|
151
|
+
Manual rotators need a terminal; without one (CI) they are skipped.
|
|
152
|
+
|
|
153
|
+
## Hosts and pods: `render`
|
|
154
|
+
|
|
155
|
+
Projects that run on plain hosts or GPU pods keep a committed template of
|
|
156
|
+
references and render it where the values are needed:
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
# secrets/prod.env.tpl
|
|
160
|
+
API_SECRET=op://my-app/production/current/API_SECRET
|
|
161
|
+
DATABASE_URL=op://my-app/production/current/DATABASE_URL
|
|
162
|
+
TLS_KEY_B64=op://my-app/production/current/TLS_KEY # multi-line → base64
|
|
163
|
+
PUBLIC_URL=https://example.com # literals pass through
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
npx super-secret render --template secrets/prod.env.tpl --check
|
|
168
|
+
npx super-secret render --template secrets/prod.env.tpl --out /run/app/prod.env # 0600, atomic
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## From GitHub Actions
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
- uses: 1password/install-cli-action@v4
|
|
175
|
+
- run: npx super-secret push --apply --skip-manual
|
|
176
|
+
env:
|
|
177
|
+
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The service account needs read/write on the vault; everything else comes from
|
|
181
|
+
the `operator` item.
|
|
182
|
+
|
|
183
|
+
## Rules the code keeps
|
|
184
|
+
|
|
185
|
+
- Secret values never appear in output; `redact()` prints a prefix, a length and a short hash.
|
|
186
|
+
- Values reach CLIs over stdin or a 0600 file, never argv (the `op item edit` assignments are the one exception).
|
|
187
|
+
- Read-only calls are fine in a dry run; anything that writes checks `ctx.apply` first.
|
|
188
|
+
- The manifest is the inventory: a key that is not in it is not managed.
|
|
189
|
+
|
|
190
|
+
## Releasing
|
|
191
|
+
|
|
192
|
+
Releases are cut by [semantic-release](https://semantic-release.gitbook.io/) from
|
|
193
|
+
`main` in CI (`.github/workflows/ci.yml`), so commits follow
|
|
194
|
+
[Conventional Commits](https://www.conventionalcommits.org/): `feat:` bumps the
|
|
195
|
+
minor version, `fix:` the patch, a `BREAKING CHANGE:` footer or `feat!:` the
|
|
196
|
+
major; `docs:`, `ci:`, `chore:`, `refactor:` and `test:` publish nothing. The
|
|
197
|
+
version in `package.json` is a placeholder; the tag, the npm version and the
|
|
198
|
+
GitHub release notes come from the commits.
|
|
199
|
+
|
|
200
|
+
npm authenticates the workflow by [trusted publishing](https://docs.npmjs.com/trusted-publishers)
|
|
201
|
+
(OIDC), so no npm token is stored anywhere. Bootstrap, once:
|
|
202
|
+
|
|
203
|
+
1. From a laptop signed in to npm (`npm login`) and GitHub (`gh auth login`),
|
|
204
|
+
cut the first release with the same machinery:
|
|
205
|
+
`GITHUB_TOKEN=$(gh auth token) npx semantic-release --no-ci`
|
|
206
|
+
2. On npmjs.com → the package → Settings → Trusted publisher: GitHub Actions,
|
|
207
|
+
repository `randymarsh77/super-secret`, workflow `ci.yml`.
|
|
208
|
+
3. Set the repository variable `NPM_TRUSTED_PUBLISHING` to `true`
|
|
209
|
+
(`gh variable set NPM_TRUSTED_PUBLISHING -b true`). From then on every
|
|
210
|
+
release-worthy push to `main` publishes from CI with provenance.
|
|
211
|
+
|
|
212
|
+
## Lineage
|
|
213
|
+
|
|
214
|
+
Extracted from the secrets tooling in two private projects: a TypeScript
|
|
215
|
+
pipeline (store interface, rotators, Railway and GitHub targets, revocation
|
|
216
|
+
queue) and a Python one (op-backed vault items, `op://` templates rendered on
|
|
217
|
+
hosts, dual-password datastore rotation). Datastore rotators (MySQL `RETAIN
|
|
218
|
+
CURRENT PASSWORD`, Redis ACL) are the next thing to port.
|
|
219
|
+
|
|
220
|
+
MIT.
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* super-secret — import, push and rotate a project's secrets with 1Password as
|
|
4
|
+
* the store. Dry run is the default; nothing is written without --apply.
|
|
5
|
+
*
|
|
6
|
+
* super-secret status [--probe] what is where (store vs each target), pending revocations
|
|
7
|
+
* super-secret init write a starter super-secret.json
|
|
8
|
+
* super-secret import [ids|KEYS…] [--from env:.env] [--apply]
|
|
9
|
+
* copy values from an env file into the store
|
|
10
|
+
* super-secret import --paste KEY… [--apply] paste values (hidden) into the store
|
|
11
|
+
* super-secret import --operator NAME… [--apply] copy operator credentials from the shell env into the operator item
|
|
12
|
+
* super-secret pull <target> [ids…] [--apply] copy a readable target's values into the store (one-time sync)
|
|
13
|
+
* super-secret push [ids…] [--targets a,b] [--no-deploy] [--force] [--apply]
|
|
14
|
+
* deploy the store's values to the targets, redeploy what changed
|
|
15
|
+
* super-secret plan [ids…] dry run of rotate
|
|
16
|
+
* super-secret rotate [ids…] --apply [--targets a,b] [--no-deploy] [--no-revoke] [--skip-manual]
|
|
17
|
+
* super-secret verify [ids…] [--previous] [--expect-dead]
|
|
18
|
+
* super-secret revoke-pending [--apply] [--ack-manual]
|
|
19
|
+
* super-secret deploy <target>… --apply
|
|
20
|
+
* super-secret render --template FILE (--check | --out FILE [--format env|json])
|
|
21
|
+
*
|
|
22
|
+
* --manifest PATH picks the manifest (default: super-secret.config.{ts,mts,mjs,js} or super-secret.json in the cwd).
|
|
23
|
+
*/
|
|
24
|
+
import { spawnSync } from 'node:child_process';
|
|
25
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
26
|
+
import { resolve } from 'node:path';
|
|
27
|
+
import { parseArgs } from 'node:util';
|
|
28
|
+
import { createStore, findManifest, loadManifest, resolveManifest, selectSecrets } from "./manifest.js";
|
|
29
|
+
import { makeOperatorEnv, operatorStore } from "./operator.js";
|
|
30
|
+
import { deployTargets, importStore, pullFromTarget, push, revokePending, rotate, status, verify } from "./pipeline.js";
|
|
31
|
+
import { formatEnv, formatJson, opReader, parseTemplate, render, writePrivate } from "./render.js";
|
|
32
|
+
import { EnvFileStore, memoryStore } from "./store/index.js";
|
|
33
|
+
import { makeLogger, readHidden } from "./util.js";
|
|
34
|
+
const { values: flags, positionals } = parseArgs({
|
|
35
|
+
args: process.argv.slice(2),
|
|
36
|
+
allowPositionals: true,
|
|
37
|
+
strict: true,
|
|
38
|
+
options: {
|
|
39
|
+
manifest: { type: 'string' },
|
|
40
|
+
apply: { type: 'boolean', default: false },
|
|
41
|
+
from: { type: 'string' },
|
|
42
|
+
paste: { type: 'boolean', default: false },
|
|
43
|
+
operator: { type: 'boolean', default: false },
|
|
44
|
+
targets: { type: 'string' },
|
|
45
|
+
'no-deploy': { type: 'boolean', default: false },
|
|
46
|
+
'no-revoke': { type: 'boolean', default: false },
|
|
47
|
+
'skip-manual': { type: 'boolean', default: false },
|
|
48
|
+
'ack-manual': { type: 'boolean', default: false },
|
|
49
|
+
'expect-dead': { type: 'boolean', default: false },
|
|
50
|
+
previous: { type: 'boolean', default: false },
|
|
51
|
+
probe: { type: 'boolean', default: false },
|
|
52
|
+
force: { type: 'boolean', default: false },
|
|
53
|
+
template: { type: 'string' },
|
|
54
|
+
out: { type: 'string' },
|
|
55
|
+
format: { type: 'string', default: 'env' },
|
|
56
|
+
check: { type: 'boolean', default: false },
|
|
57
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
const [command = 'status', ...rest] = positionals;
|
|
61
|
+
function usage() {
|
|
62
|
+
const src = readFileSync(new URL(import.meta.url), 'utf8');
|
|
63
|
+
const header = src.split('*/')[0].split('\n').slice(2).map((l) => l.replace(/^ \* ?/, ''));
|
|
64
|
+
console.log(header.join('\n').trim());
|
|
65
|
+
}
|
|
66
|
+
if (flags.help || command === 'help') {
|
|
67
|
+
usage();
|
|
68
|
+
process.exit(0);
|
|
69
|
+
}
|
|
70
|
+
if (command === 'init') {
|
|
71
|
+
const file = resolve(process.cwd(), 'super-secret.json');
|
|
72
|
+
if (existsSync(file)) {
|
|
73
|
+
console.error(`${file} already exists`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
writeFileSync(file, JSON.stringify({
|
|
77
|
+
$schema: 'https://github.com/randymarsh77/super-secret#manifest',
|
|
78
|
+
store: { kind: 'op', vault: 'my-vault', item: 'production', account: 'my.1password.com' },
|
|
79
|
+
targets: {
|
|
80
|
+
github: { kind: 'github', repo: 'owner/repo' },
|
|
81
|
+
},
|
|
82
|
+
secrets: [
|
|
83
|
+
{
|
|
84
|
+
id: 'example-token',
|
|
85
|
+
description: 'Replace me: a shared bearer token the app verifies itself',
|
|
86
|
+
keys: ['EXAMPLE_TOKEN'],
|
|
87
|
+
targets: { github: true },
|
|
88
|
+
rotator: { kind: 'generated', length: 48 },
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
}, null, 2) + '\n');
|
|
92
|
+
console.log(`wrote ${file}; add .super-secret/ to .gitignore`);
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
95
|
+
// A TypeScript manifest on a Node that does not strip types by default (22.x):
|
|
96
|
+
// re-run ourselves with the flag so `import()` of the .ts file works.
|
|
97
|
+
{
|
|
98
|
+
const path = flags.manifest ? resolve(process.cwd(), flags.manifest) : findManifest();
|
|
99
|
+
const isTs = !!path && /\.(mts|ts)$/.test(path);
|
|
100
|
+
const features = process.features;
|
|
101
|
+
if (isTs && !features.typescript && !process.execArgv.includes('--experimental-strip-types')) {
|
|
102
|
+
const r = spawnSync(process.execPath, ['--experimental-strip-types', '--no-warnings', ...process.argv.slice(1)], { stdio: 'inherit' });
|
|
103
|
+
process.exit(r.status ?? 1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const apply = flags.apply && command !== 'plan';
|
|
107
|
+
const log = makeLogger(!apply);
|
|
108
|
+
const onlyTargets = flags.targets?.split(',').map((s) => s.trim()).filter(Boolean);
|
|
109
|
+
function splitIdsAndKeys(tokens, defs) {
|
|
110
|
+
const ids = [];
|
|
111
|
+
const keys = [];
|
|
112
|
+
for (const t of tokens)
|
|
113
|
+
(/^[A-Z][A-Z0-9_]*$/.test(t) ? keys : ids).push(t);
|
|
114
|
+
const selected = selectSecrets(defs, ids);
|
|
115
|
+
const known = new Set(defs.flatMap((d) => d.keys));
|
|
116
|
+
const unknownKeys = keys.filter((k) => !known.has(k));
|
|
117
|
+
if (unknownKeys.length)
|
|
118
|
+
throw new Error(`key(s) not in the manifest: ${unknownKeys.join(', ')}`);
|
|
119
|
+
return { ids, keys: keys.length ? keys : selected.flatMap((d) => d.keys) };
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
if (command === 'render') {
|
|
123
|
+
if (!flags.template)
|
|
124
|
+
throw new Error('render needs --template FILE');
|
|
125
|
+
if (!flags.check && !flags.out)
|
|
126
|
+
throw new Error('render needs --check or --out FILE');
|
|
127
|
+
const manifest = await loadManifest(flags.manifest).catch(() => undefined);
|
|
128
|
+
const account = manifest?.config.store.kind === 'op' ? manifest.config.store.account : undefined;
|
|
129
|
+
const entries = parseTemplate(readFileSync(resolve(process.cwd(), flags.template), 'utf8'), flags.template);
|
|
130
|
+
const values = await render(entries, opReader(account));
|
|
131
|
+
for (const e of entries)
|
|
132
|
+
console.log(` ${e.key.padEnd(28)} ok ${e.source.startsWith('op://') ? 'reference' : 'literal'}${e.key.endsWith('_B64') ? ' (base64)' : ''}`);
|
|
133
|
+
if (flags.check)
|
|
134
|
+
console.log(`== ${entries.length} keys resolve from ${flags.template}`);
|
|
135
|
+
else {
|
|
136
|
+
writePrivate(flags.out, flags.format === 'json' ? formatJson(values) : formatEnv(values));
|
|
137
|
+
console.log(`== wrote ${entries.length} keys to ${flags.out} (mode 0600); delete it when it has been consumed`);
|
|
138
|
+
}
|
|
139
|
+
process.exit(0);
|
|
140
|
+
}
|
|
141
|
+
const manifest = await loadManifest(flags.manifest);
|
|
142
|
+
const { targets, secrets } = resolveManifest(manifest.config);
|
|
143
|
+
const store = createStore(manifest.config.store, apply, log, manifest.root);
|
|
144
|
+
const operator = makeOperatorEnv(manifest.config.store, manifest.config.operator, log);
|
|
145
|
+
const ctx = {
|
|
146
|
+
store,
|
|
147
|
+
apply,
|
|
148
|
+
log,
|
|
149
|
+
operator,
|
|
150
|
+
targets,
|
|
151
|
+
root: manifest.root,
|
|
152
|
+
stateDir: resolve(manifest.root, manifest.config.stateDir ?? '.super-secret'),
|
|
153
|
+
};
|
|
154
|
+
const dryRunHint = (verb) => {
|
|
155
|
+
if (!apply)
|
|
156
|
+
log.info(`\nDry run. Re-run with --apply to ${verb}.`);
|
|
157
|
+
};
|
|
158
|
+
switch (command) {
|
|
159
|
+
case 'status':
|
|
160
|
+
await status(selectSecrets(secrets, rest), ctx, { probe: flags.probe });
|
|
161
|
+
break;
|
|
162
|
+
case 'plan':
|
|
163
|
+
case 'rotate':
|
|
164
|
+
await rotate(selectSecrets(secrets, rest), ctx, {
|
|
165
|
+
deploy: !flags['no-deploy'],
|
|
166
|
+
revoke: !flags['no-revoke'],
|
|
167
|
+
targets: onlyTargets,
|
|
168
|
+
skipManual: flags['skip-manual'],
|
|
169
|
+
});
|
|
170
|
+
if (!apply)
|
|
171
|
+
log.info('\nDry run. Re-run `rotate … --apply` to execute.');
|
|
172
|
+
break;
|
|
173
|
+
case 'import': {
|
|
174
|
+
if (flags.operator) {
|
|
175
|
+
const names = rest;
|
|
176
|
+
if (!names.length)
|
|
177
|
+
throw new Error('import --operator needs the variable names to copy');
|
|
178
|
+
const present = {};
|
|
179
|
+
for (const n of names) {
|
|
180
|
+
const v = process.env[n] ?? (flags.paste ? await readHidden(`Paste ${n} (hidden): `) : undefined);
|
|
181
|
+
if (v)
|
|
182
|
+
present[n] = v;
|
|
183
|
+
else
|
|
184
|
+
log.note(`${n}: not in the shell env, skipped (use --paste to enter it)`);
|
|
185
|
+
}
|
|
186
|
+
const to = operatorStore(manifest.config.store, manifest.config.operator, apply, log);
|
|
187
|
+
await importStore(memoryStore(present, 'shell env'), to, names, ctx);
|
|
188
|
+
dryRunHint('write the operator item');
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
const { keys } = splitIdsAndKeys(rest, secrets);
|
|
192
|
+
if (flags.paste) {
|
|
193
|
+
const pasted = {};
|
|
194
|
+
for (const k of keys) {
|
|
195
|
+
const v = await readHidden(`Paste ${k} (hidden, empty to skip): `);
|
|
196
|
+
if (v)
|
|
197
|
+
pasted[k] = v;
|
|
198
|
+
}
|
|
199
|
+
await importStore(memoryStore(pasted, 'pasted values'), store, Object.keys(pasted), ctx);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
const spec = flags.from ?? 'env:.env';
|
|
203
|
+
const [kind, arg] = spec.includes(':') ? [spec.slice(0, spec.indexOf(':')), spec.slice(spec.indexOf(':') + 1)] : [spec, ''];
|
|
204
|
+
if (kind !== 'env')
|
|
205
|
+
throw new Error(`--from must be env:<file> (for a target, use \`pull <target>\`)`);
|
|
206
|
+
const from = new EnvFileStore(resolve(manifest.root, arg || '.env'), false, log);
|
|
207
|
+
await importStore(from, store, keys, ctx);
|
|
208
|
+
}
|
|
209
|
+
dryRunHint('write the store');
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
case 'pull': {
|
|
213
|
+
const [target, ...ids] = rest;
|
|
214
|
+
if (!target)
|
|
215
|
+
throw new Error('pull needs a target name');
|
|
216
|
+
await pullFromTarget(selectSecrets(secrets, ids), target, ctx);
|
|
217
|
+
dryRunHint('write the store');
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
case 'push':
|
|
221
|
+
await push(selectSecrets(secrets, rest), ctx, { deploy: !flags['no-deploy'], targets: onlyTargets, force: flags.force });
|
|
222
|
+
dryRunHint('push');
|
|
223
|
+
break;
|
|
224
|
+
case 'verify': {
|
|
225
|
+
const ok = await verify(selectSecrets(secrets, rest), ctx, flags['expect-dead'], flags.previous ? 'previous' : 'current');
|
|
226
|
+
process.exit(ok ? 0 : 1);
|
|
227
|
+
}
|
|
228
|
+
// eslint-disable-next-line no-fallthrough
|
|
229
|
+
case 'revoke-pending':
|
|
230
|
+
await revokePending(secrets, ctx, { ackManual: flags['ack-manual'] });
|
|
231
|
+
dryRunHint('revoke');
|
|
232
|
+
break;
|
|
233
|
+
case 'deploy':
|
|
234
|
+
if (!rest.length)
|
|
235
|
+
throw new Error('deploy needs target name(s)');
|
|
236
|
+
await deployTargets(ctx, rest);
|
|
237
|
+
dryRunHint('deploy');
|
|
238
|
+
break;
|
|
239
|
+
default:
|
|
240
|
+
console.error(`Unknown command "${command}".\n`);
|
|
241
|
+
usage();
|
|
242
|
+
process.exit(2);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
catch (e) {
|
|
246
|
+
console.error(`\n✗ ${e.message}`);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './types.ts';
|
|
2
|
+
export { redact, sha, randomSecret, run, http, statusOf, probeFromStatus, makeLogger, readHidden, interpolate, HttpError } from './util.ts';
|
|
3
|
+
export { resolveRef } from './resolve.ts';
|
|
4
|
+
export * from './store/index.ts';
|
|
5
|
+
export * from './targets/index.ts';
|
|
6
|
+
export * from './rotators/index.ts';
|
|
7
|
+
export { defineManifest, loadManifest, findManifest, validateManifest, resolveManifest, selectSecrets, createStore, MANIFEST_NAMES, type ManifestConfig, type SecretConfig, type StoreConfig, type OperatorConfig, type LoadedManifest, type Resolved, } from './manifest.ts';
|
|
8
|
+
export { makeOperatorEnv, operatorStore, OPERATOR_ITEM } from './operator.ts';
|
|
9
|
+
export { RevocationQueue, type PendingRevocation } from './state.ts';
|
|
10
|
+
export * from './pipeline.ts';
|
|
11
|
+
export * from './render.ts';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export { redact, sha, randomSecret, run, http, statusOf, probeFromStatus, makeLogger, readHidden, interpolate, HttpError } from "./util.js";
|
|
3
|
+
export { resolveRef } from "./resolve.js";
|
|
4
|
+
export * from "./store/index.js";
|
|
5
|
+
export * from "./targets/index.js";
|
|
6
|
+
export * from "./rotators/index.js";
|
|
7
|
+
export { defineManifest, loadManifest, findManifest, validateManifest, resolveManifest, selectSecrets, createStore, MANIFEST_NAMES, } from "./manifest.js";
|
|
8
|
+
export { makeOperatorEnv, operatorStore, OPERATOR_ITEM } from "./operator.js";
|
|
9
|
+
export { RevocationQueue } from "./state.js";
|
|
10
|
+
export * from "./pipeline.js";
|
|
11
|
+
export * from "./render.js";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC5I,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EACN,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,WAAW,EACX,cAAc,GAOd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,eAAe,EAA0B,MAAM,YAAY,CAAC;AACrE,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type RotatorSpec } from './rotators/index.ts';
|
|
2
|
+
import { type OpLayout } from './store/index.ts';
|
|
3
|
+
import { type TargetConfig } from './targets/index.ts';
|
|
4
|
+
import type { Logger, Rotator, SecretDef, SecretStore, Target, TargetBinding } from './types.ts';
|
|
5
|
+
export type StoreConfig = {
|
|
6
|
+
kind: 'op';
|
|
7
|
+
vault: string;
|
|
8
|
+
/** One Secure Note per deployment target, e.g. `production`. */
|
|
9
|
+
item: string;
|
|
10
|
+
/** 1Password account (sign-in address or shorthand) when several are signed in. */
|
|
11
|
+
account?: string;
|
|
12
|
+
layout?: OpLayout;
|
|
13
|
+
} | {
|
|
14
|
+
kind: 'env';
|
|
15
|
+
/** Relative to the manifest. */
|
|
16
|
+
file: string;
|
|
17
|
+
};
|
|
18
|
+
export interface OperatorConfig {
|
|
19
|
+
/** 1Password item in the store's vault holding operator credentials (default `operator`). */
|
|
20
|
+
item?: string;
|
|
21
|
+
layout?: OpLayout;
|
|
22
|
+
}
|
|
23
|
+
export interface SecretConfig {
|
|
24
|
+
id: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
keys: string[];
|
|
27
|
+
/** target name → true (same names) or {storeKey: targetKey}. */
|
|
28
|
+
targets?: Record<string, TargetBinding>;
|
|
29
|
+
rotator: RotatorSpec | Rotator;
|
|
30
|
+
disruption?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ManifestConfig {
|
|
33
|
+
store: StoreConfig;
|
|
34
|
+
/** false: operator credentials come from the shell env only. */
|
|
35
|
+
operator?: OperatorConfig | false;
|
|
36
|
+
targets?: Record<string, TargetConfig | Target>;
|
|
37
|
+
secrets: SecretConfig[];
|
|
38
|
+
/** Pending revocations live here, relative to the root (default `.super-secret`). Git-ignore it. */
|
|
39
|
+
stateDir?: string;
|
|
40
|
+
/**
|
|
41
|
+
* The directory relative paths resolve against (targets' cwd and files,
|
|
42
|
+
* stateDir), relative to the manifest file. Default: the manifest's own
|
|
43
|
+
* directory; a manifest kept in `infra/` sets `root: '..'`.
|
|
44
|
+
*/
|
|
45
|
+
root?: string;
|
|
46
|
+
}
|
|
47
|
+
/** Identity helper so a TypeScript manifest gets completion and checking. */
|
|
48
|
+
export declare function defineManifest(config: ManifestConfig): ManifestConfig;
|
|
49
|
+
export declare const MANIFEST_NAMES: string[];
|
|
50
|
+
export interface LoadedManifest {
|
|
51
|
+
config: ManifestConfig;
|
|
52
|
+
path: string;
|
|
53
|
+
/** `config.root` resolved: relative paths resolve against it. */
|
|
54
|
+
root: string;
|
|
55
|
+
}
|
|
56
|
+
export declare function findManifest(cwd?: string): string | undefined;
|
|
57
|
+
export declare function loadManifest(path?: string, cwd?: string): Promise<LoadedManifest>;
|
|
58
|
+
export declare function validateManifest(cfg: ManifestConfig): string[];
|
|
59
|
+
export declare function createStore(cfg: StoreConfig, apply: boolean, log: Logger, root: string): SecretStore;
|
|
60
|
+
export interface Resolved {
|
|
61
|
+
targets: Record<string, Target>;
|
|
62
|
+
secrets: SecretDef[];
|
|
63
|
+
}
|
|
64
|
+
export declare function resolveManifest(cfg: ManifestConfig): Resolved;
|
|
65
|
+
export declare function selectSecrets(defs: SecretDef[], ids: string[]): SecretDef[];
|