stop creating the container bind-mount directory too

~/.local/dockers existed only so a rootless container's inner uid could traverse to a
bind source. No daemon, no need. Commented out with its reasoning intact in
confineUserTree.

.local itself stays — it is not Docker's. The claude installer targets ~/.local/bin,
and the comment above it records that directory being created root-owned and blocking
the install.

Nothing to change in deprovisionOsAccount: it never called os-user-docker.ts. Its
only Docker-shaped part is capturing /etc/subuid before userdel, which already treats
a missing entry as normal and is worth keeping for any rootless tooling.

Verified: transpiles. No test referenced composeDir.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 11:58:06 +00:00
co-authored by Claude Opus 5
parent 7b32f5bc2f
commit 41cdd1e63a
+43 -31
View File
@@ -369,7 +369,17 @@ export async function confineUserTree(params: {
}
}
// ── One directory where container bind mounts can live ──
// ── One directory where container bind mounts can live: DISABLED 2026-08-13, code kept ──
//
// `~/.local/dockers` existed only for rootless Docker, and members no longer get a daemon — see the
// commented-out step in api/users/provision-os.ts. Creating a blessed bind-mount directory for a daemon
// that is not there is a promise about container storage the platform no longer keeps.
//
// `.local` itself STAYS. It is not Docker's: the `claude` installer targets `~/.local/bin`, and the
// paragraph below is the record of that directory being created root-owned and blocking it.
//
// The reasoning is kept in full because turning containers back on means uncommenting the block, not
// rediscovering why 711 and why the ACLs come off.
//
// The default ACLs above are inherited by everything created in the home afterwards, including
// `default:other::---`. A rootless container's INNER uid is neither the service user nor the member —
@@ -416,37 +426,39 @@ export async function confineUserTree(params: {
]);
if (!madeLocalDir.ok) return { ok: false, error: `could not create ${localDir}: ${madeLocalDir.out}` };
const composeDir = join(home, '.local', 'dockers');
const madeComposeDir = await run([
'sudo',
'-n',
'install',
'-d',
'-o',
String(params.uid),
'-g',
String(params.gid),
// 711, not 700, and this is the whole point of the directory. A container's inner uid is `other`, so it
// needs `x` HERE to reach a bind source inside — 700 blocks the path before any ACL matters. `r` stays
// off, so nothing can list it. Safe because the home above is 700: no other account can traverse this
// far in the first place, and the only uids that get here are the member's own containers.
'-m',
'711',
composeDir,
]);
if (!madeComposeDir.ok) return { ok: false, error: `could not create ${composeDir}: ${madeComposeDir.out}` };
// `-b`, not `-k`: remove ACCESS entries as well as defaults, leaving plain POSIX modes.
// const composeDir = join(home, '.local', 'dockers');
// const madeComposeDir = await run([
// 'sudo',
// '-n',
// 'install',
// '-d',
// '-o',
// String(params.uid),
// '-g',
// String(params.gid),
// // 711, not 700, and this is the whole point of the directory. A container's inner uid is `other`, so
// // it needs `x` HERE to reach a bind source inside — 700 blocks the path before any ACL matters. `r`
// // stays off, so nothing can list it. Safe because the home above is 700: no other account can
// // traverse this far in the first place, and the only uids that get here are the member's own
// // containers.
// '-m',
// '711',
// composeDir,
// ]);
// if (!madeComposeDir.ok) return { ok: false, error: `could not create ${composeDir}: ${madeComposeDir.out}` };
//
// `-k` alone left `mask::---` behind — inherited named entries with every permission masked off, reading
// as `user:pastilhas:rwx #effective:---`. An ACL that says one thing and means another is worse than no
// ACL, and container storage is the one place in the home that wants ordinary mode bits and nothing else.
//
// AFTER the recursive grant above, or what it just set is re-inherited here.
const stripped = await run(['sudo', '-n', 'setfacl', '-R', '-b', composeDir]);
if (!stripped.ok) {
return { ok: false, error: `could not clear inherited ACLs from ${composeDir}: ${stripped.out}` };
}
// // `-b`, not `-k`: remove ACCESS entries as well as defaults, leaving plain POSIX modes.
// //
// // `-k` alone left `mask::---` behind — inherited named entries with every permission masked off,
// // reading as `user:pastilhas:rwx #effective:---`. An ACL that says one thing and means another is worse
// // than no ACL, and container storage is the one place in the home that wants ordinary mode bits and
// // nothing else.
// //
// // AFTER the recursive grant above, or what it just set is re-inherited here.
// const stripped = await run(['sudo', '-n', 'setfacl', '-R', '-b', composeDir]);
// if (!stripped.ok) {
// return { ok: false, error: `could not clear inherited ACLs from ${composeDir}: ${stripped.out}` };
// }
return { ok: true };
} catch (ex) {