Get started ๐¶
Install airfs, declare two layers, and read them as one directory. About five
minutes, ending with a working workspace you can keep.
1. Install ๐ฆ¶
airfs links no C library, so installing is one command and needs no toolchain
beyond Go:
Make sure $(go env GOPATH)/bin is on your PATH, then check it runs:
No Go on this machine?
Every release ships a prebuilt binary for Linux and macOS on amd64 and
arm64. One command picks the right one, checks it against the published
SHA-256, and installs it into ~/.local/bin โ no toolchain, no root:
Two environment variables change what it does:
curl -fsSL https://raw.githubusercontent.com/sylvanld/airfs/main/scripts/get-airfs.sh \
| AIRFS_VERSION=v0.1.0 AIRFS_INSTALL_DIR=/usr/local/bin sh
AIRFS_VERSION installs a specific release tag instead of the latest, and
AIRFS_INSTALL_DIR chooses where the binary lands โ a system-wide directory
needs a sudo you type yourself, since the script never asks for one.
Prefer doing it by hand? The archives and their checksums.txt are on the
releases page; unpack the one
for your platform and install -m 755 airfs ~/.local/bin/airfs.
2. Check the host ๐ฉบ¶
Mounting needs two things an unprivileged process cannot provide for itself:
/dev/fuse, and a setuid fusermount3. Both ship with your distribution's
FUSE package and are already present on a normal desktop Linux.
Now that airfs is installed, ask it โ before installing anything else:
ok /dev/fuse readable and writable by you
ok fusermount3 /usr/bin/fusermount3, setuid
Every mount prerequisite is satisfied.
If something is missing, doctor names the package that provides it and exits
2. Install it with your package manager โ fuse3 on Debian, Ubuntu, Fedora,
and Arch โ then run airfs doctor again.
Why airfs will not install it for you
Installing a system package needs root, and a tool that asks you for root to run a package manager is a tool that should have printed the command instead.
3. Create a workspace ๐๏ธ¶
A workspace is an ordinary directory holding one plain-text file. The default is
~/.ai-resources, which is where agent tooling usually looks:
For this walkthrough, two layers โ a personal one and a project one:
mkdir -p ~/ai/personal/skills/commit ~/ai/personal/skills/review
mkdir -p ~/ai/project/skills/commit
echo "personal commit skill" > ~/ai/personal/skills/commit/SKILL.md
echo "personal review skill" > ~/ai/personal/skills/review/SKILL.md
echo "project commit skill" > ~/ai/project/skills/commit/SKILL.md
In real use these are git working copies you already have โ nothing about a
layer is airfs-specific, and airfs never writes into one.
4. Declare the layers โ๏ธ¶
One path per line, in ~/.ai-resources/sources.txt. The order is the
precedence order: the last line wins. Declare from the most general to the most
specific.
cat > ~/.ai-resources/sources.txt <<'EOF'
# Layers, most general first โ the last declaration wins.
~/ai/personal
~/ai/project
EOF
Now ask airfs what that file means, before mounting anything:
target /home/you/.ai-resources
config /home/you/.ai-resources/sources.txt
Sources, in precedence order โ the last declaration wins:
1. ~/ai/personal agents 0 skills 2 commands 0 scripts 0
2. ~/ai/project agents 0 skills 1 commands 0 scripts 0
Empty kinds: agents, commands, scripts
Shadowed entries โ the winner is what the view serves:
skills/commit wins ~/ai/project over ~/ai/personal
Both layers ship a commit skill, so one has to lose. The report says which, by
name โ the point being that shadowing is never silent. sources reads and
reports only; it mounts nothing.
Where did the empty kinds come from?
airfs creates the missing agents/, commands/, and scripts/
directories inside each layer, so that every layer contributes every kind and
adding a resource of a new kind never needs a mkdir first.
5. Mount it ๐งต¶
Serving /home/you/.ai-resources from 2 sources:
1. ~/ai/personal
2. ~/ai/project
Serving in the background. Stop it with: airfs umount --target /home/you/.ai-resources
Without --detach, airfs mount blocks in the foreground and unmounts on
Ctrl+C โ which is what you want from a service manager unit, or while you are
still experimenting.
6. Read the merged view ๐¶
One directory, both layers. And the winner won whole:
Now the part that makes this worth doing โ edit the file in its repository and read it again through the view:
echo "edited in place" > ~/ai/project/skills/commit/SKILL.md
cat ~/.ai-resources/skills/commit/SKILL.md
No sync step, no remount. The view holds no copy and caches nothing; it reads through to the file you just edited. Adding a whole new skill to a layer works the same way โ it appears in the listing immediately.
The other direction is refused, by the kernel rather than by convention:
A new file in the merged view would belong to some layer, and the view has no basis to pick one. Create it in the repository that owns it.
7. Check and stop ๐¶
target /home/you/.ai-resources
agents served, 0 entries
skills served, 2 entries
commands served, 0 entries
scripts served, 0 entries
status exits 0 when the target is fully served and 2 when it is not, so a
shell profile can branch on it without reading the prose.
Released /home/you/.ai-resources/agents
Released /home/you/.ai-resources/skills
Released /home/you/.ai-resources/commands
Released /home/you/.ai-resources/scripts
Where next ๐¶
- ๐ User guide โ layers, precedence, mounting, the Go API
- ๐งญ Declaring layers โ the full file format
- ๐ฉน Troubleshooting โ when something is not served
- ๐ Specs โ why it behaves the way it does