Commit Graph
2 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 893130940e create docker's storage ourselves, so the strip is not a race
host verified green's reprovision: claude 2.1.228 installs and runs as the
member, the file browser reads their home, rootless Docker runs and sees 0
containers while the owner has 8. First end-to-end proof of any of this.

One thing came out dirty. ~/.local/share/docker carried the home's inherited
default ACLs after a "successful" strip, because the strip was guarded on
existsSync and only the daemon creates that directory. On a first run the guard
was false and the strip no-opped; the retry then started the daemon, which
created the directory and inherited the defaults. The run meant to clean it up
was the one that made it, and the guard could not tell "nothing to strip" from
"nothing there yet".

Now created by us before the daemon exists — member-owned, 700, nothing to
inherit — and the strip is unconditional afterwards, repairing an account
provisioned before this and no-opping on a clean one. A guard that depends on
another process having got there first is a race however it is written; the fix
is owning the order rather than testing for it.

Third bug of this class tonight: an implicit parent directory, a strip guarded
on another process's work, and an installer piped into the wrong shell. All
three were invisible until a real member account existed, which is the argument
for making the second one sooner than feels necessary.

Left alone deliberately: .local being unreadable by the platform (a decision
about intent, not a defect, and the owner's), and the -u 70 + bind mount
observation, whose probe host already distrusts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 00:08:55 +00:00
pastilhasandClaude Opus 5 3bea46f2d7 rootless docker per member — provisioning works, running a container does not yet
Not finished. Committed because the diagnosis is worth more than the code.

WHY ROOTLESS AND NOT THE DOCKER GROUP. `usermod -aG docker <user>` is the one-line version
and it is root: `docker run -v /:/host -it alpine chroot /host` is a root shell, which reads
.env, every other member's home and the wallet seed. Every boundary from today, bypassed by
one documented command. Rootless gives what was actually asked for — a daemon per account,
containers in that account's user namespace, images in their own home.

VERIFIED on this host: provisioning succeeds, the server reports 29.5.0, the daemon runs as
the member, `docker pull` puts 403 MB under their own home, and `docker ps -a` shows nothing
while the owner has four containers. That last line is the isolation, measured.

NOT VERIFIED: actually running a container. It failed, and the cause is an interaction
between two things built today:

  failed to copy xattrs: failed to set xattr "system.posix_acl_default" on …/volumes/…/_data

Creating a volume copies xattrs, and the DEFAULT ACLs on a member's home — added so the file
browser could read their files — are inherited by Docker's storage, where a mapped id inside
a user namespace is not a valid id to set. Both features correct alone. The fix here strips
default ACLs from ~/.local/share/docker only, leaving the access ACLs the file browser needs.

That fix is UNPROVEN. The re-test failed for a different, environmental reason: probe users
recycle uid 1001, and a stale lingering systemd user manager from a previous probe answered
`systemctl --user`, so the unit appeared not to exist. Cleaned with `loginctl terminate-user`.
Retest on a machine that has not had a uid-1001 user, or on a fresh uid.

Also worth knowing before this ships: uid reuse after deleting a member is a real hazard, not
just a test artefact — the next member gets the previous member's uid, and anything left
lingering belongs to them.

setup.sh gains uidmap and dbus-user-session as core packages; the shell template exports
DOCKER_HOST from $XDG_RUNTIME_DIR when the socket exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 20:01:17 +00:00