boxdown 1.0.0 → 1.2.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.
Files changed (76) hide show
  1. package/README.md +135 -12
  2. package/assets/devcontainer/README.md +65 -21
  3. package/assets/devcontainer/devcontainer.json +27 -15
  4. package/assets/devcontainer/hooks/initialize.sh +76 -22
  5. package/assets/devcontainer/hooks/post-create.sh +70 -12
  6. package/assets/devcontainer/hooks/post-start.sh +20 -13
  7. package/assets/devcontainer/ssh-config-install.sh +12 -3
  8. package/assets/devcontainer/start.sh +721 -44
  9. package/assets/devcontainer/utils/coding-agent-cli-update.sh +267 -3
  10. package/assets/devcontainer/utils/deps-install.sh +68 -0
  11. package/assets/devcontainer/utils/git-config-bootstrap.sh +128 -0
  12. package/assets/devcontainer/utils/git-signing-bootstrap.sh +109 -0
  13. package/assets/devcontainer/utils/python-bootstrap.sh +69 -0
  14. package/assets/devcontainer/utils/secret-env-bootstrap.sh +22 -0
  15. package/assets/devcontainer/utils/ssh-agent-proxy-bootstrap.sh +27 -0
  16. package/assets/devcontainer/utils/ssh-agent-proxy.mjs +39 -0
  17. package/assets/devcontainer/utils/ssh-bootstrap.sh +9 -0
  18. package/dist/bin/cli.cjs +1 -1
  19. package/dist/bin/cli.mjs +1 -1
  20. package/dist/main-BDgyf2t5.cjs +5758 -0
  21. package/dist/main-J4_2Up3o.mjs +5718 -0
  22. package/dist/main-J4_2Up3o.mjs.map +1 -0
  23. package/dist/main.cjs +5 -2
  24. package/dist/main.d.cts +501 -4
  25. package/dist/main.d.cts.map +1 -1
  26. package/dist/main.d.mts +501 -4
  27. package/dist/main.d.mts.map +1 -1
  28. package/dist/main.mjs +2 -2
  29. package/docs/README.md +1 -0
  30. package/docs/architecture.md +32 -0
  31. package/docs/development.md +13 -6
  32. package/docs/features/README.md +2 -0
  33. package/docs/features/commit-signing.md +94 -0
  34. package/docs/features/generated-config-and-state.md +73 -5
  35. package/docs/features/github-auth-refresh.md +15 -2
  36. package/docs/features/lifecycle.md +103 -11
  37. package/docs/features/setup.md +66 -0
  38. package/docs/features/ssh-config-and-proxy.md +228 -7
  39. package/docs/features/start-and-shell.md +45 -5
  40. package/docs/superpowers/plans/2026-07-11-default-commit-signing.md +110 -0
  41. package/docs/superpowers/plans/2026-07-11-progress-ci-regression.md +125 -0
  42. package/docs/superpowers/plans/2026-07-17-node-ssh-agent-proxy-signing.md +132 -0
  43. package/docs/superpowers/plans/2026-07-17-runtime-secret-environment.md +174 -0
  44. package/docs/superpowers/specs/2026-07-11-default-commit-signing-design.md +416 -0
  45. package/docs/superpowers/specs/2026-07-11-progress-ci-regression-design.md +43 -0
  46. package/docs/superpowers/specs/2026-07-17-node-ssh-agent-proxy-design.md +63 -0
  47. package/docs/superpowers/specs/2026-07-17-runtime-secret-environment-design.md +194 -0
  48. package/docs/testing.md +35 -2
  49. package/docs/todo.md +2 -2
  50. package/package.json +1 -1
  51. package/src/claude-app-config.ts +304 -0
  52. package/src/cli-style.ts +43 -0
  53. package/src/codex-app-config.ts +656 -0
  54. package/src/config.ts +80 -10
  55. package/src/constants.ts +12 -0
  56. package/src/devcontainer.ts +511 -64
  57. package/src/doctor.ts +292 -30
  58. package/src/git-signing.ts +267 -0
  59. package/src/interactive-prompts.ts +692 -0
  60. package/src/list.ts +16 -2
  61. package/src/logging.ts +164 -0
  62. package/src/main.ts +1214 -63
  63. package/src/metadata.ts +52 -3
  64. package/src/package-info.ts +18 -0
  65. package/src/paths.ts +71 -11
  66. package/src/process.ts +80 -2
  67. package/src/progress.ts +593 -0
  68. package/src/purge.ts +216 -0
  69. package/src/shell.ts +25 -0
  70. package/src/ssh-config.ts +134 -16
  71. package/src/ssh-install-targets.ts +111 -0
  72. package/src/ssh-key.ts +53 -13
  73. package/src/status.ts +5 -0
  74. package/dist/main-BuEptwlL.cjs +0 -1707
  75. package/dist/main-ZFTrSVgt.mjs +0 -1685
  76. package/dist/main-ZFTrSVgt.mjs.map +0 -1
@@ -4,10 +4,12 @@ This directory explains Boxdown's user-facing capabilities and the main internal
4
4
  flows behind them.
5
5
 
6
6
  - [Start command](./start-and-shell.md)
7
+ - [Setup command](./setup.md)
7
8
  - [Lifecycle commands](./lifecycle.md)
8
9
  - [SSH config and proxy workflow](./ssh-config-and-proxy.md)
9
10
  - [GitHub auth refresh](./github-auth-refresh.md)
10
11
  - [Generated config and state](./generated-config-and-state.md)
12
+ - [Commit signing](./commit-signing.md)
11
13
 
12
14
  Most features share the same first step: resolve a target workspace, derive
13
15
  workspace-specific state paths, generate a devcontainer override config, and
@@ -0,0 +1,94 @@
1
+ # Commit signing
2
+
3
+ New Boxdown environments attempt SSH commit signing by default. Boxdown forwards
4
+ the host SSH agent, selects a signing identity only when there is one
5
+ unambiguous candidate, and keeps the private key on the host.
6
+
7
+ If the agent is unavailable, has no identities, or has multiple identities
8
+ that Boxdown cannot distinguish, Boxdown warns and configures unsigned commits.
9
+ It never guesses a key and does not block setup or commits.
10
+
11
+ Signing readiness is resolved when generated devcontainer configuration is
12
+ written. A container created while readiness is unavailable does not gain an
13
+ SSH-agent mount later; recreate it after correcting the warning:
14
+
15
+ ```bash
16
+ boxdown setup --workspace /path/to/project --recreate
17
+ ```
18
+
19
+ The container can request operations from identities exposed by the forwarded
20
+ agent. Use a dedicated signing identity or an agent that confirms sensitive
21
+ operations when that exposure is unacceptable.
22
+
23
+ On Docker Desktop, Boxdown places a small root-owned relay between the
24
+ host-facing agent socket and the non-root container user. The relay exposes no
25
+ private-key material and lets normal `node`-user Git commits reach the host
26
+ agent.
27
+
28
+ GitHub verification is separate from signing. Upload the selected public key as
29
+ a GitHub SSH signing key once to receive the Verified badge:
30
+
31
+ ```bash
32
+ gh ssh-key add /path/to/signing-key.pub --type signing --title "Boxdown commit signing"
33
+ ```
34
+
35
+ The same key may already be registered for GitHub SSH authentication; GitHub
36
+ requires a second registration with type `signing`.
37
+
38
+ ## Optional: configure SSH signing on the host
39
+
40
+ Boxdown can sign commits without changing the host Git configuration. The
41
+ container uses the selected identity from the host SSH agent directly. Configure
42
+ the host as well when you also make commits outside Boxdown, or when you want
43
+ to make the selected Boxdown identity explicit.
44
+
45
+ First, verify the intended identity is loaded in the SSH agent. `ssh-add -l`
46
+ prints loaded key fingerprints:
47
+
48
+ ```bash
49
+ ssh-add -l
50
+ ```
51
+
52
+ Then configure Git to use that public key for SSH signing:
53
+
54
+ ```bash
55
+ git config --global gpg.format ssh
56
+ git config --global user.signingkey ~/.ssh/id_ed25519.pub
57
+ git config --global --unset-all gpg.program || true
58
+ git config --global commit.gpgsign true
59
+ ```
60
+
61
+ Replace `~/.ssh/id_ed25519.pub` with the selected public key when you use a
62
+ different identity. The public key must also be registered with GitHub as a
63
+ Signing key for pushed commits to display the Verified badge.
64
+
65
+ Boxdown accepts `user.signingkey` as an inline SSH public key, a `key::` public
66
+ key, an absolute path, a `~/` path, or a path relative to the workspace. Only
67
+ public-key files are read. An explicitly configured key is authoritative: if
68
+ it is unreadable, invalid, or absent from the SSH agent, Boxdown disables
69
+ signing instead of selecting another identity.
70
+
71
+ ## User configuration precedence
72
+
73
+ Boxdown defaults to SSH signing only when Git has no explicit signing
74
+ preference. A repository-local `commit.gpgsign=false`, a non-SSH `gpg.format`,
75
+ or an explicit `gpg.program` is preserved. Explicit SSH signing remains
76
+ supported; Boxdown maps an inaccessible host public-key path to the selected
77
+ public-key snapshot inside the container. This policy is intentional and may
78
+ be revisited in a future release if Boxdown becomes authoritative.
79
+
80
+ Git can require `gpg.ssh.allowedSignersFile` for trust-aware local verification
81
+ such as `git log --show-signature`. It is not required to create SSH signatures
82
+ or for GitHub to verify a commit.
83
+
84
+ ## Troubleshooting
85
+
86
+ Signing failures remain non-blocking. User-facing lifecycle commands print a
87
+ concise reason, while the workspace `boxdown.log` records a stable reason code
88
+ and sanitized diagnostic detail. Container validation further distinguishes a
89
+ missing mounted public key, an unavailable forwarded agent, a selected key that
90
+ is not loaded, and a failed disposable signed-commit probe.
91
+
92
+ Run `boxdown doctor --workspace /path/to/project` to recheck host identity
93
+ selection and GitHub signing-key registration. After fixing the reported
94
+ condition, use `--recreate`; existing unsigned commits are not rewritten.
@@ -35,6 +35,21 @@ Boxdown-specific override is not set.
35
35
  SSH private keys live in persistent data. A public-key-only runtime directory is
36
36
  used for the container mount.
37
37
 
38
+ Boxdown-managed lifecycle command output is appended to one per-workspace log
39
+ file:
40
+
41
+ ```text
42
+ ~/.local/share/boxdown/workspaces/<workspace-hash>/boxdown.log
43
+ ```
44
+
45
+ The log is append-only across lifecycle runs. Each run starts a timestamped
46
+ section and records Boxdown-managed command output, Docker/devcontainer child
47
+ process output, and command exit codes. Full interactive shell, agent, and
48
+ tunnel session bytes are not tee'd into the log.
49
+
50
+ The log lives with the workspace's persistent data so `boxdown status` can show
51
+ its path and `boxdown purge` removes it with the rest of the workspace data.
52
+
38
53
  Each touched workspace also records inventory metadata at:
39
54
 
40
55
  ```text
@@ -44,21 +59,74 @@ Each touched workspace also records inventory metadata at:
44
59
  `boxdown list` reads these metadata files as its source of truth, then enriches
45
60
  entries with best-effort Docker state.
46
61
 
62
+ Metadata may also record the last inspected Docker image ID for the workspace so
63
+ `boxdown purge` can remove that exact image even after the container is gone.
64
+
65
+ ## Runtime Secret State
66
+
67
+ `ANTHROPIC_API_KEY`, `SNYK_TOKEN`, and the optional 1Password service-account
68
+ token are written to owner-only files in a per-workspace runtime directory,
69
+ separate from persistent workspace data. Boxdown mounts that directory
70
+ read-only at `/run/boxdown/secrets`; only non-secret mount and Bash-bootstrap
71
+ paths appear in generated Docker configuration.
72
+
73
+ Bash sessions load available files as ordinary environment variables. Missing
74
+ host values and failed 1Password lookup are non-blocking. `boxdown down` and
75
+ `boxdown purge` remove the workspace runtime directory after container removal.
76
+ Boxdown does not use or modify project `.env.development` files.
77
+
78
+ ## External App Config
79
+
80
+ External app integration config is not Boxdown workspace state. Boxdown writes
81
+ it only when an SSH install target is selected from an interactive
82
+ `boxdown setup` or `boxdown ssh install` prompt, or requested explicitly with
83
+ `boxdown setup --target <name>` or `boxdown ssh install --target <name>`.
84
+ Non-interactive runs without `--target` install only the SSH alias and leave
85
+ external app config unchanged.
86
+
87
+ When requested, Boxdown writes Codex's app config at:
88
+
89
+ ```text
90
+ ~/.codex/codex-app/config.json
91
+ ```
92
+
93
+ `BOXDOWN_CODEX_APP_CONFIG` overrides this path for tests and local development.
94
+ The Codex entry refers to the Boxdown-managed SSH alias and the canonical
95
+ container-side workspace path `/workspaces/<repo-name>`. Codex owns later
96
+ global-state records and sidebar entries it creates from that config, but
97
+ `boxdown ssh uninstall` removes the matching Codex sidebar cache entry when
98
+ unregistering the project.
99
+
100
+ When requested, Boxdown writes Claude's SSH remote config at:
101
+
102
+ ```text
103
+ ~/Library/Application Support/Claude/ssh_configs.json
104
+ ```
105
+
106
+ `BOXDOWN_CLAUDE_SSH_CONFIGS` overrides this path for tests and local
107
+ development. The Claude entry refers to the Boxdown-managed SSH alias and adds
108
+ that alias to Claude's trusted host list.
109
+
47
110
  ## Generated Changes
48
111
 
49
112
  Boxdown starts from `assets/devcontainer/devcontainer.json` and rewrites:
50
113
 
51
114
  - `name`, to include the workspace basename.
52
- - `initializeCommand`, to call the host asset script with the target workspace.
115
+ - `initializeCommand`, to call the host asset script with the target workspace
116
+ and host Git config snapshot paths.
53
117
  - `postCreateCommand`, to call mounted container assets.
54
118
  - `postStartCommand`, to call mounted container assets.
55
- - `mounts`, to add the read-only asset mount, public-key mount, and host
56
- `~/.agents` mount when that directory exists.
119
+ - `mounts`, to add the read-only asset mount, public-key mount, host Git config
120
+ snapshot mount, runtime secret mount, host `~/.agents` mount when that
121
+ directory exists, and host `~/.codex/auth.json` read-only mount when that
122
+ file exists.
57
123
  - `containerEnv`, to point SSH bootstrap at the mounted public key and actual
58
124
  container workspace.
59
125
 
60
126
  The target repository is still the Dev Container workspace via
61
127
  `--workspace-folder`.
62
128
 
63
- Mounts are create-time container settings. Use `boxdown start --recreate` after
64
- creating or removing host `~/.agents` so Docker receives the updated mount set.
129
+ Mounts are create-time container settings. Existing containers created before
130
+ runtime-mounted secrets require `boxdown start --recreate`. The same applies
131
+ after creating or removing host `~/.agents` or `~/.codex/auth.json` so Docker
132
+ receives the updated mount set.
@@ -5,6 +5,7 @@
5
5
  ```sh
6
6
  boxdown refresh-gh-token
7
7
  boxdown refresh-gh-token-running
8
+ boxdown refresh-gh-token --verbose
8
9
  ```
9
10
 
10
11
  Both commands copy host GitHub CLI auth into the container using the host token
@@ -16,6 +17,9 @@ gh auth token
16
17
 
17
18
  They do not start a browser login or device-code flow.
18
19
 
20
+ The startup and refresh work uses concise progress output by default. Pass
21
+ `--verbose` to stream raw devcontainer and auth command output while debugging.
22
+
19
23
  These commands are explicit on purpose. Normal `boxdown start`, coding-agent
20
24
  launches, and SSH proxy connections do not copy GitHub credentials into the
21
25
  container.
@@ -48,6 +52,8 @@ container's authenticated `gh` for `git fetch`, `git pull`, and `git push`.
48
52
  For GitHub remotes, Boxdown:
49
53
 
50
54
  - rewrites fetch and push URLs to `https://github.com/<owner>/<repo>.git`
55
+ - configures container-global GitHub HTTPS credentials through the container's
56
+ `gh` auth store
51
57
  - resets inherited `credential.https://github.com.helper` entries locally
52
58
  - adds `!gh auth git-credential` as the local GitHub credential helper
53
59
  - adds a repository-specific HTTPS self-rewrite so broader host rewrites like
@@ -57,8 +63,15 @@ For GitHub remotes, Boxdown:
57
63
  The local Git config changes are written to the workspace repository because the
58
64
  host checkout is mounted into the devcontainer.
59
65
 
66
+ Separately, Boxdown snapshots the host `.gitconfig` into workspace state and
67
+ copies it to a writable `/home/node/.gitconfig` during container creation. That
68
+ container copy is sanitized so tools cloning from directories such as `/tmp` use
69
+ HTTPS GitHub auth and do not inherit host-only helpers, GitHub SSH rewrites, or
70
+ signing programs that are unavailable inside Linux.
71
+
60
72
  If host `gh` is missing, logged out, or cannot return a token, the refresh is a
61
73
  no-op.
62
74
 
63
- `OP_SERVICE_ACCOUNT_TOKEN`, when present, is a 1Password service account token.
64
- It is not a GitHub token and is not used by `gh` or GitHub Git operations.
75
+ `OP_SERVICE_ACCOUNT_TOKEN`, when available through Boxdown's runtime secret
76
+ mount, is a 1Password service account token. It is not a GitHub token and is
77
+ not used by `gh` or GitHub Git operations.
@@ -4,33 +4,48 @@
4
4
 
5
5
  ```sh
6
6
  boxdown list
7
+ boxdown list --details
7
8
  boxdown list --json
9
+ boxdown list --format json
8
10
  boxdown status
9
11
  boxdown status --json
12
+ boxdown status --format json
10
13
  boxdown stop
11
14
  boxdown down
15
+ boxdown purge
12
16
  boxdown doctor
13
17
  ```
14
18
 
15
- Workspace-targeting commands accept `--workspace <path>`. `status` also accepts
16
- `--alias <name>` so its output can match a custom SSH host alias.
19
+ Workspace-targeting commands accept `--workspace <path>`. `down` also accepts
20
+ repeated `--workspace` flags to remove multiple workspace containers in order.
21
+ `purge --workspace` also accepts the `PATH`, `SSH ALIAS`, or unambiguous `REPO`
22
+ value shown by `boxdown list`. `purge` does not accept repeated `--workspace`
23
+ flags. `status` accepts `--alias <name>` so its output can match a custom SSH
24
+ host alias.
17
25
 
18
26
  ## List
19
27
 
20
28
  `list` shows every workspace with Boxdown metadata under the data root,
21
29
  regardless of the directory where the command is run. Human output includes
22
- `STATE`, `REPO`, `PATH`, `SSH ALIAS`, and `CONTAINER` columns.
30
+ `STATE`, `REPO`, `PATH`, and `CONTAINER` columns.
23
31
 
24
- `list --json` prints the same inventory as structured JSON. Docker state is
25
- best-effort: if Docker is unavailable, entries still print and their container
26
- state is `unknown`. If a recorded repository path no longer exists, the entry is
27
- shown as `missing`.
32
+ `list --details` prints one workspace per block with full copyable `path`,
33
+ `ssh alias`, and `container` values.
34
+
35
+ `list --json` and `list --format json` print the same inventory as structured
36
+ JSON. Docker state is best-effort and includes recorded SSH aliases: if Docker
37
+ is unavailable, entries still print and their container state is `unknown`. If a
38
+ recorded repository path no longer exists, the entry is shown as `missing`.
39
+ Container states come from Docker, so active containers usually show `running`,
40
+ stopped containers can show `exited`, and workspaces with no matching container
41
+ show `absent`.
28
42
 
29
43
  ## Status
30
44
 
31
45
  `status` is read-only. It reports the resolved workspace, intended cache/data
32
- paths, generated config path, SSH key paths, SSH alias, and the matching Docker
33
- container state without recording the workspace in Boxdown metadata.
46
+ paths, generated config path, command log path, SSH key paths, SSH alias, and
47
+ the matching Docker container state without recording the workspace in Boxdown
48
+ metadata.
34
49
 
35
50
  Human output distinguishes intended values from detected state. For example, an
36
51
  SSH alias can be shown as a computed default even when the matching Boxdown SSH
@@ -46,7 +61,7 @@ workspace container is running. Missing setup or a stopped/absent container is
46
61
  reported in the output and exits nonzero. Installing an SSH alias is optional
47
62
  and is not required for a healthy status exit.
48
63
 
49
- ## Stop and Down
64
+ ## Stop, Down, and Purge
50
65
 
51
66
  `stop` stops the workspace devcontainer when it is running. If the container is
52
67
  already stopped or absent, it prints a short message and exits 0.
@@ -54,11 +69,88 @@ already stopped or absent, it prints a short message and exits 0.
54
69
  `down` removes the workspace devcontainer with Docker. It does not remove
55
70
  Boxdown cache, generated config, data directories, or SSH keys.
56
71
 
72
+ Lifecycle commands append managed output to the workspace command log under the
73
+ workspace data directory. `--verbose` only controls terminal streaming; it does
74
+ not disable or enable log persistence. Foreground interactive shell, agent, and
75
+ tunnel session bytes are not tee'd into the log.
76
+
77
+ `purge` removes the workspace devcontainer with Docker, force-removes the exact
78
+ Docker image ID Boxdown can inspect or has recorded for the workspace, removes
79
+ Boxdown-managed SSH/Codex/Claude entries for the computed, recorded, and
80
+ provided aliases, and deletes the workspace's Boxdown cache/data directories,
81
+ including the per-workspace command log. It does not delete the target
82
+ repository directory or files inside it.
83
+
84
+ `purge --workspace <value>` first treats `<value>` as a filesystem path. If that
85
+ path does not exist, it looks for an exact `PATH`, then `SSH ALIAS`, then `REPO`
86
+ match in Boxdown metadata. `PATH` and `REPO` are shown by `boxdown list`; use
87
+ `boxdown status`, `boxdown list --details`, or JSON list output for SSH aliases.
88
+ `REPO` must match exactly one workspace; if multiple known workspaces share the
89
+ same repo basename, use `PATH` or `SSH ALIAS`.
90
+
91
+ ```sh
92
+ boxdown purge
93
+ boxdown purge --workspace my-repo-devcontainer
94
+ boxdown purge --workspace my-repo
95
+ boxdown purge --workspace /path/to/my-repo
96
+ ```
97
+
98
+ To remove multiple workspace containers, repeat `--workspace`:
99
+
100
+ ```sh
101
+ boxdown down --workspace /path/to/repo-a --workspace /path/to/repo-b
102
+ ```
103
+
104
+ When `down` runs without `--workspace` from a directory that is not a known
105
+ Boxdown workspace, interactive terminals show a multi-select list of known
106
+ workspaces with existing repository paths. Non-interactive runs keep the
107
+ current-directory behavior.
108
+
109
+ Batch `down` continues after individual workspace failures, but exits nonzero if
110
+ any requested workspace cannot be resolved or removed.
111
+
112
+ When `purge` runs without `--workspace` from a tracked Boxdown workspace, it
113
+ purges that workspace after confirmation. When it runs without `--workspace`
114
+ from an untracked directory, interactive terminals show a multi-select list of
115
+ all tracked workspaces from `boxdown list`, including `running`, `exited`,
116
+ `absent`, `missing`, and `unknown` states. After selection, `purge` asks for one
117
+ destructive confirmation for the selected batch, then purges each selected
118
+ workspace in order. Batch purge continues after individual workspace failures,
119
+ but exits nonzero if any selected workspace fails.
120
+
121
+ In the interactive purge selector, the focused row color-codes only the state
122
+ token: `running` is green, `exited` is yellow, and `absent`, `missing`, or
123
+ `unknown` are red. These colors are terminal UI affordances only; scripts should
124
+ rely on text or JSON state values.
125
+
126
+ Interactive `purge` runs ask for confirmation before removing devcontainer,
127
+ image, SSH/Codex integration, cache, and data state. Non-interactive purge runs
128
+ do not prompt so existing targeted scripts keep working. Non-interactive
129
+ `purge` from an untracked directory fails safely instead of treating the current
130
+ directory as a workspace.
131
+
57
132
  ## Doctor
58
133
 
59
134
  `doctor` validates required host prerequisites: Node, Docker CLI, Docker
60
135
  daemon access, SSH tools, packaged devcontainer assets, and Boxdown's packaged
61
- `@devcontainers/cli` dependency.
136
+ `@devcontainers/cli` dependency. It also uses a local Docker image, when one is
137
+ available, to create and immediately remove disposable containers that verify
138
+ Docker can bind-mount the workspace, Boxdown assets, and Boxdown runtime-state
139
+ paths. The probe never pulls an image or starts container code.
140
+
141
+ Known bind-mount/share failures are errors with the affected host path and
142
+ Docker Desktop remediation guidance. If Docker has no local image or the probe
143
+ cannot complete for another reason, doctor reports a warning instead.
144
+
145
+ `boxdown setup` runs the required doctor checks before it prompts, writes
146
+ workspace metadata, creates SSH keys, generates devcontainer configuration, or
147
+ starts Docker. Required failures stop setup; warnings remain non-blocking.
62
148
 
63
149
  GitHub CLI auth is optional. Missing or unauthenticated `gh` is reported as a
64
150
  warning rather than a failure.
151
+
152
+ Doctor uses the same SSH signing identity precedence as environment creation.
153
+ It honors an explicit `user.signingkey` public-key value or path, can select a
154
+ single GitHub authentication-key match from multiple loaded identities, and
155
+ checks the selected key's GitHub signing registration when available. Signing
156
+ readiness warnings never fail setup.
@@ -0,0 +1,66 @@
1
+ # Setup Command
2
+
3
+ ## Command
4
+
5
+ ```sh
6
+ boxdown setup
7
+ boxdown setup --target codex
8
+ boxdown setup --target claude
9
+ boxdown setup --recreate
10
+ ```
11
+
12
+ `setup` prepares the current workspace for remote tools without opening an
13
+ interactive shell. It accepts:
14
+
15
+ ```sh
16
+ --workspace <path>
17
+ --alias <name>
18
+ --recreate
19
+ --target <name>
20
+ --verbose
21
+ ```
22
+
23
+ ## Flow
24
+
25
+ 1. Resolve the workspace to a real absolute path.
26
+ 2. Ensure per-workspace SSH key material exists.
27
+ 3. Generate a Boxdown-owned devcontainer config.
28
+ 4. Run `devcontainer up --workspace-folder <repo> --override-config <config>`.
29
+ 5. Install or update the Boxdown-managed SSH alias.
30
+ 6. Optionally install selected SSH targets such as Codex or Claude.
31
+
32
+ `setup` prints plain progress sections by default. Docker, Dev Containers CLI,
33
+ and lifecycle hook output is captured and only summarized if a command fails.
34
+ Pass `--verbose` to stream the raw build and hook logs to the terminal.
35
+
36
+ Boxdown also appends the managed setup output to the workspace command log at:
37
+
38
+ ```text
39
+ ~/.local/share/boxdown/workspaces/<workspace-hash>/boxdown.log
40
+ ```
41
+
42
+ The log is written regardless of `--verbose`; the flag only changes terminal
43
+ streaming.
44
+
45
+ When `--target codex` is provided, Boxdown writes the Codex app config entry for
46
+ the same alias and container-side project path used by:
47
+
48
+ ```sh
49
+ boxdown ssh install --target codex
50
+ ```
51
+
52
+ When `--target claude` is provided, Boxdown writes the Claude app SSH remote
53
+ entry for the same alias used by:
54
+
55
+ ```sh
56
+ boxdown ssh install --target claude
57
+ ```
58
+
59
+ When no target is provided, Boxdown uses the same optional target prompt as
60
+ `boxdown ssh install`. In non-interactive shells, setup skips target
61
+ registration unless `--target` is provided.
62
+
63
+ `setup` does not open a shell, launch a coding-agent CLI, or keep a tunnel in the
64
+ foreground. Use `boxdown start`, `boxdown codex`, or `boxdown tunnel` for those
65
+ foreground workflows. Those commands log Boxdown-managed startup steps, but do
66
+ not tee full interactive shell, agent, or tunnel session bytes into the log.