Paths and the WIT contract
Every interaction with omnifs is a filesystem operation on a path. There is no client library, no query language, no pagination cursor. The path encodes what you want; the host runtime resolves it; the bytes come back. This page explains exactly how that mapping works.
Anatomy of a path
Section titled “Anatomy of a path”Take this path as a worked example:
/github/0xff-ai/omnifs/_issues/_open/2853/title │ │ │ │ │ │ │ │ │ │ │ │ │ └── leaf file │ │ │ │ │ └─────── object identifier │ │ │ │ └──────────── filter (synthetic view) │ │ │ └──────────────────── collection (synthetic view) │ │ └──────────────────────────── repo │ └──────────────────────────────────── owner └──────────────────────────────────────────── provider mountEach segment has a fixed role:
| Segment | Role | Example |
|---|---|---|
| Provider mount | First segment; names the WASM provider loaded by the host | github |
| Owner | Account or organization scoping the resource | 0xff-ai |
| Repo | Repository within that owner | omnifs |
| Collection | Synthetic directory grouping a kind of object | _issues |
| Filter | Synthetic directory narrowing the collection | _open |
| Object identifier | The specific object within the filtered set | 2853 |
| Leaf file | A single readable field of that object | title |
Not every path has all seven segments. ls /github/0xff-ai/omnifs resolves at the repo level and returns the top-level synthetic directories. ls /github/0xff-ai/omnifs/_issues/_open goes two levels deeper and returns issue identifiers.
Synthetic directories
Section titled “Synthetic directories”Segments that begin with an underscore are synthetic directories. They do not correspond to objects that exist upstream; they are views the provider constructs.
_issues, _prs, and _actions are collections: they group objects of the same kind under one path prefix. _open, _all, and _running are filters: they narrow a collection to a subset defined by the provider’s logic. runs inside _actions is a sub-collection keyed by run ID.
The underscore convention signals this distinction. A path segment without an underscore is usually a real upstream identifier: an owner, a repo name, an issue number, a container name. A segment with a leading underscore is a view that the provider synthesizes from upstream data.
/github/{owner}/{repo}/_issues/_open/2853/title ↑ ↑ ↑ collection filter leaf (synthetic) (synthetic) (real field)
/docker/containers/_running ↑ filter (synthetic: only running containers)
/docker/containers/by-name/{name}/state ↑ ↑ sub-tree leaf (real field)More path examples across the five live providers:
# GitHubls /github/0xff-ai/omnifs/_issues/_opencat /github/0xff-ai/omnifs/_issues/_open/2853/bodycat /github/0xff-ai/omnifs/_prs/_open/17/diffcat /github/0xff-ai/omnifs/_actions/runs/14209871234/status
# Dockerls /docker/containers/_runningcat /docker/containers/by-name/postgres/statecat /docker/containers/by-name/postgres/inspect.json
# Linearls /linear/teams/ENG/issues/_opencat /linear/teams/ENG/issues/_open/ENG-1421/statecat /linear/teams/ENG/issues/_open/ENG-1421/priority
# arXivls /arxiv/papers/1706.03762cat /arxiv/papers/1706.03762/metadata.json
# DNSls /dns/example.comcat /dns/example.com/MXcat /dns/@8.8.8.8/example.com/AHow shell commands map to the three read ops
Section titled “How shell commands map to the three read ops”The host runtime exposes a FUSE mount. Every shell operation on that mount resolves to exactly one of three read operations defined in the WIT interface: lookup-child, list-children, or read-file.
| Shell operation | Read op invoked | What it does |
|---|---|---|
ls /path/to/dir | list-children | Returns the names of entries directly under this path |
cat /path/to/file | read-file | Returns the byte content of a leaf file |
| Traversal through intermediate segments | lookup-child | Verifies a named child exists at this path segment |
When you run cat /github/0xff-ai/omnifs/_issues/_open/2853/title, the host walks the path segment by segment. Each intermediate segment (github, 0xff-ai, omnifs, _issues, _open, 2853) resolves via lookup-child. The final segment (title) resolves via read-file.
When you run ls /github/0xff-ai/omnifs/_issues/_open, the host resolves each segment up to _open via lookup-child, then calls list-children on the resulting node to enumerate the issue identifiers beneath it.
The provider never sees the full path string. The host resolves the provider from the first segment, then feeds it one operation at a time as it descends. The provider only needs to answer three questions: “does this child exist here?”, “what children are here?”, and “what are the bytes at this leaf?”
The WIT interface
Section titled “The WIT interface”The entire provider contract fits in one WIT interface. The package is omnifs:provider@1.0.0.
package omnifs:provider@1.0.0;
interface provider { /// Verify that a named child exists under the given node. /// Returns the child's node handle, or none if it does not exist. lookup-child: func(node: node-id, name: string) -> option<node-id>;
/// Enumerate the direct children of a directory node. /// Returns a list of (name, node-id) pairs. list-children: func(node: node-id) -> result<list<dir-entry>, provider-error>;
/// Read the byte content of a leaf file node. read-file: func(node: node-id, offset: u64, length: u64) -> result<list<u8>, provider-error>;}
world provider-world { export provider;}Three exported functions. No ambient I/O; the host grants the provider a capability handle for its upstream service at instantiation time. A provider that does not call out to the network (for example, a provider over a local socket or a static dataset) does not need any capability at all.
Why three ops, not one
Section titled “Why three ops, not one”A single “resolve path” RPC would force the provider to re-parse and re-validate the full path on every request. Three separate ops let the host drive traversal incrementally. The host can short-circuit on a cache hit at any intermediate node, stopping the descent without calling the provider at all. It can also cache individual nodes (not just full paths), so a second cat on a different leaf of the same issue reuses the already-resolved intermediate nodes.
The three ops also map cleanly onto POSIX filesystem semantics: lookup-child is lookup(2), list-children is readdir(2), and read-file is read(2). This alignment is what makes every existing tool work without modification. grep, find, diff, tail, rsync all reduce to the same three ops at the provider boundary.
What the host adds
Section titled “What the host adds”The host does more than route ops to the provider:
- Cache. A capacity-bounded in-memory cache holds resolved nodes and file contents. Invalidation is event-driven (upstream webhooks or polling), not TTL-based. A node does not expire on a timer; it expires when the upstream signals a change.
- Stable inodes. Object identity is stable across upstream renames. If a GitHub repo is renamed, the inode for its subtree does not change. Tools that track files by inode (editors, watchers) remain consistent.
- Credential isolation. Credentials live in the host’s credential store, not in the provider binary. The provider receives a scoped capability handle; it cannot read the raw token. A compromised provider WASM module cannot exfiltrate credentials it was never given.
Putting it together
Section titled “Putting it together”The path grammar and the three-op interface are two views of the same model. The path is how humans and tools express what they want. The three ops are how the host and provider negotiate the answer. Everything between a cat in your shell and bytes printed to stdout passes through exactly this interface, no matter which of the five live providers serves the request.
cat /github/0xff-ai/omnifs/_issues/_open/2853/title │ └── FUSE → host runtime │ ├── lookup-child(root, "github") → github node ├── lookup-child(github, "0xff-ai") → owner node ├── lookup-child(owner, "omnifs") → repo node ├── lookup-child(repo, "_issues") → issues node ├── lookup-child(issues, "_open") → open-filter node ├── lookup-child(open, "2853") → issue node └── read-file(issue, "title", 0, ...) → "Auth tokens expire mid-session\n"Each lookup-child call may return immediately from cache. Only the final read-file (and any cache-miss lookup-child calls) reach the provider WASM component, and from there, one HTTPS request upstream.