Filesystem-native app protocol for shell-first AI agents, powered by AgentFS.
AppFS turns different apps into one filesystem contract so agents can use the same primitives everywhere:
catto read resources>> *.actto trigger actions with JSONLtail -fto watch async event streams
This repository contains the AppFS protocol docs, runtime, reference fixtures, bridge adapters, and conformance tests. AppFS is the app-facing protocol and managed runtime; AgentFS is the storage, overlay, sync, and mount engine underneath.
AppFS is designed for practical LLM + shell workflows:
- one interaction model across many apps instead of one schema per app
- path-native operations with low token overhead
- stream-first async flows with replay support
- managed runtime lifecycle with dynamic app registration
- connector adapters for in-process, HTTP, and gRPC integrations
AppFS is the primary product story in this repository, but it is currently shipped through the AgentFS engine and CLI:
agentfs init ...prepares the underlying AgentFS database and storage layeragentfs appfs up ...starts the managed AppFS runtime on top of that AgentFS-backed filesystem- the
agentfsbinary remains the entrypoint because AppFS currently depends on AgentFS database lifecycle, overlay semantics, sync support, and platform mount backends
In other words: AppFS is the app-facing protocol and UX; AgentFS is the engine that powers it.
The recommended integration entrypoint for real app projects is:
agentfs appfs compose up -f appfs-compose.yamlThe lower-level managed runtime primitive remains:
agentfs appfs up <id-or-path> <mountpoint>Managed runtime state lives in:
/_appfs/apps.registry.json
Low-level debug commands still exist:
agentfs mount ... --managed-appfsagentfs serve appfs --managed
The higher-level AppFS flow is now:
- declare runtime, connectors, and apps in
appfs-compose.yaml - run
agentfs appfs compose up - use the mounted tree directly
The lower-level managed flow is still:
- start a bridge or in-process connector
- initialize an empty AgentFS database that AppFS will use as its storage and mount substrate
- start AppFS with
agentfs appfs up - register an app through
/_appfs/register_app.act - read files, switch scope, and trigger actions through the mounted tree
Today the recommended integration path is compose-first. agentfs appfs up remains the lower-level runtime primitive when you want to debug mount/runtime behavior directly.
A Huoyan attached-case compose example lives at examples/appfs/appfs-compose.huoyan-attached-case.example.yaml.
Minimal compose shape for the reference HTTP bridge:
version: 1
runtime:
db: ./.agentfs/compose-aiim.db
mountpoint: C:/mnt/appfs-compose-aiim
backend: winfsp
init: if_missing
reset: false
fallback_poll_ms: 0
connectors:
aiim-http:
mode: command
transport: http
endpoint: http://127.0.0.1:8080
healthcheck:
kind: connector
interval_ms: 500
timeout_ms: 2000
max_attempts: 40
command:
cwd: ./examples/appfs/bridges/http-python
program: uv
args: ["run", "python", "bridge_server.py"]
apps:
aiim:
connector: aiim-httpRuntime polling is split by responsibility:
.actwrites are normally processed through the mount runtime's write-wake path, so action execution does not require polling.apps.<app-id>.inbound_poll_msenables connector inbound event polling for apps that must pull backend-side events into the mounted tree, such as a chat inbox.runtime.fallback_poll_msis a legacy full-runtime fallback tick. It is disabled by default and should usually stay0. The old compose fieldruntime.poll_msand CLI flag--poll-msare still accepted as compatibility aliases, but new configs should usefallback_poll_ms.
Private, principal-aware apps can be declared through app policy fields. Compose writes the policy/template, but the actual private instances are materialized when a principal is attached or created. For example, a Tinode app can materialize one private tree per AppFS principal and poll backend events into each tree:
apps:
tinode:
connector: tinode-http
visibility: private
path_template: private/{principal_id}/tinode
profile_template: tinode:{principal_id}
credential_policy: auto-create
inbound_poll_ms: 1000That inbound poll keeps the mounted AppFS tree current. It does not replace a higher-level agent wake/subscription mechanism; agents still need to observe or read the relevant files until an explicit wake layer is added. In practice, the current identity and private app set are established by the appfs-agent attach flow, not by AppFS startup alone.
With that file in place:
agentfs appfs compose up -f appfs-compose.yamlCompose is the recommended path when you want one command to:
- prepare or reopen the AgentFS runtime database
- supervise an external or command-launched connector
- bootstrap the managed AppFS registry
- mount the tree and start the runtime in one foreground process
Prerequisites:
- Rust toolchain with
cargo - Python +
uvfor the reference HTTP bridge - port
127.0.0.1:8080available - Windows: WinFsp installed
- Linux: FUSE available
- macOS: NFS mount support available
Install WinFsp first. AppFS uses WinFsp as the Windows mount backend for --backend winfsp.
- Download and install the latest WinFsp release from winfsp.dev/rel
- After installation, open a new terminal before running
agentfs appfs up - A reboot is usually not required, but if Windows reports the driver is busy or mounts still fail after install, reboot once and retry
- For WinFsp, the mountpoint path itself should be absent before mount. Keep the parent directory, such as
C:\mnt, but do not pre-createC:\mnt\appfs-compose-aiim.appfs compose upnow preserves this WinFsp expectation and will clean up an empty stale placeholder directory if an older run left one behind.
Compose-first startup on Windows:
cd C:\Users\esp3j\rep\agentfs\cli
cargo run -- appfs compose up -f ..\examples\appfs\appfs-compose.huoyan-attached-case.example.yamlIf the target software is already inside a case or other attached scope, prefer passing that bootstrap mode through connector env in the compose file, as shown in the Huoyan example. That avoids issuing an extra enter_scope just to land on the working tree.
Start the reference HTTP bridge:
cd C:\Users\esp3j\rep\agentfs\examples\appfs\bridges\http-python
uv run python bridge_server.pyInitialize an empty database:
cd C:\Users\esp3j\rep\agentfs\cli
cargo run -- init managed-http --forceStart AppFS:
cd C:\Users\esp3j\rep\agentfs\cli
cargo run -- appfs up .agentfs\managed-http.db C:\mnt\appfs-managed-http --backend winfspRegister an app:
Add-Content C:\mnt\appfs-managed-http\_appfs\register_app.act '{"app_id":"aiim","transport":{"kind":"http","endpoint":"http://127.0.0.1:8080","http_timeout_ms":5000,"grpc_timeout_ms":5000,"bridge_max_retries":2,"bridge_initial_backoff_ms":100,"bridge_max_backoff_ms":1000,"bridge_circuit_breaker_failures":5,"bridge_circuit_breaker_cooldown_ms":3000},"client_token":"reg-http-001"}'Read a snapshot and trigger an action:
Get-Content C:\mnt\appfs-managed-http\aiim\chats\chat-001\messages.res.jsonl | Select-Object -First 5
Add-Content C:\mnt\appfs-managed-http\aiim\contacts\zhangsan\send_message.act '{"version":2,"client_token":"msg-001","payload":{"text":"hello"}}'Start the reference HTTP bridge:
cd /path/to/agentfs/examples/appfs/bridges/http-python
uv run python bridge_server.pyInitialize an empty database:
cd /path/to/agentfs/cli
cargo run -- init managed-http --forceStart AppFS:
cd /path/to/agentfs/cli
mkdir -p /tmp/appfs-managed-http
cargo run -- appfs up .agentfs/managed-http.db /tmp/appfs-managed-http --backend fuseRegister an app:
echo '{"app_id":"aiim","transport":{"kind":"http","endpoint":"http://127.0.0.1:8080","http_timeout_ms":5000,"grpc_timeout_ms":5000,"bridge_max_retries":2,"bridge_initial_backoff_ms":100,"bridge_max_backoff_ms":1000,"bridge_circuit_breaker_failures":5,"bridge_circuit_breaker_cooldown_ms":3000},"client_token":"reg-http-001"}' >> /tmp/appfs-managed-http/_appfs/register_app.actRead a snapshot and trigger an action:
head -n 5 /tmp/appfs-managed-http/aiim/chats/chat-001/messages.res.jsonl
echo '{"version":2,"client_token":"msg-001","payload":{"text":"hello"}}' >> /tmp/appfs-managed-http/aiim/contacts/zhangsan/send_message.actmacOS uses the AgentFS NFS backend instead of FUSE. The AppFS managed flow is the same, but Linux is still the primary required CI platform.
Initialize an empty database:
cd /path/to/agentfs/cli
cargo run -- init managed-http --forceStart AppFS:
cd /path/to/agentfs/cli
mkdir -p /tmp/appfs-managed-http
cargo run -- appfs up .agentfs/managed-http.db /tmp/appfs-managed-http --backend nfsThen use the same /_appfs/register_app.act, per-app .act, and ordinary file reads shown above.
On a fresh Windows machine, install the Rust toolchain and Visual Studio Build Tools before running cargo build.
If you see:
error: linker `link.exe` not found
the MSVC build environment is not available in the current shell yet.
- Install Rust
winget install --id Rustlang.Rustup -e
rustup default stable-x86_64-pc-windows-msvc- Install Visual Studio Build Tools
# Download the Visual Studio Build Tools installer
Invoke-WebRequest -Uri https://aka.ms/vs/17/release/vs_buildtools.exe -OutFile vs_buildtools.exe
# Install the C++ Build Tools workload (without the full IDE)
Start-Process -Wait -FilePath .\vs_buildtools.exe -ArgumentList "--quiet --wait --norestart --nocache --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"- Install the official LLVM Windows release
Download the latest Windows installer (LLVM-*-win64.exe) from the official LLVM release page:
During installation, enable the option to add LLVM to PATH if the installer offers it. The default install location is usually:
C:\Program Files\LLVM\bin
- Make sure the MSVC and LLVM compiler directories are on
PATH
Open a new terminal, then run:
$MsvcBin = Get-ChildItem "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC" -Directory |
Sort-Object Name -Descending |
Select-Object -First 1 |
ForEach-Object { Join-Path $_.FullName "bin\Hostx64\x64" }
$LlvmBin = "C:\Program Files\LLVM\bin"
$env:Path = "$MsvcBin;$LlvmBin;$env:Path"
where.exe cl
where.exe link
where.exe clang-clOn a typical machine these resolve to paths like:
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\<version>\bin\Hostx64\x64
C:\Program Files\LLVM\bin
If where.exe cl or where.exe link still cannot find anything, check that the MSVC directory above exists first. If it does not, re-run the Build Tools installer.
If where.exe clang-cl still cannot find anything, LLVM is either not installed yet or was not added to PATH. In that case, check whether C:\Program Files\LLVM\bin\clang-cl.exe exists and add C:\Program Files\LLVM\bin to PATH.
For cargo build, the important part is simply that cl.exe, link.exe, and clang-cl.exe are reachable on PATH.
cd cli
cargo build
cargo build --release
cargo test --package agentfscd sdk/rust
cargo testcd sdk/typescript
npm install
npm run build
npm testcd sdk/python
uv sync
uv run pytest
uv run ruff check .
uv run ruff format .Key entry points:
- Documentation Index
- Current AppFS milestone (v4)
- Connectorization milestone (v3)
- Backend-native milestone (v2)
- examples/appfs guide
- cli/TEST-WINDOWS.md
- Runtime closure design plan
AppFS is organized into three layers:
- AgentFS Core: the engine beneath AppFS, including SQLite filesystem, generic overlay behavior, sync, and platform mount backends
- AppFS Engine: registry, structure sync, runtime lifecycle, snapshot read-through, connector adapters, and inbound event polling
- AppFS UX:
agentfs appfs upplus the mounted control plane under/_appfs/*
flowchart TD
A["Shell / PowerShell / bash"] --> B["agentfs appfs up"]
B --> C["AgentFS Core"]
B --> D["AppFS Engine"]
D --> E["/_appfs/apps.registry.json"]
C --> F["Mounted AppFS tree"]
D --> F
D --> G["AppTreeSyncService"]
D --> H["AppConnector"]
G --> H
H --> I["in-process / HTTP / gRPC adapters"]
I --> J["real app backend or demo backend"]
Notes:
AppConnectoris the canonical runtime-facing connector surface_meta/manifest.res.jsonis a derived view, not the runtime source of truth- snapshot cold misses auto-expand on ordinary file reads through the mount path
- action wake, snapshot read-through, and connector inbound polling are separate runtime paths
- the current CLI layering is intentional: AppFS is exposed as an
agentfssubcommand because it still depends on AgentFS storage and mount infrastructure agentfs init --baseremains an AgentFS feature, but it is not part of the recommended AppFS path
cli/: theagentfsCLI, including AppFS subcommands, runtime, and mount integrationsdk/rust/: Rust SDK and filesystem implementationssdk/typescript/: TypeScript SDKsdk/python/: Python SDKsandbox/: Linux-only syscall interception sandboxexamples/appfs/: AppFS examples, split intofixtures/,bridges/,templates/, andlegacy/docs/: ADRs, plans, contracts, and release notes
Core validation paths:
- Rust CLI tests:
cargo test --manifest-path cli/Cargo.toml --package agentfs - Rust SDK tests:
cargo test --manifest-path sdk/rust/Cargo.toml - Linux contract suite:
cli/tests/test-appfs-connector-contract.sh - Windows managed lifecycle regression:
cli/test-windows-appfs-managed.ps1
Linux remains the primary required CI platform. Windows has dedicated manual regression coverage for the managed AppFS flow.
Current repository status:
v0.3connectorization is merged and remains the release baselinev0.4structure sync, unified connector, managed lifecycle, andappfs upare implemented in-tree- multi-app managed runtime is available
- real app pilot work is the next validation milestone before a stable release claim
MIT