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.
This commit is contained in:
vh
2026-10-09 22:45:52 -07:00
parent 172d893cdf
commit 70b7281182
9 changed files with 202 additions and 13 deletions
@@ -50,6 +50,16 @@ the server in the clean state (`.browse/` present, no `.browse.withheld`).
- **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
File diff suppressed because one or more lines are too long
@@ -0,0 +1,79 @@
#!/bin/zsh
# macOS client run of the albok browse-view withdrawal test (unit 4.3 exposure gate).
# Staged on nh3-albok under ~vh/albok-browse-test/harness/ with a vh-owned replica beside it.
# On the Mac:
# mkdir -p ~/albok-browse-test && cd ~/albok-browse-test && scp 'vh@10.100.50.44:albok-browse-test/harness/*' . && zsh run-mac.sh
# It tests every FUSE backend this Mac can mount with (the backend decides whether a rename
# withdraws access), smoke-tests the real wings as vh, and copies the results back to nh3-albok.
set -u
cd "${0:A:h}"
SRV=vh@10.100.50.44
MNT=$PWD/mnt
N=${N:-5}
stamp=$(date +%Y%m%d-%H%M)
mkdir -p "$MNT" results
unmount() { umount "$MNT" 2>/dev/null || diskutil unmount force "$MNT" >/dev/null 2>&1; }
{
echo "date: $(date '+%F %H%M %Z') host: $(scutil --get LocalHostName 2>/dev/null)"
sw_vers
echo "arch: $(uname -m)"
echo "sshfs: $(command -v sshfs || echo MISSING)"
command -v sshfs >/dev/null && { sshfs -V 2>&1 | head -5; otool -L "$(command -v sshfs)" 2>/dev/null | grep -i fuse; }
[[ -d /Library/Filesystems/macfuse.fs ]] && echo "macFUSE: $(defaults read /Library/Filesystems/macfuse.fs/Contents/Info CFBundleShortVersionString 2>/dev/null)"
pkgutil --pkgs 2>/dev/null | grep -i fuse
} 2>&1 | tee "results/client-$stamp.txt"
if ! command -v sshfs >/dev/null; then
print "\nNo sshfs on this Mac. Install fuse-t (no kernel extension), then re-run this script:"
print " brew install macos-fuse-t/homebrew-cask/fuse-t macos-fuse-t/homebrew-cask/fuse-t-sshfs"
exit 2
fi
# Candidate backends: "" = whatever this sshfs mounts with by default.
candidates=("")
if pkgutil --pkgs 2>/dev/null | grep -qi fuse-t || [[ -e /usr/local/lib/libfuse-t.dylib ]]; then
candidates+=("backend=smb" "backend=fskit")
fi
[[ -d /Library/Filesystems/macfuse.fs ]] && candidates+=("backend=fskit")
candidates=("${(@u)candidates}") # dedupe, keeping the empty "default" entry
working=()
for c in "${candidates[@]}"; do
label=${c:-default}; label=${label#backend=}
o="ro,follow_symlinks${c:+,$c}"
if sshfs "$SRV:albok-browse-test/wing/.browse" "$MNT" -o "$o" 2>>"results/client-$stamp.txt" \
&& ls "$MNT/stash" >/dev/null 2>&1; then
print "backend $label: mounts"; working+=("$c")
else
print "backend $label: does not mount here (skipped)"
fi
unmount
done
(( ${#working} )) || { print "No backend mounted; send results/client-$stamp.txt to infra-ops."; exit 3; }
for c in "${working[@]}"; do
label=${c:-default}; label=${label#backend=}
print "\n== trials, backend $label (N=$N per condition; ~3 min)"
ALBOK_TEST_SRV=$SRV ALBOK_TEST_KEY= ALBOK_TEST_KNOWN_HOSTS= ALBOK_TEST_MNT=$MNT \
ALBOK_TEST_EXTRA_OPTS=$c python3 run-trials.py manifest.json $N > "results/trials-$label-$stamp.json"
python3 summarize.py "results/trials-$label-$stamp.json"
done
# Smoke test of the REAL views as vh (read-only; prints counts, never content).
first=${working[1]}
print "\n== real wings as vh" | tee "results/smoke-$stamp.txt"
for w in fleet/memory fleet/stash library/fiction library/nonfiction personal/notes personal/agent-feedback; do
if sshfs "$SRV:/srv/albok/store/$w/.browse" "$MNT" -o "ro,follow_symlinks${first:+,$first}" 2>/dev/null; then
n=$(find "$MNT" -type f 2>/dev/null | wc -l | tr -d ' ')
f=$(find "$MNT" -type f 2>/dev/null | head -1)
b=$([[ -n $f ]] && head -c 4096 "$f" | wc -c | tr -d ' ' || echo -)
print "$w: mount ok, files=$n, first-file bytes read=$b"
else
print "$w: MOUNT FAILED"
fi | tee -a "results/smoke-$stamp.txt"
unmount
done
scp -q results/*-$stamp.* "$SRV:albok-browse-test/results/" && print "\nResults copied to nh3-albok; tell infra-ops it is done."
@@ -34,11 +34,16 @@ SRV = os.environ.get("ALBOK_TEST_SRV", "infra-ops@10.100.50.44")
KEY = os.environ.get("ALBOK_TEST_KEY", "/keys/id")
KH = os.environ.get("ALBOK_TEST_KNOWN_HOSTS", "/keys/known_hosts")
MNT = os.environ.get("ALBOK_TEST_MNT", "/mnt/browse")
SSH_OPTS = ["-i", KEY, "-o", "BatchMode=yes", "-o", f"UserKnownHostsFile={KH}",
"-o", "StrictHostKeyChecking=yes", "-o", "ControlMaster=auto",
"-o", "ControlPath=/tmp/cm-%r@%h", "-o", "ControlPersist=900"]
SSHFS_BASE = ["ro", "follow_symlinks", f"IdentityFile={KEY}", f"UserKnownHostsFile={KH}",
"StrictHostKeyChecking=yes", "BatchMode=yes"]
# An empty KEY / KNOWN_HOSTS means "use the client's own ssh config" (a workstation run).
_id = (["-i", KEY] if KEY else []) + (["-o", f"UserKnownHostsFile={KH}"] if KH else [])
SSH_OPTS = _id + ["-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=yes",
"-o", "ControlMaster=auto", "-o", "ControlPath=/tmp/cm-%r@%h",
"-o", "ControlPersist=900"]
SSHFS_BASE = (["ro", "follow_symlinks"] + ([f"IdentityFile={KEY}"] if KEY else [])
+ ([f"UserKnownHostsFile={KH}"] if KH else [])
+ ["StrictHostKeyChecking=yes", "BatchMode=yes"]
# e.g. backend=fskit on macOS: the FUSE backend under test
+ [o for o in os.environ.get("ALBOK_TEST_EXTRA_OPTS", "").split(",") if o])
AGGRESSIVE = ["kernel_cache", "entry_timeout=3600", "attr_timeout=3600",
"dir_cache=yes", "dcache_timeout=3600"]
CONDITIONS = [("null", [], None), ("default", [], "ok"),
@@ -157,7 +162,8 @@ def trial(cond, extra, rename, k, slot):
finally:
if fd_b is not None:
os.close(fd_b)
for um in (["fusermount3", "-u", MNT], ["fusermount", "-u", MNT], ["umount", MNT]):
for um in (["fusermount3", "-u", MNT], ["fusermount", "-u", MNT], ["umount", MNT],
["diskutil", "unmount", "force", MNT]):
try:
if subprocess.run(um, capture_output=True).returncode == 0:
break
@@ -18,9 +18,11 @@ fetching a reference copy.
import hashlib
import json
import os
import sys
import uuid
ROOT = "/home/infra-ops/albok-browse-test/wing"
# Optional argv[1] places the replica elsewhere (python3 - <root> < setup-replica.py).
ROOT = sys.argv[1] if len(sys.argv) > 1 else "/home/infra-ops/albok-browse-test/wing"
COUNT = 60
SIZE = 1 << 20 # 1 MiB: larger than FUSE readahead, so a late read must reach the server
@@ -0,0 +1,41 @@
#!/usr/bin/env python3
"""Print one line per condition from a run-trials.py result file, and the gate verdict.
python3 summarize.py results-*.json
"""
import json
import sys
from collections import Counter
def c_outcome(t):
c = t.get("post_open_C", {})
if not c.get("opened"):
return f"open {c.get('errno')}"
if "read_errno" in c:
return f"opened, read {c['read_errno']}"
return "opened, bytes ok" if c.get("content_ok") else "opened, BYTES WRONG"
for path in sys.argv[1:]:
d = json.load(open(path))
print(f"== {path} sshfs -o {','.join(o for o in d['sshfs_base'] if not o.startswith(('IdentityFile', 'UserKnownHosts')))}")
by = {}
for t in d["trials"]:
by.setdefault(t["cond"], []).append(t)
verdict = []
for cond, ts in by.items():
aborted = [t["aborted"] for t in ts if "aborted" in t]
ok = [t for t in ts if "aborted" not in t]
oc = Counter(c_outcome(t) for t in ok)
held = Counter("ok" if t["post_held_B"].get("content_ok") else str(t["post_held_B"]) for t in ok)
lst = Counter(str(t["post_list"].get("n", t["post_list"].get("errno"))) for t in ok)
print(f" {cond:14} n={len(ok)} aborted={len(aborted)} open C: {dict(oc)} held B: {dict(held)} "
f"cached list: {dict(lst)}" + (f" abort: {aborted[0][:80]}" if aborted else ""))
if cond in ("default", "aggressive") and ok:
denied = sum(1 for t in ok if not t["post_open_C"].get("opened")
or "read_errno" in t["post_open_C"])
verdict.append(f"{cond}: C denied {denied}/{len(ok)}")
if cond == "null" and ok:
verdict.append(f"null: C served {sum(1 for t in ok if c_outcome(t) == 'opened, bytes ok')}/{len(ok)}")
print(" verdict:", "; ".join(verdict) or "no usable trials")