Files
esh-pfi-infrastructure/scripts/oneoff/albok-browse-withdrawal/README.md
T
vh 70b7281182 ops(albok): vh joins every albok read group; macOS withdrawal-test run staged
Prime, 2026-10-09: "vh gets albok-everything". playbooks/nh3-albok-vh-viewer.yaml
joins vh to albok-read, albok-personal and albok-feedback on nh3-albok and
authorizes his four Macs' public keys for vh; verified at a fresh login that
every wing's .browse/ lists and reads.

The withdrawal harness gains a macOS driver (run-mac.sh: every mountable FUSE
backend, a read-only smoke of the real wings, results copied back), env
overrides for a non-container client, and summarize.py. Sanity-checked as vh
from the Linux container before staging.
2026-10-09 22:45:52 -07:00

76 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
## macOS run (staged 2026-10-09 2245, not yet run)
`run-mac.sh` runs the same trials from the operator's Mac, as `vh`, against a vh-owned replica at
`~vh/albok-browse-test/wing` (`manifest-mac.json`). It tries every FUSE backend the Mac can mount with
(the default, plus `backend=smb` / `backend=fskit` where fuse-t or macFUSE offers them), smoke-tests every
real wing's view as vh (counts only), and copies its results to `~vh/albok-browse-test/results/`. The vh
path was sanity-checked first from the Linux container (N=1 per condition, all four as expected).
mkdir -p ~/albok-browse-test && cd ~/albok-browse-test && scp 'vh@10.100.50.44:albok-browse-test/harness/*' . && zsh run-mac.sh
## 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.