Say the harness step where the operator is standing when they skip it
tests / runner-env (pull_request_target) Successful in 34s
tests / tests (pull_request_target) Successful in 54s

The runbook now states the prerequisite, and bootstrap's closing output is
where it gets ignored: that text hands over to install.sh and, until now,
implied that was the last thing between the operator and a session.

So it reports the harness alongside the audit result, and reports it three
ways, because one message for every state is how a check stops being read. No
~/.claude and the harness has never run as that account, which matters beyond
the credential: dejima mounts paths under that directory, and a bind mount
whose source is missing is a root-owned empty directory in somebody's home
rather than an error naming what is absent. The directory but no
.credentials.json and it has never signed in here, which is the state that
fails at the API with the container looking like the broken part, so the API
key route is named there too. Both present and it says what it actually knows,
which is that a file exists rather than that a token works.

It checks and does not fix. The sign-in belongs to the account and writes a
credential this program has no business handling, and §7 of the runbook is the
argument for keeping root's hands off it.
This commit is contained in:
2026-08-17 15:29:18 -07:00
parent 54085211c6
commit 903983d63f
3 changed files with 53 additions and 1 deletions
+42
View File
@@ -359,6 +359,48 @@ else
say " in the guest can apply them, and this program cannot either."
fi
# ---------------------------------------------------------------------------
# 10. The prerequisite this program deliberately does not meet.
#
# dejima takes two things from the account that launches it: a harness
# installed for that account, and that harness signed in. Both are the
# account's rather than the machine's, and the sign-in writes a credential this
# program has no business handling, so it says what has to be true instead of
# arranging it.
#
# One half is checkable from here and the other is not, and the two messages
# differ because saying the same thing either way is how a check stops being
# read. Whether the harness has ever run: yes if ~/.claude exists, and that
# directory matters beyond the credential because dejima mounts paths under it.
# A bind mount whose source is missing is a root-owned empty directory in
# somebody's home rather than an error naming what is absent. Whether the token
# in it still works: not from here. A file being present is where this stops.
say ""
if [ ! -d "$target_home/.claude" ]; then
say "The harness has not run as $target_user on this box."
say ""
say " Install it for that account and start it once, signed in, before"
say " dejima's first session. $target_home/.claude does not exist yet,"
say " and it is both where the credential lands and what dejima mounts"
say " into the container."
elif [ ! -f "$target_home/.claude/.credentials.json" ]; then
say "The harness has run as $target_user and has not signed in here."
say ""
say " There is no $target_home/.claude/.credentials.json. The credential"
say " proxy runs out here, reads that file and hands the container a"
say " placeholder, so until it exists a session fails at the API and the"
say " container is what looks broken. An API key in that account's"
say " environment is the other way to answer this."
else
say "The harness is signed in for $target_user, as far as this can tell:"
say "$target_home/.claude/.credentials.json is there. Whether that token"
say "still works is between the harness and the backend."
fi
say ""
say " docs/building-a-devbox.md section 5 has both halves, and what each one"
say " breaks when it is the missing one."
# The handoff. Everything above this point is the machine; everything below it
# belongs to dejima, and the boundary is deliberate - see bin/dejima-devbox-clone
# for why the source is fetched here and installed there.
+2 -1
View File
@@ -75,7 +75,8 @@ It asks which account will run dejima, installs what dejima needs from the host
(Docker with the `compose` v2 plugin, `python3`, `jq`, `git`) and adds that
account to the `docker` group, clones dejima to `~/dejima`, offers to pin the
addressing, asks for your forge's hostname, installs the audit with its timer,
runs the audit once, and then tells you where that result leaves the host.
runs the audit once, and then tells you where that result leaves the host and
how far the account has got with §5's harness step.
Everything needing the network happens before the addressing step, which is the
one thing here that can take the network away.
+9
View File
@@ -116,6 +116,15 @@ setup() {
[ "$status" -eq 0 ]
}
@test "bootstrap tells the operator the harness comes before dejima" {
# The other half of that handoff, and the one with nothing else pointing at
# it. dejima needs a harness installed for the account and signed in, this
# program arranges neither, and an operator who reads only its output
# reaches the first session with a credential proxy holding nothing.
grep -q 'harness' "$REPO/bin/dejima-devbox-bootstrap"
grep -q '\.credentials\.json' "$REPO/bin/dejima-devbox-bootstrap"
}
@test "bootstrap does not clone into root's home" {
# It runs under sudo. $HOME there is root's, so the target has to be
# resolved from the account rather than inherited.