Reference-path correctness¶
A mounted AWC workspace is a real Linux filesystem, not a simulation. A build or an agent that walks the tree sees a directory served by the kernel. The choice is deliberate, and it is the reason standard tools behave correctly inside a workspace (spec §6.9, §14.1).
The kernel resolves paths¶
AWC mounts a workspace as a kernel OverlayFS. The lower layer is the shared,
read-only FUSE filesystem at /var/lib/awc/basefs. The upper layer is a
private writable directory. The merged view is what the agent sees (spec
§8.4, §11.1, §11.3):
Path resolution is the kernel's job, not the daemon's. The Linux VFS walks a
path one component at a time. For each component it sends a single lookup
to the filesystem that owns the parent. It handles ., .., trailing
slashes, and symlink-following rules itself. AWC does not reimplement any of
that.
This matters because path resolution is full of edge cases. Symlink loops,
.. across mount boundaries, O_NOFOLLOW, and magic links all have defined
Linux semantics. A custom virtual filesystem that took a whole path string
and resolved it itself would have to reimplement every rule, and would get
some of them wrong. Routing through the VFS inherits the kernel's
implementation, which tools already depend on.
The lower layer is read-only and content-addressed¶
Every snapshot lives at a digest-qualified path (spec §8.4):
Two properties follow from that address.
The content cannot change. A snapshot is an immutable Merkle tree (spec §6.1, §9.7). A read is strongly snapshot-consistent: every path resolves against one root digest, and the bytes cannot shift after publication (spec §14.1). The daemon returns generous entry and attribute TTLs to the kernel, because the answer is fixed for the life of the mount (spec §15.7).
The daemon answers one component at a time. The FUSE lookup takes a parent
inode and a single name. It never parses a path string, and it rejects a name
that contains / or NUL. To resolve a name inside a snapshot, it walks the
immutable TREE manifests (spec §10.1).
Symlinks are data, not directions: the stored target is returned by
readlink, and the kernel decides whether to follow it (spec §9.6, §10.1).
Writes are isolated to the upper layer¶
The basefs rejects every mutation. The FUSE layer replies EROFS to write,
create, mkdir, unlink, rename, setattr, setxattr, and every other
mutator. open refuses anything that is not O_RDONLY. Reported permission
bits have the write bits stripped, so a stat agrees with the read-only truth.
The spec requires exactly this:
unsupported writes on the base filesystem MUST return EROFS, and all
mutations occur in the private upper through OverlayFS (spec §8.4, §11.1).
The overlay does the write work. Copy-up, whiteouts, and directory opacity are OverlayFS semantics, interpreted by the checkpoint builder (spec §11.7). A tool that writes a file is talking to a normal writable filesystem. It does not know that some bytes came from an immutable snapshot underneath.
The caches must agree with this path¶
Spec §6.9 defines the invariant. Every optimized read path — the page cache, the manifest cache, prefetch, singleflight — MUST be observationally equivalent to resolving the path from the immutable snapshot manifests and fetching the verified objects.
The design makes the invariant easier to keep. The kernel drives resolution,
and the daemon answers each lookup from the same manifests a cold,
unoptimized read would walk. A cache that served a different answer for the
same path would be visible to any tool that re-stat the file. There is no
second, private resolution path to drift out of agreement. For how those
caches are layered, see overlay, cache, and
singleflight.
Safe traversal on ingest¶
Path safety runs in the other direction too, when content is ingested.
Import, checkpoint, and export open files relative to directory descriptors,
not by concatenating paths. They prefer openat2 with RESOLVE_BENEATH and
RESOLVE_NO_MAGICLINKS, and fall back to openat with O_NOFOLLOW where
the kernel lacks openat2 (spec §8.6, §15.10). Symlinks are reported, never
followed (spec §9.6). Containment is
never left to the caller: where openat2 exists the kernel enforces the
authorized root, and on the openat fallback the crate rechecks each
component itself.
Where the evidence stops¶
The kernel-VFS resolution just described is a local property of one host. The spec also requires that a read-only mount of the same snapshot on different hosts resolves to identical logical bytes (spec §14.1). That cross-host equivalence is specified, not yet measured. The AWC pilot is a single host, and the recorded micro-measurements are loopback-only.
The read-only basefs is exposed to root and to the awcd mount owner
through FUSE AllowRoot. That grant exists so the kernel OverlayFS can
resolve its lowerdir with the
credentials it needs. Without it the overlay mount fails with EACCES. The
grant does not widen write access, because the mutators still reply EROFS.
Spec §6.9 is a property the optimized paths must maintain, not a claim the code can prove on its own. It is checked by the digest-correctness and tool-compatibility gates, which compare mounted behaviour against the verified objects (spec §20.2).