Hand off with dejima's dependencies met and its source on the box #11

Merged
branderson merged 1 commits from dejima-handoff into main 2026-08-17 00:23:40 -07:00
Collaborator

Summary generated by LLM agent

Stacked on #10. Merge that one first and this diff drops to its own commit.

A devbox is a machine that runs dejima, and this repository set one up without installing anything dejima needs. Bootstrap finished on a host with no container runtime at all.

What the host now gets

docker.io and docker-cli from Debian, compose by whichever route the release offers, and python3, jq and git - dejima's own stated prerequisites. The first two are not incidental: the host-side credential proxy is python3, and dejima's installer reads settings.json with jq.

The compose ladder is the sister repository's, unchanged in substance: the distribution package, then backports, then Docker's own apt repository last and announced, because it is a third-party source on a machine whose value is a small, known surface. It exists because docker-compose-v2 does not exist on bookworm, and naming it in the package list aborts the whole run there.

docker-cli is named explicitly alongside docker.io for the same reason it is in ci-hardener: Debian splits the client from the daemon, --no-install-recommends drops it, and apt then reports success on a set with no docker command in it.

The line between the two projects

bin/dejima-devbox-clone puts dejima's source at ~/dejima and stops.

It does not run dejima's install.sh. That step writes into ~/.claude, ~/.local/bin and ~/.config/dejima - the operator's account rather than this VM's plumbing - and what it does there is dejima's to define and to change. A copy of another project's setup contract kept here would go stale with nothing to notice it.

What is worth doing on this side is the ordering. Source has to arrive over a network that is about to be locked down, so the clone happens before the addressing step and before the node rules go on. Fetch it afterwards and it is a clone against a firewall rather than a clone.

An existing directory is never modified. A checkout somebody has been working in is not this program's to pull into, and a directory that is not a checkout at all is far more likely to be theirs than to be a mistake. An empty directory is not a conflict - git clones into one, and whoever made it meant this.

The handoff at the end of bootstrap prints the one command that finishes the job, whose account to run it as, and - only when it just added them - that they need to log out first, because a shell that was already open does not have the new group and that presents as Docker being broken.

Two things that follow

The account. dejima runs as a person, not as root. Bootstrap asks which one (defaulting to SUDO_USER), adds it to the docker group and records it in the config. The docker group is root on this VM in one command; docs/threat-model.md already treats that as a grant rather than a lapse and writes the blast radius assuming it.

The radius report was inventorying the wrong home. It runs as root - from the timer, or under sudo, where $HOME is root's - while everything it reports on lives in the account that runs dejima. Root's home on a devbox holds nothing, so the report read none found on a box whose credentials sat one directory over. That is the reassuring direction to be wrong in, which is the direction this repository cares about most.

It now resolves the recorded account, falling back to $HOME when the key is absent so a host set up before this reports exactly what it always did. README and the runbook said "root's own home" as though it were intended; both are corrected.

Tested

98 tests pass.

test/clone.bats clones over file:// from a fixture repository built in the test, so it needs no network and exercises the real git invocation rather than a stub: the clone itself, DEJIMA_DIR, a URL argument, an existing checkout left alone with its uncommitted file still there, a non-checkout directory refused without being touched, an empty one accepted, a bad URL failing loudly, and that nothing is written into ~/.claude, ~/.local/bin or ~/.config/dejima - which is the test that keeps the boundary between the two projects from drifting the first time running install.sh here looks convenient.

test/audit.bats covers the home resolution, including an account that no longer exists.

test/units.bats now checks that every program in bin/ is executable. Not theoretical: dejima-devbox-clone was written without the bit and every caller got 126 with a message about permissions rather than about the program. install -m 0755 hides this for the audit and for nothing else.

**Summary generated by LLM agent** **Stacked on [#10](https://gitbox.branderson.io/dejima/dejima-devbox-hardener/pulls/10).** Merge that one first and this diff drops to its own commit. A devbox is a machine that runs dejima, and this repository set one up without installing anything dejima needs. Bootstrap finished on a host with no container runtime at all. ## What the host now gets `docker.io` and `docker-cli` from Debian, compose by whichever route the release offers, and `python3`, `jq` and `git` - dejima's own stated prerequisites. The first two are not incidental: the host-side credential proxy is python3, and dejima's installer reads `settings.json` with jq. The compose ladder is the sister repository's, unchanged in substance: the distribution package, then backports, then Docker's own apt repository last and announced, because it is a third-party source on a machine whose value is a small, known surface. It exists because `docker-compose-v2` does not exist on bookworm, and naming it in the package list aborts the whole run there. `docker-cli` is named explicitly alongside `docker.io` for the same reason it is in ci-hardener: Debian splits the client from the daemon, `--no-install-recommends` drops it, and apt then reports success on a set with no `docker` command in it. ## The line between the two projects `bin/dejima-devbox-clone` puts dejima's source at `~/dejima` and stops. It does not run dejima's `install.sh`. That step writes into `~/.claude`, `~/.local/bin` and `~/.config/dejima` - the operator's account rather than this VM's plumbing - and what it does there is dejima's to define and to change. A copy of another project's setup contract kept here would go stale with nothing to notice it. What *is* worth doing on this side is the ordering. Source has to arrive over a network that is about to be locked down, so the clone happens before the addressing step and before the node rules go on. Fetch it afterwards and it is a clone against a firewall rather than a clone. An existing directory is never modified. A checkout somebody has been working in is not this program's to pull into, and a directory that is not a checkout at all is far more likely to be theirs than to be a mistake. An empty directory is not a conflict - git clones into one, and whoever made it meant this. The handoff at the end of bootstrap prints the one command that finishes the job, whose account to run it as, and - only when it just added them - that they need to log out first, because a shell that was already open does not have the new group and that presents as Docker being broken. ## Two things that follow **The account.** dejima runs as a person, not as root. Bootstrap asks which one (defaulting to `SUDO_USER`), adds it to the `docker` group and records it in the config. The `docker` group is root on this VM in one command; `docs/threat-model.md` already treats that as a grant rather than a lapse and writes the blast radius assuming it. **The radius report was inventorying the wrong home.** It runs as root - from the timer, or under `sudo`, where `$HOME` is root's - while everything it reports on lives in the account that runs dejima. Root's home on a devbox holds nothing, so the report read `none found` on a box whose credentials sat one directory over. That is the reassuring direction to be wrong in, which is the direction this repository cares about most. It now resolves the recorded account, falling back to `$HOME` when the key is absent so a host set up before this reports exactly what it always did. README and the runbook said "root's own home" as though it were intended; both are corrected. ## Tested 98 tests pass. `test/clone.bats` clones over `file://` from a fixture repository built in the test, so it needs no network and exercises the real git invocation rather than a stub: the clone itself, `DEJIMA_DIR`, a URL argument, an existing checkout left alone *with its uncommitted file still there*, a non-checkout directory refused without being touched, an empty one accepted, a bad URL failing loudly, and that nothing is written into `~/.claude`, `~/.local/bin` or `~/.config/dejima` - which is the test that keeps the boundary between the two projects from drifting the first time running `install.sh` here looks convenient. `test/audit.bats` covers the home resolution, including an account that no longer exists. `test/units.bats` now checks that every program in `bin/` is executable. Not theoretical: `dejima-devbox-clone` was written without the bit and every caller got 126 with a message about permissions rather than about the program. `install -m 0755` hides this for the audit and for nothing else.
dejima-agent added 1 commit 2026-08-17 00:08:33 -07:00
Hand off with dejima's dependencies met and its source on the box
tests / runner-env (pull_request_target) Successful in 37s
tests / tests (pull_request_target) Successful in 57s
a4c90efd7b
A devbox is a machine that runs dejima, and this repository set one up
without installing anything dejima needs. Bootstrap finished on a host
with no container runtime at all.

It now installs docker.io and docker-cli from Debian, compose by whichever
route the release offers, and python3, jq and git - dejima's own stated
prerequisites, the first two for its host-side credential proxy and its
installer. The compose ladder is the sister repository's, unchanged in
substance: distribution package, then backports, then Docker's own apt
repository last and said out loud, because it is a third-party source on a
machine whose value is a small, known surface.

bin/dejima-devbox-clone then puts dejima's source at ~/dejima, and stops
there. It does not run dejima's install.sh: that step writes into
~/.claude, ~/.local/bin and ~/.config/dejima, and what it does there is
dejima's to define and to change. A copy of another project's setup
contract kept here would go stale with nothing to notice. What is worth
doing on this side is the ordering - source has to arrive over a network
that is about to be locked down, so the clone happens before the
addressing step and before the node rules go on.

An existing directory is never modified. A checkout somebody has been
working in is not this program's to pull into, and a directory that is not
a checkout is more likely to be theirs than to be a mistake.

Two smaller things follow from the same change:

The account. dejima runs as a person, not as root, so bootstrap asks which
one, adds it to the docker group and records it. The docker group is root
on this VM in one command - docs/threat-model.md already treats that as a
grant rather than a lapse, and the blast radius there is written assuming
it.

The audit's radius report was inventorying the wrong home. It runs as root,
from the timer or under sudo, where HOME is root's - and root's home on a
devbox holds nothing, so the report read "none found" on a box whose
credentials sat one directory over. It now resolves the recorded account,
falling back to $HOME so a host set up before this reports what it always
did.

Tested: 98 tests pass. test/clone.bats clones over file:// from a fixture
repository, so it needs no network: the clone, DEJIMA_DIR, an existing
checkout left alone with its uncommitted work intact, a non-checkout
directory refused, an empty one accepted, a bad URL failing loudly, and
that nothing is written into the account it clones for. test/audit.bats
covers the home resolution. test/units.bats now checks that every program
in bin/ is executable, which dejima-devbox-clone was not.
branderson approved these changes 2026-08-17 00:23:34 -07:00
branderson merged commit 1f75dd422a into main 2026-08-17 00:23:40 -07:00
Sign in to join this conversation.