@crvouga/personal 0.0.0-stage → 0.1.1

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/releasing.md ADDED
@@ -0,0 +1,160 @@
1
+ # Automated npm releases
2
+
3
+ The source workspace stays private. Only the allowlisted contents of
4
+ `packages/personal-cli/dist` are packed as **`@crvouga/personal`**.
5
+
6
+ ## One-time bootstrap
7
+
8
+ Node, npm and Bun must be on PATH through the shared mise setup. npm 11.15+ is
9
+ needed for the bootstrap's `npm trust` command. Start with a read-only plan:
10
+
11
+ ```sh
12
+ bash scripts/dev/bootstrap.sh
13
+ exec "$SHELL" -l
14
+ ```
15
+
16
+ The machine bootstrap needs no Bun or npm, installs the shared locked mise
17
+ toolchain and frozen workspace dependencies, and configures persistent shell
18
+ PATH. On native Windows use `& ./scripts/dev/bootstrap.ps1`, then reopen your
19
+ terminal. See [machine setup](../../scripts/dev/README.md) for new/remote nodes.
20
+ After refreshing your terminal, run:
21
+
22
+ ```sh
23
+ bun run personal:release:plan
24
+ bun run personal:publish:bootstrap --apply --yes --npm-login
25
+ ```
26
+
27
+ Release progress on stderr identifies each stage (`npm-version`, `build`,
28
+ `pack`, `consumer-install`, consumer commands and publisher verification).
29
+ Failures name the stage without exposing raw subprocess output or credentials.
30
+ The npm runner supports mise shims, shell launchers and native executables;
31
+ Windows `.cmd` launchers use npm's adjacent JavaScript entry point through Node.
32
+ To validate the complete build/pack/anonymous-consumer path without logging in,
33
+ publishing or changing trusted publishers, run:
34
+
35
+ ```sh
36
+ bun run personal:build
37
+ bun run --cwd packages/personal-cli smoke
38
+ ```
39
+
40
+ The bootstrap builds and installs/tests the exact tarball in an anonymous isolated
41
+ consumer, prints npm's account authorization URL, publishes the package if necessary,
42
+ and configures/verifies its GitHub trusted publisher. Approve npm login and any
43
+ npm account/2FA approval in your own terminal/browser. No npm password, recovery
44
+ code or long-lived publishing token needs to be supplied to this project.
45
+ The trusted publisher is **`crvouga/personal` / `ci.yml`**, with direct publishing
46
+ permission. The publishing job runs directly in `ci.yml` so the registered
47
+ workflow and executing workflow have the same identity. Do not move publication
48
+ into a reusable workflow: its OIDC identity can differ from the registered publisher.
49
+
50
+ Bootstrap is retryable: if content is already published, it only checks/configures
51
+ the publisher. Existing publishers are preserved; no connection is revoked or
52
+ replaced. npm account authorization uses an isolated temporary 0600 npmrc, which
53
+ is removed with its logs/cache when the command exits. Initial publication also
54
+ inherits your terminal so npm can request its publishing-specific 2FA approval.
55
+ Global npm login/config is
56
+ not changed. A hard process kill can leave a temporary directory; remove any
57
+ abandoned `personal-npm-auth-*` directory in your OS temporary directory.
58
+
59
+ ### Remote Mac / SSH browser approval
60
+
61
+ Run the bootstrap **on the Mac Mini** in an interactive remote terminal. If you
62
+ connect with SSH, use `ssh -t YOUR_ALIAS` to allocate a terminal. No browser is
63
+ needed on the Mini, and no port forwarding or copying npm credentials is needed.
64
+
65
+ 1. Run `bun run personal:publish:bootstrap --apply --yes --npm-login` on the Mini.
66
+ 2. When npm prints its HTTPS login URL, copy/open that **new URL on your MacBook
67
+ Air**, sign in to npm and complete the account/2FA approval there.
68
+ 3. **Leave the Mini's terminal running**. It waits for npm's approval and resumes
69
+ automatically; no Enter-to-open-browser prompt is used. Ctrl+C cancels login.
70
+ 4. Publishing may print another approval URL or ask for a one-time code. Open
71
+ that URL on the Air too, or enter the requested code in the Mini's terminal.
72
+
73
+ Browser launch is disabled for the entire isolated npm session, including
74
+ publish-time web 2FA and trusted-publisher setup. npm update notices/progress are
75
+ disabled too; keep npm versions owned by the shared mise lock, not a global
76
+ `npm install -g`. After cancellation or expiry, rerun the command and use its new
77
+ URL, not an earlier session's URL. Treat approval links as private; never paste
78
+ them into reports or shared logs. Registry verification retries version and latest-tag propagation with 100 seconds of bounded propagation waits without republishing. Transient read failures are retried within that same bound. The browser approval is not proof of publication;
79
+ wait for registry-integrity and trusted-publisher verification to finish.
80
+
81
+ ## Vault username/password login
82
+
83
+ If a suitable npm publishing token already exists in Vault, use:
84
+
85
+ ```sh
86
+ bun run personal:publish:bootstrap --apply --yes --login
87
+ ```
88
+
89
+ This prompts for **Vault username and password**, not a Vault token. The default
90
+ username is `crvouga`; set `PERSONAL_VAULT_USERNAME` to change it. Login creates a
91
+ temporary in-memory userpass session, reads the requested credential and revokes
92
+ the session immediately afterward. Nothing is written to `~/.vault-token`.
93
+ `PERSONAL_VAULT_PASSWORD_FILE` supports a private secret-delivery file for agents;
94
+ the caller owns deleting that file immediately after use. Never pass passwords as
95
+ command arguments. Noninteractive calls never prompt.
96
+
97
+ Defaults: Vault `https://vault.chrisvouga.dev`, document
98
+ `secret/data/personal/prd`, field `NPM_TOKEN`. Select an **existing** field/document
99
+ using `PERSONAL_NPM_TOKEN_KEY` / `PERSONAL_NPM_VAULT_PATH`. The token must permit
100
+ creating/publishing `@crvouga/personal` and satisfy npm's unattended-publish/2FA
101
+ policy. Recovery codes are not publishing credentials. Missing fields fail
102
+ clearly; no code guesses another secret, seeds values or changes Vault policy.
103
+ Vault auth, policy and secret changes remain exclusively OpenTofu-owned.
104
+
105
+ Existing npm `NODE_AUTH_TOKEN` / `NPM_TOKEN` are also supported for automation.
106
+ Local Vault access always uses username/password, never a user-supplied Vault
107
+ token or a saved `~/.vault-token`. `--login` explicitly chooses username/password
108
+ even when an npm credential is already injected. A publishing
109
+ token is passed only in the npm child environment; its temporary npmrc contains
110
+ an environment-variable reference, not the token itself.
111
+
112
+ ## CI: automatic publishing after checks
113
+
114
+ The main **CI** workflow validates the CLI on Linux/macOS/Windows and Node
115
+ 22/24/26, including installing the packed distribution into a clean directory
116
+ with anonymous registry configuration and running it on real Node. The required
117
+ **Check** includes this job. After Check/Fleet pass on main, the release job directly in
118
+ `ci.yml`:
119
+
120
+ 1. Reads the public registry and hashes the release inputs.
121
+ 2. Skips publication when the published content hash already matches.
122
+ 3. Uses `0.1.0` initially, then automatically increments the current stable patch
123
+ version. A higher source manifest version deliberately selects a minor/major.
124
+ 4. Builds, allowlist-checks, packs and consumer-tests the exact tarball.
125
+ 5. Publishes using npm's **GitHub OIDC trusted publishing**, without npm secrets.
126
+ 6. Confirms the content hash, SHA-512 integrity and latest tag, with bounded
127
+ read-only retries for registry propagation. Unconfirmed outcomes fail rather
128
+ than blindly republish.
129
+
130
+ Publishing is serialized with non-cancelling concurrency. Dispatch **CI** on main
131
+ to retry. PRs, merge queues, failed checks and non-main dispatches never publish.
132
+ Public-source CI includes npm provenance. Local bootstrap records its revision
133
+ and dirty-checkout status and does not claim CI provenance. Registry versions
134
+ are the checkpoint; no version-bump commits, tags or GitHub write access is needed.
135
+ Atlas and Mockingbird informed the bootstrap/trusted-publisher and exact-consumer
136
+ validation design; their accounts/publishers are never modified.
137
+
138
+ For an explicitly selected existing Vault-backed npm token instead of OIDC, set
139
+ the **repository variable** `PERSONAL_NPM_TOKEN_KEY` to its field name, never its
140
+ value. CI then uses existing GitHub → Vault OIDC (`jwt` / `github-actions`) to read
141
+ that one field from `secret/data/personal/prd`. No duplicate GitHub secret is
142
+ created. Leave the variable unset for default tokenless npm publishing.
143
+
144
+ ## Recovery and verification
145
+
146
+ If publication is unconfirmed, rerun the **plan** first. Never guess a new version
147
+ or blindly repeat a publish. Bootstrap can resume publisher setup after a package
148
+ was published successfully. npm account access is a prerequisite for first
149
+ publication; Vault login alone cannot grant npm publishing access.
150
+
151
+ Verify the released consumer independently:
152
+
153
+ ```sh
154
+ npx --yes @crvouga/personal@latest commands --json
155
+ npx --yes @crvouga/personal@latest fleet doctor --all --json
156
+ ```
157
+
158
+ Do not claim release or green CI based on local checks. Deployment of the CI
159
+ workflow requires these source changes to land on main, and remote fleet coverage
160
+ requires actual locally configured SSH aliases and checkout paths.
package/schema.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Developer fleet roster (not infrastructure inventory)",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": ["version", "nodes"],
7
+ "properties": {
8
+ "$schema": { "type": "string" },
9
+ "version": { "const": 1 },
10
+ "nodes": {
11
+ "type": "array",
12
+ "minItems": 1,
13
+ "maxItems": 64,
14
+ "items": {
15
+ "type": "object",
16
+ "additionalProperties": false,
17
+ "required": ["id", "ssh", "workspace"],
18
+ "properties": {
19
+ "id": { "type": "string", "pattern": "^[a-z][a-z0-9-]{0,62}$" },
20
+ "ssh": {
21
+ "type": "string",
22
+ "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._-]{0,252}$"
23
+ },
24
+ "workspace": {
25
+ "type": "string",
26
+ "maxLength": 4096,
27
+ "pattern": "^/[^\\u0000-\\u001f\\u007f]*$"
28
+ },
29
+ "runtime": {
30
+ "type": "string",
31
+ "maxLength": 4096,
32
+ "pattern": "^/[^\\u0000-\\u001f\\u007f]*$"
33
+ },
34
+ "required": {
35
+ "type": "array",
36
+ "uniqueItems": true,
37
+ "default": ["github", "vault", "t3", "toolchain"],
38
+ "items": {
39
+ "enum": [
40
+ "github",
41
+ "vault",
42
+ "t3",
43
+ "vscode",
44
+ "toolchain",
45
+ "codex",
46
+ "claude",
47
+ "services",
48
+ "workstation"
49
+ ]
50
+ }
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }