//! The harness file tree served over 9P2000: a unified, read-only,
//! fresh-from-disk view of every AI-agent harness's state on the machine.
//!
//! /pid /uptime daemon facts, one line each
//! /claude/ projects/ (mirror of ~/.claude/projects),
//! history (~/.claude/history.jsonl),
//! skills/ (mirror of ~/.claude/skills)
//! /codex/ sessions/ (~/.codex/sessions),
//! session-index (~/.codex/session_index.jsonl),
//! history (~/.codex/history.jsonl)
//! /omp/ mirror of ~/.omp/agent, raw blobs
//! /hermes/ mirror of ~/.hermes, raw
//! /dsh/ mirror of ~/.dsh
//! /skills/{claude,codex,omp}/ the same skills trees the harnesses own
//!
//! Everything below the named mount points is a lazy mirror: a lookup,
//! getattr or readdir walks the real filesystem at request time, so a
//! session transcript grows as its harness writes it and a new session
//! appears as soon as its file lands. No cache, no invalidation.
//!
//! Security boundary — the exclusion rule is absolute and unit-tested:
//! no name that looks like a credential, key, token or auth store is ever
//! answered, at any depth (`excluded`); symlinks are never served (they
//! are an escape hatch around the pinned roots). Every path is resolved
//! from its pinned root one component at a time with `O_NOFOLLOW`
//! (`openIn`), so no name below a root — swapped mid-session or not —
//! can point the daemon at a file outside it. The daemon must never
//! become a credential reader for anything that mounts it. Writes,
//! creates and setattrs answer EPERM.
//!
//! This module is the backend of `cloud9.fs.Server` (main.zig hands it to
//! `serve.Runner`). It declares `features = .{ .references = true }`: every
//! lookup result is a reference, so table entries below (the mirrored
//! files) are refcounted and freed when the last fid lets go. Node ids
//! pack (kind, root index, serial) in a u64, zmx-style: a mirrored file's
//! serial is its table slot and generation, so two walks of the same file
//! answer the same qid path while it is held.
const std = @import("std");
const cloud9 = @import("cloud9");
const fs = cloud9.fs;
const E = fs.E;
const Io = std.Io;
const linux = std.os.linux;
// ---- comptime bounds ---------------------------------------------------------
/// Longest relative path a mirrored file may carry (bytes below a root).
pub const rel_capacity: usize = 640;
/// Mirrored files remembered at once; each holds one reference per fid.
pub const path_capacity: usize = 4096;
comptime {
if (path_capacity > 1 << 12) @compileError("path table slots must fit the 12 serial bits");
}
/// Largest frame the daemon negotiates; read and readdir staging buffers
/// are this big, so a single Rread can carry one full frame of bytes.
pub const msize: u32 = 32 * 1024;
/// Names one directory listing may stage (sorted for cursor stability).
pub const list_capacity: usize = 1024;
/// Bytes of name storage one listing may use (255 per name, packed).
pub const list_name_bytes: usize = 128 * 1024;
/// Longest base path a pinned root may carry.
pub const base_capacity: usize = 512;
pub const opts: fs.Options = .{
.fid_capacity = 512,
.slot_capacity = 8,
.name_capacity = 255,
.username_capacity = 28,
.fid_index = true,
};
// ---- the tree skeleton ------------------------------------------------------
pub const Root = enum(u8) { claude, codex, omp, hermes, dsh };
/// Static nodes: the facts, the harness dirs and the union skills dir.
pub const Top = enum(u8) {
root = 1,
pid,
uptime,
claude,
codex,
omp,
hermes,
dsh,
skills,
/// Directory entries of the root, in listing order.
pub const listed = [_]Top{ .pid, .uptime, .claude, .codex, .omp, .hermes, .dsh, .skills };
pub fn fileName(t: Top) []const u8 {
return @tagName(t);
}
pub fn dir(t: Top) bool {
return switch (t) {
.root, .claude, .codex, .omp, .hermes, .dsh, .skills => true,
.pid, .uptime => false,
};
}
fn parent(t: Top) Top {
_ = t;
return .root; // the root is its own parent; every top hangs off it
}
/// The mirror a harness dir serves: its root and the relative path
/// below it. The claude and codex dirs are *virtual* (their children
/// are named mounts); omp, hermes and dsh mirror one subtree each.
fn mirror(t: Top) ?Mount {
return switch (t) {
.omp => .{ .root = .omp, .rel = "agent" },
.hermes => .{ .root = .hermes, .rel = "" },
.dsh => .{ .root = .dsh, .rel = "" },
else => null,
};
}
};
/// Where a mirrored subtree sits: `rel` below the root's pinned base path.
pub const Mount = struct {
root: Root,
rel: []const u8,
};
const NamedMount = struct {
/// The name as it appears in its virtual directory.
name: []const u8,
owner: Top,
mount: Mount,
};
/// The children of the virtual directories (claude, codex, skills). The
/// same target may appear twice (/claude/skills and /skills/claude): a
/// lookup of either hands out the same table entry, so both paths share
/// qid paths and identity.
const named_mounts = [_]NamedMount{
.{ .name = "projects", .owner = .claude, .mount = .{ .root = .claude, .rel = "projects" } },
.{ .name = "history", .owner = .claude, .mount = .{ .root = .claude, .rel = "history.jsonl" } },
.{ .name = "skills", .owner = .claude, .mount = .{ .root = .claude, .rel = "skills" } },
.{ .name = "sessions", .owner = .codex, .mount = .{ .root = .codex, .rel = "sessions" } },
.{ .name = "session-index", .owner = .codex, .mount = .{ .root = .codex, .rel = "session_index.jsonl" } },
.{ .name = "history", .owner = .codex, .mount = .{ .root = .codex, .rel = "history.jsonl" } },
.{ .name = "claude", .owner = .skills, .mount = .{ .root = .claude, .rel = "skills" } },
.{ .name = "codex", .owner = .skills, .mount = .{ .root = .codex, .rel = "skills" } },
.{ .name = "omp", .owner = .skills, .mount = .{ .root = .omp, .rel = "skills" } },
};
fn mountNamed(owner: Top, name: []const u8) ?Mount {
for (named_mounts) |m| {
if (m.owner == owner and std.mem.eql(u8, m.name, name)) return m.mount;
}
return null;
}
/// The Top that owns the mount whose target is exactly (root, rel) — the
/// canonical parent a ".." walk answers. Falls back to the root's harness
/// dir for targets no named mount covers (deep paths, mirrors).
fn mountOwner(r: Root, rel_path: []const u8) Top {
for (named_mounts) |m| {
if (m.mount.root == r and std.mem.eql(u8, m.mount.rel, rel_path)) return m.owner;
}
return switch (r) {
.claude => .claude,
.codex => .codex,
.omp => .omp,
.hermes => .hermes,
.dsh => .dsh,
};
}
// ---- node ids and the path table ---------------------------------------------
pub const Kind = enum(u8) { top = 0, path = 1 };
pub const Node = packed struct(u64) {
/// A Top index (kind == .top) or a Root index (kind == .path).
idx: u8 = 0,
kind: u8 = 0,
serial: u48 = 0,
};
pub fn topNode(t: Top) u64 {
return @bitCast(Node{ .idx = @intFromEnum(t), .kind = @intFromEnum(Kind.top) });
}
pub const root: u64 = topNode(Top.root);
/// One remembered mirrored file: its root, its relative path, the hash
/// that short-circuits dedupe lookups, and its reference count (one per
/// fid holding it, handed out by lookup, paid back by release).
const Entry = struct {
used: bool = false,
root: Root = .claude,
rel_buf: [rel_capacity]u8 = undefined,
rel_len: u16 = 0,
hash: u64 = 0,
refs: u32 = 0,
gen: u32 = 0,
fn rel(e: *const Entry) []const u8 {
return e.rel_buf[0..e.rel_len];
}
};
fn serialOf(slot: u12, gen: u32) u48 {
return (@as(u48, gen) << 12) | slot;
}
fn nodeOf(e: *const Entry, slot: u12) u64 {
return @bitCast(Node{
.idx = @intFromEnum(e.root),
.kind = @intFromEnum(Kind.path),
.serial = serialOf(slot, e.gen),
});
}
// ---- the exclusions (the security boundary) -----------------------------------
fn containsFold(name: []const u8, needle: []const u8) bool {
if (name.len < needle.len) return false;
var i: usize = 0;
while (i + needle.len <= name.len) : (i += 1) {
if (std.ascii.eqlIgnoreCase(name[i .. i + needle.len], needle)) return true;
}
return false;
}
fn endsWithFold(name: []const u8, suffix: []const u8) bool {
return name.len >= suffix.len and std.ascii.eqlIgnoreCase(name[name.len - suffix.len ..], suffix);
}
fn isOneOf(name: []const u8, comptime names: []const []const u8) bool {
inline for (names) |n| if (std.ascii.eqlIgnoreCase(name, n)) return true;
return false;
}
/// Absolute rule: a name that may hold a credential, key, token or auth
/// material is never served, at any depth, by lookup or readdir. The
/// substrings are folded (case-insensitive) and deliberately broad —
/// "auth" also hides "author-notes", the price of never guessing wrong.
/// When unsure, exclude and document (docs/DESIGN.md).
pub fn excluded(name: []const u8) bool {
@setEvalBranchQuota(20000);
if (name.len == 0) return true;
for ([_][]const u8{ "credentials", "token", "auth", "secret" }) |needle| {
if (containsFold(name, needle)) return true;
}
if (endsWithFold(name, ".key")) return true;
if (endsWithFold(name, ".pem")) return true;
if (endsWithFold(name, ".env")) return true;
// Config and settings files of the other harnesses may embed API keys
// (hermes config.yaml, omp config.yml, dsh settings.yaml). Claude
// Code's settings.json is not in the tree at all: /claude serves only
// projects/, skills/ and history.jsonl.
if (isOneOf(name, &.{ "settings.json", "settings.yaml", "settings.local.json", "config.yml", "config.yaml", "config.toml", "models.yml", "models.yaml", ".ssh" })) return true;
for ([_][]const u8{ "id_rsa", "id_dsa", "id_ecdsa", "id_ed25519" }) |prefix| {
if (name.len >= prefix.len and std.ascii.eqlIgnoreCase(name[0..prefix.len], prefix)) return true;
}
return false;
}
/// Every component of a relative path must pass `excluded`; used on the
/// comptime mount targets and the unit-test fixtures alike.
pub fn excludedPath(rel_path: []const u8) bool {
@setEvalBranchQuota(4000);
var it = std.mem.splitScalar(u8, rel_path, '/');
while (it.next()) |comp| {
if (excluded(comp)) return true;
}
return false;
}
// ---- the backend --------------------------------------------------------------
pub const Answer = struct {
reply: fs.Reply,
bytes: []const u8 = "",
};
fn fail(tag: u64, e: u16) Answer {
return .{ .reply = fs.Reply.fail(tag, e) };
}
/// The daemon's state. One instance serves every connection; `handle` is
/// called with `mutex` held (the serve handler in main.zig holds it across
/// handle and reply, because `bytes` point into the shared buffers).
pub const Harness = struct {
pub const Req = fs.Req;
pub const Reply = fs.Reply;
pub const features: fs.Features = .{ .references = true };
io: Io,
pid: u32 = 0,
started_sec: i64 = 0,
/// The five pinned roots; a root without a base is unreachable.
base_buf: [5][base_capacity]u8 = @splat(@splat(0)),
base_len: [5]u16 = @splat(0),
base_set: [5]bool = @splat(false),
table: [path_capacity]Entry = @splat(.{}),
mutex: Io.Mutex = .init,
// Shared staging (guarded by `mutex`).
data_buf: [msize]u8 = undefined,
stage_buf: [msize]u8 = undefined,
list_names: [list_name_bytes]u8 = undefined,
list_dirs: [list_capacity]bool = undefined,
list_offs: [list_capacity]u32 = undefined,
pub const InitOptions = struct {
io: Io,
pid: u32,
/// Base path of each root, or "" when the root is not pinned.
bases: [5][]const u8,
};
pub fn init(h: *Harness, o: InitOptions) void {
h.* = .{ .io = o.io, .pid = o.pid, .started_sec = Io.Timestamp.now(o.io, .real).toSeconds() };
inline for (0..5) |i| {
const path = o.bases[i];
h.base_set[i] = path.len > 0 and path.len <= base_capacity;
if (h.base_set[i]) {
@memcpy(h.base_buf[i][0..path.len], path);
h.base_len[i] = @intCast(path.len);
}
}
}
pub fn base(h: *const Harness, r: Root) ?[]const u8 {
if (!h.base_set[@intFromEnum(r)]) return null;
return h.base_buf[@intFromEnum(r)][0..h.base_len[@intFromEnum(r)]];
}
// -- path table --
fn hashOf(r: Root, rel_path: []const u8) u64 {
var hh = std.hash.Wyhash.init(0x6861_726e_6573_7300); // "harness"
hh.update(&.{@intFromEnum(r)});
hh.update(rel_path);
return hh.final();
}
/// Finds or creates the entry for (root, rel). `ref` says whether the
/// caller hands out a reference (a lookup) or only names the file (a
/// readdir record). Two walks of the same file find the same entry,
/// so their node ids are equal; a freed entry's generation changes.
fn entryFor(h: *Harness, r: Root, rel_path: []const u8, ref: bool) ?*Entry {
if (rel_path.len == 0 or rel_path.len > rel_capacity) return null;
const hash = hashOf(r, rel_path);
var free: ?*Entry = null;
for (&h.table) |*e| {
if (!e.used) {
if (free == null) free = e;
continue;
}
if (e.hash == hash and e.root == r and e.rel_len == rel_path.len and
std.mem.eql(u8, e.rel(), rel_path))
{
if (ref) e.refs += 1;
return e;
}
}
const e = free orelse return null;
e.used = true;
e.root = r;
e.rel_len = @intCast(rel_path.len);
@memcpy(e.rel_buf[0..rel_path.len], rel_path);
e.hash = hash;
e.refs = @intFromBool(ref);
e.gen +%= 1;
return e;
}
fn slotOf(h: *Harness, e: *Entry) u12 {
const off = (@intFromPtr(e) - @intFromPtr(&h.table)) / @sizeOf(Entry);
return @intCast(off);
}
/// The target a node names; a freed or forged serial answers null.
fn resolve(h: *Harness, node: u64) ?Target {
const n: Node = @bitCast(node);
switch (std.enums.fromInt(Kind, n.kind) orelse return null) {
.top => return .{ .top = std.enums.fromInt(Top, n.idx) orelse return null },
.path => {
const r = std.enums.fromInt(Root, n.idx) orelse return null;
const slot: u12 = @truncate(n.serial);
const e = &h.table[slot];
if (!e.used or e.root != r) return null;
if (serialOf(slot, e.gen) != n.serial) return null;
return .{ .path = e };
},
}
}
fn entryNode(h: *Harness, e: *Entry) u64 {
return nodeOf(e, h.slotOf(e));
}
};
pub const Target = union(enum) {
top: Top,
path: *Entry,
};
// ---- attributes ---------------------------------------------------------------
/// Opens `/` without following a symlink at any step
/// below the base: every component is opened with `O_NOFOLLOW`, so a
/// name swapped for a symlink between the walk and the read fails
/// instead of reaching out of the root. Composing the whole path and
/// opening it in one call cannot do that — only the last component's
/// symlink is refused then, and every directory above it is followed
/// wherever it points. The base itself is configuration (`--root` or
/// $HOME), not client bytes, so it resolves normally. The caller owns
/// the descriptor.
fn openIn(h: *Harness, r: Root, rel_path: []const u8, final: linux.O) ?i32 {
const b = h.base(r) orelse return null;
if (rel_path.len > rel_capacity or b.len > base_capacity) return null;
const walk: linux.O = .{ .PATH = true, .DIRECTORY = true, .CLOEXEC = true, .NOFOLLOW = true };
var base_z: [base_capacity + 1]u8 = @splat(0);
@memcpy(base_z[0..b.len], b);
var top_flags = if (rel_path.len == 0) final else walk;
top_flags.NOFOLLOW = false; // the pinned root may legitimately be a link
top_flags.CLOEXEC = true;
var fd = fdOf(linux.open(@ptrCast(&base_z), top_flags, 0)) orelse return null;
if (rel_path.len == 0) return fd;
var rest = rel_path;
while (true) {
const slash = std.mem.indexOfScalar(u8, rest, '/');
const comp = if (slash) |i| rest[0..i] else rest;
const last = slash == null;
// Nothing below composes these; a component that could re-enter
// the walk is refused rather than opened.
if (comp.len == 0 or comp.len > 255 or
std.mem.eql(u8, comp, ".") or std.mem.eql(u8, comp, ".."))
{
_ = linux.close(fd);
return null;
}
var name_z: [256]u8 = @splat(0);
@memcpy(name_z[0..comp.len], comp);
var flags = if (last) final else walk;
flags.NOFOLLOW = true;
flags.CLOEXEC = true;
const next = fdOf(linux.openat(fd, @ptrCast(&name_z), flags, 0));
_ = linux.close(fd);
fd = next orelse return null;
if (last) return fd;
rest = rest[comp.len + 1 ..];
}
}
fn fdOf(rc: usize) ?i32 {
if (linux.errno(rc) != .SUCCESS) return null;
return @intCast(rc);
}
/// Stat of `/`, resolved the no-follow way. A symlink
/// is never served: it is the one escape hatch around the pinned roots.
/// `O_PATH` opens the name itself, so a fifo or device never blocks and
/// never has its open side effects run.
fn statIn(h: *Harness, r: Root, rel_path: []const u8) ?Io.Dir.Stat {
const fd = openIn(h, r, rel_path, .{ .PATH = true, .CLOEXEC = true }) orelse return null;
const f: Io.File = .{ .handle = fd, .flags = .{ .nonblocking = false } };
defer f.close(h.io);
const st = f.stat(h.io) catch return null;
if (st.kind == .sym_link) return null; // never served: an escape hatch
return st;
}
fn statAttr(h: *Harness, e: *Entry) ?fs.Attr {
const st = statIn(h, e.root, e.rel()) orelse return null;
return .{
.name = basename(e.rel()),
.node = h.entryNode(e),
.dir = st.kind == .directory,
.size = if (st.kind == .directory) 0 else st.size,
.mode = if (st.kind == .directory) 0o555 else 0o444,
.mtime = @truncate(@as(u64, @bitCast(st.mtime.toSeconds()))),
};
}
fn basename(rel_path: []const u8) []const u8 {
if (std.mem.lastIndexOfScalar(u8, rel_path, '/')) |i| return rel_path[i + 1 ..];
return rel_path;
}
/// A fact's text, rendered on demand. `buf` should be a small stack buffer.
fn factText(h: *Harness, fact: Top, buf: []u8) []const u8 {
var w = Io.Writer.fixed(buf);
switch (fact) {
.pid => w.print("{d}\n", .{h.pid}) catch {},
.uptime => {
const now = Io.Timestamp.now(h.io, .real).toSeconds();
const up: u64 = if (now > h.started_sec) @intCast(now - h.started_sec) else 0;
w.print("{d}\n", .{up}) catch {};
},
else => {},
}
return w.buffered();
}
var fact_buf: [64]u8 = undefined;
fn attrFor(h: *Harness, t: Target) ?fs.Attr {
switch (t) {
.top => |top| {
switch (top) {
.root => return .{ .name = "/", .node = root, .dir = true, .mode = 0o555 },
.pid, .uptime => return .{
.name = top.fileName(),
.node = topNode(top),
.size = factText(h, top, &fact_buf).len,
.mode = 0o444,
},
.claude, .codex, .skills => return .{
.name = top.fileName(),
.node = topNode(top),
.dir = true,
.mode = 0o555,
},
.omp, .hermes, .dsh => {
// The harness dir IS its mirror: stat the target.
const m = top.mirror().?;
const st = statIn(h, m.root, m.rel) orelse return null;
if (st.kind != .directory) return null;
return .{ .name = top.fileName(), .node = topNode(top), .dir = true, .mode = 0o555 };
},
}
},
.path => |e| return statAttr(h, e),
}
}
/// Attr of the mount target as a table entry (fresh from disk, or null
/// when the target is missing, a symlink, or excluded by name).
fn attrOfMount(h: *Harness, r: Root, rel_path: []const u8) ?fs.Attr {
const e = h.entryFor(r, rel_path, false) orelse return null;
defer if (e.refs == 0) {
e.used = false;
e.gen +%= 1;
};
return statAttr(h, e);
}
// ---- dispatch ------------------------------------------------------------------
/// Answers one engine request. The caller holds `h.mutex` and replies
/// before letting go of it (answer bytes point into `h`).
pub fn handle(h: *Harness, req: fs.Req) Answer {
const t = h.resolve(req.node) orelse return fail(req.tag, E.NOENT);
return switch (req.op) {
.lookup => lookup(h, req, t),
.getattr => attrReply(h, req.tag, t),
.setattr => fail(req.tag, E.PERM),
.open => open(h, req, t),
.release => release(h, req, t),
.readdir => readdir(h, req, t),
.read => read(h, req, t),
.write => fail(req.tag, E.PERM),
};
}
fn attrReply(h: *Harness, tag: u64, t: Target) Answer {
const a = attrFor(h, t) orelse return fail(tag, E.NOENT);
return .{ .reply = .{ .tag = tag, .attr = a } };
}
/// A lookup's attr names the file as the client asked for it (the engine
/// keeps that name for the fid); refs for path targets were already taken
/// by `entryFor`.
fn lookupAttr(h: *Harness, req: fs.Req, t: Target, name: []const u8) Answer {
const a = attrFor(h, t) orelse return fail(req.tag, E.NOENT);
var with_name = a;
with_name.name = name;
return .{ .reply = .{ .tag = req.tag, .attr = with_name } };
}
fn lookup(h: *Harness, req: fs.Req, t: Target) Answer {
const name = req.data;
if (name.len == 0 or name.len > 255) return fail(req.tag, E.NOENT);
if (std.mem.indexOfAny(u8, name, "/\x00") != null) return fail(req.tag, E.NOENT);
if (std.mem.eql(u8, name, ".")) {
// The engine asks for "." when cloning a fid; the reference is
// paid for path targets here like any other lookup result.
switch (t) {
.top => return attrReply(h, req.tag, t),
.path => |e| {
e.refs += 1;
return lookupAttr(h, req, t, name);
},
}
}
if (std.mem.eql(u8, name, "..")) return lookupParent(h, req, t);
switch (t) {
.top => |top| switch (top) {
.root => {
const found: ?Top = blk: for (Top.listed) |e| {
if (std.mem.eql(u8, e.fileName(), name)) break :blk e;
} else break :blk null;
return attrReply(h, req.tag, .{ .top = found orelse return fail(req.tag, E.NOENT) });
},
.claude, .codex, .skills => {
const m = mountNamed(top, name) orelse return fail(req.tag, E.NOENT);
const e = h.entryFor(m.root, m.rel, true) orelse return fail(req.tag, E.NFILE);
const a = statAttr(h, e) orelse {
unref(e);
return fail(req.tag, E.NOENT);
};
var with_name = a;
with_name.name = name;
return .{ .reply = .{ .tag = req.tag, .attr = with_name } };
},
.omp, .hermes, .dsh => {
const m = top.mirror().?;
return lookupBelow(h, req, m.root, m.rel, name);
},
.pid, .uptime => return fail(req.tag, E.NOTDIR),
},
.path => |e| {
const rel_path = e.rel();
if (attrFor(h, t)) |a| {
if (!a.dir) return fail(req.tag, E.NOTDIR);
} else return fail(req.tag, E.NOENT);
return lookupBelow(h, req, e.root, rel_path, name);
},
}
}
/// A lookup of `name` in the directory (root, dir_rel): the child's rel
/// path is composed only from the remembered pair and the (single-segment,
/// engine-vetted) name.
fn lookupBelow(h: *Harness, req: fs.Req, r: Root, dir_rel: []const u8, name: []const u8) Answer {
if (excluded(name)) return fail(req.tag, E.NOENT);
var scratch: [rel_capacity + 256]u8 = undefined;
const child_rel = joinRel(&scratch, dir_rel, name) orelse return fail(req.tag, E.NOENT);
const e = h.entryFor(r, child_rel, true) orelse return fail(req.tag, E.NFILE);
const a = statAttr(h, e) orelse {
unref(e);
return fail(req.tag, E.NOENT);
};
var with_name = a;
with_name.name = name;
return .{ .reply = .{ .tag = req.tag, .attr = with_name } };
}
fn joinRel(scratch: []u8, dir_rel: []const u8, name: []const u8) ?[]const u8 {
if (dir_rel.len == 0) {
if (name.len > scratch.len) return null;
@memcpy(scratch[0..name.len], name);
return scratch[0..name.len];
}
const total = dir_rel.len + 1 + name.len;
if (total > scratch.len) return null;
@memcpy(scratch[0..dir_rel.len], dir_rel);
scratch[dir_rel.len] = '/';
@memcpy(scratch[dir_rel.len + 1 .. total], name);
return scratch[0..total];
}
fn lookupParent(h: *Harness, req: fs.Req, t: Target) Answer {
const parent: Target = switch (t) {
.top => |top| .{ .top = top.parent() },
.path => |e| blk: {
const rel_path = e.rel();
if (std.mem.lastIndexOfScalar(u8, rel_path, '/')) |i| {
const up = rel_path[0..i];
const pe = h.entryFor(e.root, up, true) orelse return fail(req.tag, E.NFILE);
break :blk .{ .path = pe };
}
break :blk .{ .top = mountOwner(e.root, rel_path) };
},
};
return attrReply(h, req.tag, parent);
}
fn unref(e: *Entry) void {
if (e.refs > 0) e.refs -= 1;
if (e.refs == 0) {
e.used = false;
e.gen +%= 1;
}
}
fn open(h: *Harness, req: fs.Req, t: Target) Answer {
_ = attrFor(h, t) orelse return fail(req.tag, E.NOENT); // still there?
// Read-only tree: any open that would write or truncate is refused.
const rw = req.omode & 3;
if (rw == cloud9.owrite or rw == cloud9.ordwr) return fail(req.tag, E.PERM);
if (req.omode & cloud9.otrunc != 0) return fail(req.tag, E.PERM);
return .{ .reply = .{ .tag = req.tag, .handle = 1 } };
}
fn release(h: *Harness, req: fs.Req, t: Target) Answer {
_ = h;
switch (t) {
.top => {},
.path => |e| unref(e),
}
return .{ .reply = .{ .tag = req.tag } };
}
// ---- reads ----------------------------------------------------------------------
fn read(h: *Harness, req: fs.Req, t: Target) Answer {
switch (t) {
.top => |top| switch (top) {
.pid, .uptime => {
const text = factText(h, top, &fact_buf);
return window(req, text);
},
else => return fail(req.tag, E.ISDIR),
},
.path => |e| {
// `O_NONBLOCK` so a fifo left in a harness root cannot park the
// daemon in `open`; the kind check below refuses it anyway.
const fd = openIn(h, e.root, e.rel(), .{
.ACCMODE = .RDONLY,
.NONBLOCK = true,
.CLOEXEC = true,
}) orelse return fail(req.tag, E.NOENT);
const file: Io.File = .{ .handle = fd, .flags = .{ .nonblocking = false } };
defer file.close(h.io);
const st = file.stat(h.io) catch return fail(req.tag, E.IO);
if (st.kind == .directory) return fail(req.tag, E.ISDIR);
// Only regular files have bytes this tree promises to serve.
if (st.kind != .file) return fail(req.tag, E.PERM);
_ = linux.fcntl(fd, linux.F.SETFL, 0); // pread wants no O_NONBLOCK
const want = @min(req.size, h.data_buf.len);
const n = file.readPositionalAll(h.io, h.data_buf[0..want], req.off) catch
return fail(req.tag, E.IO);
return .{ .reply = .{ .tag = req.tag }, .bytes = h.data_buf[0..n] };
},
}
}
/// `text` windowed by the request's offset and size.
fn window(req: fs.Req, text: []const u8) Answer {
const off: usize = @intCast(@min(req.off, text.len));
const n = @min(text.len - off, req.size);
return .{ .reply = .{ .tag = req.tag }, .bytes = text[off..][0..n] };
}
// ---- readdir --------------------------------------------------------------------
/// Directory records in the engine's shape: `node:u64le dir:u8 len:u8 name`.
const Staging = struct {
buf: []u8,
len: usize = 0,
skip: u64,
/// Set once a record did not fit. Everything after it is left for the
/// next read: dropping one record and staging a shorter one behind it
/// would lose that entry, because the client's next offset counts the
/// records it received.
full: bool = false,
fn add(s: *Staging, node: u64, dir: bool, name: []const u8) void {
if (s.full) return;
if (s.skip > 0) {
s.skip -= 1;
return;
}
if (name.len == 0 or name.len > 255) return;
if (s.len + 10 + name.len > s.buf.len) {
s.full = true;
return;
}
std.mem.writeInt(u64, s.buf[s.len..][0..8], node, .little);
s.buf[s.len + 8] = @intFromBool(dir);
s.buf[s.len + 9] = @intCast(name.len);
@memcpy(s.buf[s.len + 10 ..][0..name.len], name);
s.len += 10 + name.len;
}
};
fn readdir(h: *Harness, req: fs.Req, t: Target) Answer {
var st: Staging = .{ .buf = &h.stage_buf, .skip = req.off };
switch (t) {
.top => |top| switch (top) {
.root => for (Top.listed) |e| st.add(topNode(e), e.dir(), e.fileName()),
.claude, .codex, .skills => {
for (named_mounts) |m| {
if (m.owner != top) continue;
// Only mounts whose target exists are listed (a harness
// without skills simply has no skills/ entry).
const a = attrOfMount(h, m.mount.root, m.mount.rel) orelse continue;
const e = h.entryFor(m.mount.root, m.mount.rel, false) orelse continue;
st.add(h.entryNode(e), a.dir, m.name);
}
},
.pid, .uptime => return fail(req.tag, E.NOTDIR),
.omp, .hermes, .dsh => {
const m = top.mirror().?;
if (h.base(m.root) == null) return fail(req.tag, E.NOENT);
switch (listDir(h, &st, m.root, m.rel)) {
.ok => {},
.gone => return fail(req.tag, E.NOENT),
.failed => return fail(req.tag, E.IO),
.overflow => return fail(req.tag, E.NFILE),
}
},
},
.path => |e| {
if (attrFor(h, t)) |a| {
if (!a.dir) return fail(req.tag, E.NOTDIR);
} else return fail(req.tag, E.NOENT);
switch (listDir(h, &st, e.root, e.rel())) {
.ok => {},
.gone => return fail(req.tag, E.NOENT),
.failed => return fail(req.tag, E.IO),
.overflow => return fail(req.tag, E.NFILE),
}
},
}
return .{ .reply = .{ .tag = req.tag }, .bytes = h.stage_buf[0..st.len] };
}
/// How a listing attempt ended. `overflow` is deliberate: a comptime cap
/// that would silently drop entries answers an error instead, because a
/// short listing is indistinguishable from a small directory.
const Listing = enum { ok, gone, failed, overflow };
/// Stages the contents of the directory (root, dir_rel): every name the
/// walker yields that survives the exclusions, sorted so a listing that
/// spans several reads stays consistent.
fn listDir(h: *Harness, st: *Staging, r: Root, dir_rel: []const u8) Listing {
const fd = openIn(h, r, dir_rel, .{
.ACCMODE = .RDONLY,
.DIRECTORY = true,
.CLOEXEC = true,
}) orelse return .gone;
const dir: Io.Dir = .{ .handle = fd };
defer Io.Dir.close(dir, h.io);
var read_buf: [Io.Dir.Iterator.reader_buffer_len]u8 align(@alignOf(usize)) = undefined;
var reader = Io.Dir.Reader.init(dir, &read_buf);
var names_len: usize = 0;
var count: usize = 0;
while (true) {
const entry = (reader.next(h.io) catch return .failed) orelse break;
if (excluded(entry.name)) continue;
var kind = entry.kind;
if (kind == .unknown or kind == .sym_link) {
// The walker's word is not proof: stat without following.
var child_buf: [rel_capacity + 256]u8 = undefined;
const child_rel = joinRel(&child_buf, dir_rel, entry.name) orelse continue;
const cst = statIn(h, r, child_rel) orelse continue;
kind = cst.kind;
}
if (kind == .sym_link) continue; // never served
// A cap reached is an error, never a short listing: a directory
// that quietly loses entries is a wrong answer, and a caller
// cannot tell it from a small directory.
if (count == list_capacity or names_len + entry.name.len > h.list_names.len) return .overflow;
@memcpy(h.list_names[names_len..][0..entry.name.len], entry.name);
h.list_offs[count] = @intCast(names_len);
h.list_dirs[count] = kind == .directory;
names_len += entry.name.len;
count += 1;
}
// Sort the (offset, length) pairs by name for cursor stability.
const SortCtx = struct {
names: []const u8,
offs: []const u32,
lens: [list_capacity]u32,
fn lessThan(ctx: @This(), a: usize, b: usize) bool {
return std.mem.order(u8, ctx.nameAt(a), ctx.nameAt(b)) == .lt;
}
fn nameAt(ctx: @This(), i: usize) []const u8 {
const start = ctx.offs[i];
return ctx.names[start..][0..ctx.lens[i]];
}
};
var lens: [list_capacity]u32 = @splat(0);
var order: [list_capacity]usize = @splat(0);
{
var end: usize = 0;
for (0..count) |i| {
end = if (i + 1 < count) h.list_offs[i + 1] else names_len;
lens[i] = @intCast(end - h.list_offs[i]);
order[i] = i;
}
}
const ctx: SortCtx = .{ .names = h.list_names[0..names_len], .offs = &h.list_offs, .lens = lens };
std.mem.sort(usize, order[0..count], ctx, SortCtx.lessThan);
for (order[0..count]) |i| {
const name = ctx.nameAt(i);
var child_buf: [rel_capacity + 256]u8 = undefined;
const child_rel = joinRel(&child_buf, dir_rel, name) orelse continue;
const e = h.entryFor(r, child_rel, false) orelse return .overflow; // path table full
st.add(h.entryNode(e), h.list_dirs[i], name);
}
return .ok;
}
// ---- unit tests -------------------------------------------------------------------
const testing = std.testing;
/// A rig with a fake HOME: every root under one temp dir, never the real
/// ~/.claude or any other live harness root.
const Rig = struct {
dir: testing.TmpDir,
path_buf: [std.fs.max_path_bytes]u8 = undefined,
home: []const u8 = undefined,
h: *Harness = undefined,
harness_mem: Harness = undefined,
fn start(rig: *Rig) !void {
const io = testing.io;
rig.dir = testing.tmpDir(.{});
errdefer rig.dir.cleanup();
const len = try rig.dir.dir.realPath(io, &rig.path_buf);
rig.home = try testing.allocator.dupe(u8, rig.path_buf[0..len]);
var mk: [std.fs.max_path_bytes]u8 = undefined;
// The five roots, pinned to the fake home.
inline for ([_][]const u8{
".claude/projects/p1", ".claude/skills/revu",
".codex/sessions/2026/09/21",
".omp/agent", ".hermes/logs",
".dsh/profiles",
}) |sub| {
try Io.Dir.cwd().createDirPath(io, try std.fmt.bufPrint(&mk, "{s}/{s}", .{ rig.home, sub }));
}
var base_buf: [5][std.fs.max_path_bytes]u8 = @splat(@splat(0));
var bases: [5][]const u8 = @splat("");
inline for (0..5) |i| {
bases[i] = try std.fmt.bufPrint(&base_buf[i], "{s}/{s}", .{ rig.home, home_dirs[i] });
}
rig.harness_mem = undefined;
rig.harness_mem.init(.{ .io = io, .pid = 4242, .bases = bases });
rig.h = &rig.harness_mem;
}
fn end(rig: *Rig) void {
rig.dir.cleanup();
testing.allocator.free(rig.home);
}
fn put(rig: *Rig, rel_path: []const u8, bytes: []const u8) !void {
var buf: [std.fs.max_path_bytes]u8 = undefined;
const path = try std.fmt.bufPrint(&buf, "{s}/{s}", .{ rig.home, rel_path });
if (std.mem.lastIndexOfScalar(u8, rel_path, '/')) |i| {
var dbuf: [std.fs.max_path_bytes]u8 = undefined;
const dir_path = try std.fmt.bufPrint(&dbuf, "{s}/{s}", .{ rig.home, rel_path[0..i] });
try Io.Dir.cwd().createDirPath(testing.io, dir_path);
}
var file = try Io.Dir.createFileAbsolute(testing.io, path, .{});
defer file.close(testing.io);
try file.writeStreamingAll(testing.io, bytes);
}
fn del(rig: *Rig, rel_path: []const u8) !void {
var buf: [std.fs.max_path_bytes]u8 = undefined;
const path = try std.fmt.bufPrint(&buf, "{s}/{s}", .{ rig.home, rel_path });
try Io.Dir.deleteFileAbsolute(testing.io, path);
}
/// lookup of `name` in `dir_node`, answering the child's attr.
fn lookupName(rig: *Rig, tag: u64, dir_node: u64, name: []const u8) Answer {
return handle(rig.h, .{ .tag = tag, .op = .lookup, .node = dir_node, .data = name });
}
};
const home_dirs = [5][]const u8{ ".claude", ".codex", ".omp", ".hermes", ".dsh" };
fn expectNoent(a: Answer) !void {
try testing.expect(a.reply.status == .err);
try testing.expectEqual(E.NOENT, a.reply.errno);
}
fn expectEperm(a: Answer) !void {
try testing.expect(a.reply.status == .err);
try testing.expectEqual(E.PERM, a.reply.errno);
}
test "tree: facts at the root" {
var rig: Rig = .{ .dir = undefined };
try rig.start();
defer rig.end();
const a = rig.lookupName(1, root, "pid");
try testing.expect(a.reply.status == .ok);
try testing.expect(a.reply.attr.dir == false);
var read_a = handle(rig.h, .{ .tag = 2, .op = .read, .node = a.reply.attr.node, .off = 0, .size = 64 });
try testing.expectEqualStrings("4242\n", read_a.bytes);
read_a = handle(rig.h, .{ .tag = 3, .op = .read, .node = a.reply.attr.node, .off = 0, .size = 2 });
try testing.expectEqualStrings("42", read_a.bytes);
const up = rig.lookupName(4, root, "uptime");
try testing.expect(up.reply.status == .ok);
const up_read = handle(rig.h, .{ .tag = 5, .op = .read, .node = up.reply.attr.node, .off = 0, .size = 64 });
try testing.expect(up_read.bytes.len > 0 and up_read.bytes[up_read.bytes.len - 1] == '\n');
// The root lists the facts and the five harness dirs.
const listing = handle(rig.h, .{ .tag = 6, .op = .readdir, .node = root, .off = 0, .size = msize });
try testing.expect(listing.reply.status == .ok);
for ([_][]const u8{ "pid", "uptime", "claude", "codex", "omp", "hermes", "dsh", "skills" }) |name| {
try testing.expect(stageHas(listing.bytes, name));
}
}
fn stageHas(staged: []const u8, name: []const u8) bool {
var i: usize = 0;
while (i + 10 <= staged.len) {
const len: usize = staged[i + 9];
const entry = staged[i + 10 ..][0..len];
if (std.mem.eql(u8, entry, name)) return true;
i += 10 + len;
}
return false;
}
test "tree: exclusions never serve a credentials-shaped name" {
var rig: Rig = .{ .dir = undefined };
try rig.start();
defer rig.end();
// The fixture: a real transcript and a pile of credentials-shaped names
// at several depths, including inside the mirrored subtrees.
try rig.put(".claude/projects/p1/session-x.jsonl", "{\"a\":1}\n");
try rig.put(".claude/projects/p1/.credentials.json", "STAY OUT\n");
try rig.put(".claude/projects/p1/auth.json", "STAY OUT\n");
try rig.put(".claude/projects/p1/settings.json", "STAY OUT\n");
try rig.put(".claude/projects/p1/token.txt", "STAY OUT\n");
try rig.put(".claude/projects/p1/api.key", "STAY OUT\n");
try rig.put(".claude/projects/p1/models.yml", "STAY OUT\n");
try rig.put(".claude/projects/p1/deep/.env", "STAY OUT\n");
try rig.put(".claude/history.jsonl", "{\"h\":1}\n");
try rig.put(".hermes/auth.json", "STAY OUT\n");
try rig.put(".hermes/.env", "STAY OUT\n");
try rig.put(".hermes/config.yaml", "STAY OUT\n");
try rig.put(".hermes/logs/app.log", "log line\n");
try rig.put(".dsh/.credentials.yaml", "STAY OUT\n");
try rig.put(".dsh/settings.yaml", "STAY OUT\n");
// /claude lists only its mounts; the credentials at ~/.claude root are
// not in the tree at all (no mount serves them).
const claude = handle(rig.h, .{ .tag = 1, .op = .readdir, .node = topNode(.claude), .off = 0, .size = msize });
try testing.expect(claude.reply.status == .ok);
for ([_][]const u8{ "projects", "history", "skills" }) |name| try testing.expect(stageHas(claude.bytes, name));
try testing.expect(!stageHas(claude.bytes, "credentials"));
// A project's listing shows the transcript and nothing else.
const projects = rig.lookupName(2, topNode(.claude), "projects");
try testing.expect(projects.reply.status == .ok);
const p1 = rig.lookupName(3, projects.reply.attr.node, "p1");
try testing.expect(p1.reply.status == .ok);
const listed = handle(rig.h, .{ .tag = 4, .op = .readdir, .node = p1.reply.attr.node, .off = 0, .size = msize });
try testing.expect(listed.reply.status == .ok);
try testing.expect(stageHas(listed.bytes, "session-x.jsonl"));
for ([_][]const u8{ ".credentials.json", "auth.json", "settings.json", "token.txt", "api.key", "models.yml" }) |name| {
try testing.expect(!stageHas(listed.bytes, name));
}
// The plain directory that holds a .env is served; the .env is not.
try testing.expect(stageHas(listed.bytes, "deep"));
// Every credentials-shaped name is unreachable by lookup too.
for ([_][]const u8{ ".credentials.json", "auth.json", "settings.json", "token.txt", "api.key", "models.yml" }) |name| {
try expectNoent(rig.lookupName(5, p1.reply.attr.node, name));
}
const deep = rig.lookupName(6, p1.reply.attr.node, "deep");
try testing.expect(deep.reply.status == .ok);
try expectNoent(rig.lookupName(7, deep.reply.attr.node, ".env"));
// The fully mirrored roots hide theirs as well.
const hermes = rig.lookupName(8, root, "hermes");
try testing.expect(hermes.reply.status == .ok);
const hlist = handle(rig.h, .{ .tag = 9, .op = .readdir, .node = hermes.reply.attr.node, .off = 0, .size = msize });
try testing.expect(stageHas(hlist.bytes, "logs"));
for ([_][]const u8{ "auth.json", ".env", "config.yaml" }) |name| {
try testing.expect(!stageHas(hlist.bytes, name));
try expectNoent(rig.lookupName(10, hermes.reply.attr.node, name));
}
const dsh = rig.lookupName(11, root, "dsh");
try testing.expect(dsh.reply.status == .ok);
const dlist = handle(rig.h, .{ .tag = 12, .op = .readdir, .node = dsh.reply.attr.node, .off = 0, .size = msize });
try testing.expect(!stageHas(dlist.bytes, ".credentials.yaml"));
try testing.expect(!stageHas(dlist.bytes, "settings.yaml"));
try testing.expect(stageHas(dlist.bytes, "profiles"));
// The exclusion predicate itself, spelled out.
try testing.expect(excluded(".credentials.json"));
try testing.expect(excluded("AUTH.JSON"));
try testing.expect(excluded("session-token.bin"));
try testing.expect(excluded("id_rsa_backup"));
try testing.expect(excluded("prod.pem"));
try testing.expect(excluded("config.yaml"));
try testing.expect(!excluded("session-x.jsonl"));
try testing.expect(!excluded("SKILL.md"));
try testing.expect(!excluded("logs"));
}
test "tree: paths compose only from the pinned roots" {
var rig: Rig = .{ .dir = undefined };
try rig.start();
defer rig.end();
try rig.put(".claude/projects/p1/session-x.jsonl", "{}\n");
// A slash, a NUL or an overlong name never reaches the filesystem.
try expectNoent(rig.lookupName(1, topNode(.claude), "projects/../p1"));
try expectNoent(rig.lookupName(2, topNode(.claude), "projects/\x00"));
// A symlink inside a mirror is not served, whatever it points at.
var buf: [std.fs.max_path_bytes]u8 = undefined;
const link = try std.fmt.bufPrintZ(&buf, "{s}/.claude/projects/p1/escape", .{rig.home});
try Io.Dir.cwd().symLink(testing.io, rig.home, link, .{});
const projects = rig.lookupName(3, topNode(.claude), "projects");
const p1 = rig.lookupName(4, projects.reply.attr.node, "p1");
try expectNoent(rig.lookupName(5, p1.reply.attr.node, "escape"));
// ".." from a mirrored file lands on its canonical parent, and walking
// ".." repeatedly terminates at the root.
const session = rig.lookupName(6, p1.reply.attr.node, "session-x.jsonl");
try testing.expect(session.reply.status == .ok);
var cur = session.reply.attr.node;
var hops: usize = 0;
while (hops < 8) : (hops += 1) {
const up = rig.lookupName(7, cur, "..");
try testing.expect(up.reply.status == .ok);
if (up.reply.attr.node == root) break;
cur = up.reply.attr.node;
}
try testing.expect(cur == root or hops < 8);
// The facts refuse lookups with NOTDIR.
try testing.expect(rig.lookupName(8, topNode(.pid), "x").reply.errno == E.NOTDIR);
}
test "tree: node ids — same file equal, distinct files differ, ids recycle" {
var rig: Rig = .{ .dir = undefined };
try rig.start();
defer rig.end();
try rig.put(".claude/projects/p1/session-x.jsonl", "{}\n");
try rig.put(".claude/projects/p1/session-y.jsonl", "{}\n");
const projects = rig.lookupName(1, topNode(.claude), "projects");
const a1 = rig.lookupName(2, projects.reply.attr.node, "p1");
// Two walks of the same file: the same node id (the dedupe table).
const x1 = rig.lookupName(3, a1.reply.attr.node, "session-x.jsonl");
const x2 = rig.lookupName(4, a1.reply.attr.node, "session-x.jsonl");
try testing.expect(x1.reply.status == .ok);
try testing.expectEqual(x1.reply.attr.node, x2.reply.attr.node);
const y1 = rig.lookupName(5, a1.reply.attr.node, "session-y.jsonl");
try testing.expect(y1.reply.attr.node != x1.reply.attr.node);
// The union skills view and the harness's own skills share identity.
const via_harness = rig.lookupName(6, topNode(.claude), "skills");
const via_union = rig.lookupName(7, topNode(.skills), "claude");
try testing.expectEqual(via_harness.reply.attr.node, via_union.reply.attr.node);
// References are paid back by release: both slots go, ids recycle.
for ([_]u64{ x1.reply.attr.node, x2.reply.attr.node, y1.reply.attr.node, a1.reply.attr.node }) |n| {
const rel = handle(rig.h, .{ .tag = 8, .op = .release, .node = n });
try testing.expect(rel.reply.status == .ok);
}
const x3 = rig.lookupName(9, projects.reply.attr.node, "p1");
const x4 = rig.lookupName(10, x3.reply.attr.node, "session-x.jsonl");
// The entry was freed and re-created: a fresh generation, a new id.
try testing.expect(x4.reply.attr.node != x1.reply.attr.node);
// Node packing round-trips through the struct.
const n: Node = .{ .idx = 3, .kind = 1, .serial = 0x1234_5678_9abc };
const bits: u64 = @bitCast(n);
const back: Node = @bitCast(bits);
try testing.expect(back.idx == 3 and back.kind == 1 and back.serial == 0x1234_5678_9abc);
}
test "tree: a file that vanishes between lookup and read answers ENOENT" {
var rig: Rig = .{ .dir = undefined };
try rig.start();
defer rig.end();
try rig.put(".claude/projects/p1/session-x.jsonl", "{\"a\":1}\n");
const projects = rig.lookupName(1, topNode(.claude), "projects");
const p1 = rig.lookupName(2, projects.reply.attr.node, "p1");
const session = rig.lookupName(3, p1.reply.attr.node, "session-x.jsonl");
try testing.expect(session.reply.status == .ok);
// The transcript reads back whole, fresh from disk.
const whole = handle(rig.h, .{ .tag = 4, .op = .read, .node = session.reply.attr.node, .off = 0, .size = 1024 });
try testing.expectEqualStrings("{\"a\":1}\n", whole.bytes);
// It vanishes: lookup, getattr and read all answer ENOENT cleanly.
try rig.del(".claude/projects/p1/session-x.jsonl");
try expectNoent(rig.lookupName(5, p1.reply.attr.node, "session-x.jsonl"));
const gone = handle(rig.h, .{ .tag = 6, .op = .getattr, .node = session.reply.attr.node });
try expectNoent(gone);
const read_gone = handle(rig.h, .{ .tag = 7, .op = .read, .node = session.reply.attr.node, .off = 0, .size = 64 });
try expectNoent(read_gone);
}
test "tree: writes and setattrs answer EPERM" {
var rig: Rig = .{ .dir = undefined };
try rig.start();
defer rig.end();
try rig.put(".claude/projects/p1/session-x.jsonl", "{}\n");
const projects = rig.lookupName(1, topNode(.claude), "projects");
const p1 = rig.lookupName(2, projects.reply.attr.node, "p1");
const session = rig.lookupName(3, p1.reply.attr.node, "session-x.jsonl");
try testing.expect(session.reply.status == .ok);
const write = handle(rig.h, .{ .tag = 4, .op = .write, .node = session.reply.attr.node, .data = "x" });
try expectEperm(write);
const set = handle(rig.h, .{ .tag = 5, .op = .setattr, .node = session.reply.attr.node, .set = .{ .mtime = true }, .mtime = 1 });
try expectEperm(set);
// An open for write is refused at open time.
const wopen = handle(rig.h, .{ .tag = 6, .op = .open, .node = session.reply.attr.node, .omode = cloud9.owrite });
try expectEperm(wopen);
const ropen = handle(rig.h, .{ .tag = 7, .op = .open, .node = session.reply.attr.node, .omode = cloud9.oread });
try testing.expect(ropen.reply.status == .ok);
}
test "tree: a transcript written while serving is visible at once" {
var rig: Rig = .{ .dir = undefined };
try rig.start();
defer rig.end();
try rig.put(".claude/projects/p1/session-x.jsonl", "{\"n\":1}\n");
const projects = rig.lookupName(1, topNode(.claude), "projects");
const p1 = rig.lookupName(2, projects.reply.attr.node, "p1");
// The harness appends; the next read sees it — no cache in between.
try rig.put(".claude/projects/p1/session-x.jsonl", "{\"n\":1}\n{\"n\":2}\n");
const session = rig.lookupName(3, p1.reply.attr.node, "session-x.jsonl");
const fresh = handle(rig.h, .{ .tag = 4, .op = .read, .node = session.reply.attr.node, .off = 0, .size = 1024 });
try testing.expectEqualStrings("{\"n\":1}\n{\"n\":2}\n", fresh.bytes);
// A brand-new session file appears on the next listing.
try rig.put(".claude/projects/p1/session-new.jsonl", "{\"n\":9}\n");
const listed = handle(rig.h, .{ .tag = 5, .op = .readdir, .node = p1.reply.attr.node, .off = 0, .size = msize });
try testing.expect(stageHas(listed.bytes, "session-new.jsonl"));
}
comptime {
// Every static mount target must survive the exclusion rules; the
// daemon's own skeleton may never be filtered out from under it.
for (named_mounts) |m| {
if (excludedPath(m.mount.rel)) @compileError("a 9harness mount target is excluded by name");
}
}
fn stageCount(staged: []const u8) usize {
var i: usize = 0;
var n: usize = 0;
while (i + 10 <= staged.len) : (n += 1) i += 10 + @as(usize, staged[i + 9]);
return n;
}
test "tree: a file directly under a mirror root reads back" {
// Regression: joining a child onto an empty relative path returned an
// uncopied scratch slice, so every file at the top of a mirror root
// (/hermes/, /dsh/) listed but resolved to garbage bytes.
var rig: Rig = .{ .dir = undefined };
try rig.start();
defer rig.end();
try rig.put(".hermes/note.txt", "hello-hermes\n");
const listing = handle(rig.h, .{ .tag = 1, .op = .readdir, .node = topNode(.hermes), .off = 0, .size = msize });
try testing.expect(stageHas(listing.bytes, "note.txt"));
const note = rig.lookupName(2, topNode(.hermes), "note.txt");
try testing.expect(note.reply.status == .ok);
const bytes = handle(rig.h, .{ .tag = 3, .op = .read, .node = note.reply.attr.node, .off = 0, .size = 64 });
try testing.expectEqualStrings("hello-hermes\n", bytes.bytes);
}
test "tree: a name swapped for a symlink under an open handle serves nothing" {
// Regression: the read path composed the whole path and opened it in
// one call, following symlinks. A name replaced between the walk and
// the read handed the client bytes from outside every pinned root.
var rig: Rig = .{ .dir = undefined };
try rig.start();
defer rig.end();
try rig.put("outside.txt", "OUTSIDE-THE-ROOTS\n"); // beside the roots, not in one
try rig.put(".claude/projects/p1/swap.txt", "safe\n");
const projects = rig.lookupName(1, topNode(.claude), "projects");
const p1 = rig.lookupName(2, projects.reply.attr.node, "p1");
const swap = rig.lookupName(3, p1.reply.attr.node, "swap.txt");
try testing.expect(swap.reply.status == .ok);
const opened = handle(rig.h, .{ .tag = 4, .op = .open, .node = swap.reply.attr.node, .omode = cloud9.oread });
try testing.expect(opened.reply.status == .ok);
// The file becomes a symlink out of the tree while the handle is open.
var link_buf: [std.fs.max_path_bytes]u8 = undefined;
var target_buf: [std.fs.max_path_bytes]u8 = undefined;
const link = try std.fmt.bufPrintZ(&link_buf, "{s}/.claude/projects/p1/swap.txt", .{rig.home});
const target = try std.fmt.bufPrint(&target_buf, "{s}/outside.txt", .{rig.home});
try rig.del(".claude/projects/p1/swap.txt");
try Io.Dir.cwd().symLink(testing.io, target, link, .{});
const after = handle(rig.h, .{ .tag = 5, .op = .read, .node = swap.reply.attr.node, .off = 0, .size = 64 });
try expectNoent(after);
try testing.expectEqual(@as(usize, 0), after.bytes.len);
}
test "tree: a listing past the cap fails loudly instead of truncating" {
// Regression: a directory with more entries (or longer names) than the
// comptime caps was served short, and a short listing is
// indistinguishable from a small directory.
var rig: Rig = .{ .dir = undefined };
try rig.start();
defer rig.end();
var name_buf: [64]u8 = undefined;
for (0..list_capacity + 1) |i| {
try rig.put(try std.fmt.bufPrint(&name_buf, ".dsh/f{d:0>5}", .{i}), "");
}
const listed = handle(rig.h, .{ .tag = 1, .op = .readdir, .node = topNode(.dsh), .off = 0, .size = msize });
try testing.expect(listed.reply.status == .err);
try testing.expectEqual(E.NFILE, listed.reply.errno);
}
test "tree: a listing spanning several reads loses no entry" {
// The staging buffer fills long before a big directory ends. Staging
// must stop at the first record that does not fit: dropping it and
// packing a shorter one behind it would lose that entry, because the
// next read's offset counts the records already delivered.
var rig: Rig = .{ .dir = undefined };
try rig.start();
defer rig.end();
const count = 200;
var name_buf: [320]u8 = undefined;
for (0..count) |i| {
const name = try std.fmt.bufPrint(&name_buf, ".dsh/{d:0>3}{s}", .{ i, "n" ** 200 });
try rig.put(name, "");
}
var seen: [count]bool = @splat(false);
var off: u64 = 0;
var reads: usize = 0;
while (reads < 16) : (reads += 1) {
const page = handle(rig.h, .{ .tag = 1, .op = .readdir, .node = topNode(.dsh), .off = off, .size = msize });
try testing.expect(page.reply.status == .ok);
if (page.bytes.len == 0) break;
var i: usize = 0;
while (i + 10 <= page.bytes.len) {
const len: usize = page.bytes[i + 9];
const name = page.bytes[i + 10 ..][0..len];
i += 10 + len;
// The fixture root also holds `profiles`, created by the rig.
const idx = std.fmt.parseInt(usize, name[0..@min(3, name.len)], 10) catch continue;
try testing.expect(!seen[idx]); // never served twice
seen[idx] = true;
}
off += stageCount(page.bytes);
}
try testing.expect(reads > 1); // the listing really did span several reads
for (seen) |s| try testing.expect(s);
}