Files
esh-pfi-infrastructure/scripts/oneoff/albok-browse-withdrawal
vh 172d893cdf test(albok): sshfs pathname-withdrawal test for the .browse/ view passes (unit 4.3 exposure gate)
Replica of a browse view on nh3-albok, Linux sshfs 3.7.3 client in an ephemeral
container on nh3-dev, N=5 per condition. Fresh open of a never-opened child
through a cached listing: ENOENT 5/5 with sshfs defaults and 5/5 with hour-long
client caches. Null control (no rename) opens 5/5; held fd and failed-rename
positive controls read 5/5. Replica removed afterwards.

Also closes the post-clear checks in persistent-memory: fv-ml1 GPU 0/1 plateau,
nh3-nas usage, and the mislabelled sfsrv-ana pre-pbs4 item.
2026-10-09 20:23:30 -07:00
..

albok browse view: sshfs pathname-withdrawal test (unit 4.3 exposure gate)

The albok unit 4.3 contract gates any workstation exposure of a wing's .browse/ on infra-ops demonstrating, on the chosen share surface, that renaming .browse/ away on the server denies a fresh open of a never-opened child through a listing the client has cached. The rename is what albok-service does when it withholds a view (.browse/ → .browse.withheld/).

Result (2026-10-09 2019–2021 PT): PASS on sshfs follow_symlinks, Linux client. Raw output: results-20261009-2019.json; replica ids: manifest.json.

Harness

  • Server: a replica of a browse view on nh3-albok (setup-replica.py): 60 × 1 MiB objects at <wing>/<id[:2]>/<id>/<blob>, .browse/stash/<name> → ../../<id[:2]>/<id>/<blob>, the same shape as the live view. Same root ext4 and the same OpenSSH sftp-server as /srv/albok. The live view was NOT used: renaming it races albok-service's link/unlink and a failed mandatory unlink withholds the wing. The replica was removed after the run (ops-log 2026-10-10T03:22:22Z).
  • Client: ephemeral debian:bookworm-slim container on nh3-dev (run.sh): SSHFS 3.7.3, libfuse 3.14.0, FUSE kernel interface 7.31, host kernel 6.1. Mount: ro,follow_symlinks plus SSH options; the mount root is .browse/ alone.
  • Per trial, on a fresh mount: list stash/ and stat every child (the client's caches now hold the listing and every child's attributes); open+close child A; open child B and hold the fd; rename on the server with os.rename; then immediately list again, stat C, open C (never opened before), read B at offset 1 MiB − 8 KiB (past readahead, so it must reach the server), re-open A.
  • Conditions, N = 5 each, distinct children every trial: null (no rename), default (sshfs defaults), aggressive (kernel_cache,entry_timeout=3600, attr_timeout=3600,dir_cache=yes,dcache_timeout=3600), failed_rename (target pre-created non-empty, so rename(2) refuses with ENOTEMPTY).

Results

condition rename cached list after stat C after open C (fresh) held B far read re-open A list at +25 s
null (null control) none 60 ok opens, bytes ok 5/5 ok 5/5 opens 5/5 n/a
default ok 60 (stale) ok (cached) ENOENT 5/5 ok 5/5 ENOENT 5/5 ENOENT 5/5
aggressive caches ok 60 (stale) ok (cached) ENOENT 5/5 ok 5/5 ENOENT 5/5 n/a
failed_rename refused ENOTEMPTY 60 ok opens, bytes ok 5/5 ok 5/5 opens 5/5 n/a

The fresh open was attempted 2–6 ms after the rename returned. Every trial started and ended with the server in the clean state (.browse/ present, no .browse.withheld).

  • The gate holds. The client still listed the child and stat'ed it from cache, and the open failed anyway: each sshfs open is an SFTP OPEN by path, resolved on the server, and hour-long client caches do not change that.
  • Controls. With no rename every open succeeds (the method does not manufacture failures). The held fd keeps reading after the rename, and a refused rename leaves every path served, so the instrument does register access that survives a rename (positive controls).
  • What a rename does NOT withdraw: a file already open on the client keeps reading through its SFTP handle. That is the contract's narrowed guarantee (new access by pathname only).
  • Failed rename: the path remains and is served. The service cannot deny opens it could not withdraw; its BROWSE_WITHHELD marker only stops publication.

Limits

  • Proven for the Linux kernel FUSE client only. A macOS workstation runs a different FUSE backend (macFUSE kext, macFUSE FSKit, or fuse-t, which is NFS-backed and may cache handles the way the contract warns NFS does). Re-run this harness on the real workstation before trusting that surface: rebuild the replica, then run run-trials.py directly with ALBOK_TEST_KEY, ALBOK_TEST_KNOWN_HOSTS, ALBOK_TEST_MNT (and ALBOK_TEST_SRV) set.
  • The mount root is a presentation choice, not a boundary. The SSH user's authority is whatever its groups can read: a member of the wing's read group can mount the wing root and see raw object directories, sidecars with their sensitivity records, and git history. The contract already says this ("the wing group's raw read is authority over files").
  • The committed run-trials.py adds env overrides and a umount fallback after the run; the container defaults it ran with are unchanged.