Resources
10Install
npx skillscat add redis/agent-filesystem Install via the SkillsCat registry.
Agent Filesystem
Agent Filesystem is a Redis module that provides a complete POSIX-like filesystem as a native data type, plus a FUSE mount that exposes it as a regular local directory. One Redis key holds one filesystem volume — directories, files, symlinks, permissions, and all metadata.
Why Agent Filesystem:
- Persistence — data survives process restarts via RDB/AOF
- Multi-client access — any Redis client can read/write the same volume concurrently
- Atomic operations — every FS.* command is atomic, no partial writes
- No local disk dependency — agents can run statelessly; storage lives in Redis
- Instant cleanup —
DEL volremoves an entire filesystem in one command
Key concept: one Redis key = one filesystem volume. The key name is the volume name. All paths within a volume are absolute (start with /).
Build
cd /home/ubuntu/git/agent-filesystem
make # builds module/fs.so + mount/agent-filesystem-mount + afs + afs-control-plane
make module # builds only the Redis module (module/fs.so)
make mount # builds only the FUSE mount binary
make commands # builds afs + afs-control-planeCLI Commands
The afs binary is at the repo root after building.
| Command | Description |
|---|---|
afs setup |
Interactive first-time wizard — saves config only |
afs up |
Start services from saved config |
afs down |
Stop all services and unmount |
afs status |
Show current status |
afs migrate <dir> |
Import a directory into Redis, honoring .afsignore if present |
Use --config <path> before any command to override the config file location:
./afs --config /path/to/custom.json upProgrammatic Setup (for agents)
Skip the interactive wizard by writing the config file directly.
1. Build
cd /home/ubuntu/git/agent-filesystem && make2. Write config
cat > /home/ubuntu/git/agent-filesystem/afs.config.json << 'EOF'
{
"redis": {
"addr": "localhost:6379",
"password": "",
"db": 0
},
"mode": "sync",
"currentWorkspace": "my-workspace",
"localPath": "/home/ubuntu/agent-filesystem",
"mount": {
"backend": "none",
"readOnly": false,
"allowOther": false,
"mountBin": ""
},
"logs": {
"mount": "/tmp/afs-mount.log",
"sync": "/tmp/afs-sync.log"
},
"sync": {
"fileSizeCapMB": 100
}
}
EOF3. Start
./afs up4. Verify
./afs status
ls ~/agent-filesystemConfiguration Reference
File: afs.config.json (next to the afs binary in the repo root)
| Field | Type | Default | Description |
|---|---|---|---|
redis.addr |
string | "localhost:6379" |
Redis server address (host:port) |
redis.username |
string | "" |
Optional Redis username |
redis.password |
string | "" |
Redis password (empty = no auth) |
redis.db |
int | 0 |
Redis database number |
redis.tls |
bool | false |
Enable TLS for the Redis connection |
mode |
string | "sync" |
sync, mount, or none |
currentWorkspace |
string | "" |
Workspace selected by default for up, clone, and checkpoints |
localPath |
string | "~/afs" |
Sync root in sync mode or mountpoint in mount mode |
mount.backend |
string | "none" |
none, fuse, or nfs |
mount.readOnly |
bool | false |
Start the local surface as read-only |
mount.allowOther |
bool | false |
Allow other system users to access the mount |
mount.mountBin |
string | auto | Path to agent-filesystem-mount when using FUSE |
mount.nfsBin |
string | auto | Path to agent-filesystem-nfs when using NFS |
mount.nfsHost |
string | "127.0.0.1" |
Host for the local NFS export |
mount.nfsPort |
int | 20490 |
Port for the local NFS export |
logs.mount |
string | "/tmp/afs-mount.log" |
Mount daemon log file |
logs.sync |
string | "/tmp/afs-sync.log" |
Sync daemon log file |
sync.fileSizeCapMB |
int | 100 |
Max file size synced through local sync mode |
Common config patterns
Sync mode (recommended):
{
"redis": {
"addr": "redis.local:6379"
},
"mode": "sync",
"currentWorkspace": "my-workspace",
"localPath": "/home/ubuntu/data"
}Live mount mode:
{
"redis": {
"addr": "redis.local:6379",
"password": "secret"
},
"mode": "mount",
"currentWorkspace": "my-workspace",
"localPath": "/home/ubuntu/data",
"mount": {
"backend": "nfs"
}
}Runtime State
- Config:
afs.config.json(repo root, next to the binary) - State:
~/.afs/state.json(runtime PIDs, created/removed automatically)
FS.* Command Reference
All commands use the pattern: FS.<CMD> <key> <path> [args...]
Replace vol below with your chosen key name.
Quick reference
| Unix command | Agent Filesystem equivalent | Notes |
|---|---|---|
echo "text" > file |
FS.ECHO vol /file "text" |
Creates parents automatically |
echo "text" >> file |
FS.ECHO vol /file "text" APPEND |
Also FS.APPEND |
cat file |
FS.CAT vol /file |
Follows symlinks |
touch file |
FS.TOUCH vol /file |
Creates empty file or updates mtime |
mkdir dir |
FS.MKDIR vol /dir |
Parent must exist |
mkdir -p a/b/c |
FS.MKDIR vol /a/b/c PARENTS |
Creates intermediates |
ls dir |
FS.LS vol /dir |
Returns child names |
ls -l dir |
FS.LS vol /dir LONG |
Includes type, mode, size, mtime |
rm file |
FS.RM vol /file |
Works on files, dirs, symlinks |
rm -r dir |
FS.RM vol /dir RECURSIVE |
Deletes entire subtree |
cp src dst |
FS.CP vol /src /dst |
Files only without RECURSIVE |
cp -r src dst |
FS.CP vol /src /dst RECURSIVE |
Deep copy with metadata |
mv src dst |
FS.MV vol /src /dst |
Moves entire subtrees atomically |
find dir -name "*.txt" |
FS.FIND vol /dir "*.txt" |
Glob: *, ?, [a-z], [!x], \ |
find dir -name "*.txt" -type f |
FS.FIND vol /dir "*.txt" TYPE file |
Filter: file, dir, symlink |
grep -r "pattern" dir |
FS.GREP vol /dir "*pattern*" |
Glob on each line |
grep -ri "pattern" dir |
FS.GREP vol /dir "*pattern*" NOCASE |
Case-insensitive |
stat file |
FS.STAT vol /file |
type, mode, uid, gid, size, times |
test -e file |
FS.TEST vol /file |
Returns 1 or 0 |
chmod 0755 file |
FS.CHMOD vol /file 0755 |
Octal mode string |
chown uid:gid file |
FS.CHOWN vol /file uid gid |
Separate uid and gid args |
ln -s target link |
FS.LN vol /target /link |
Relative or absolute target |
readlink link |
FS.READLINK vol /link |
Returns raw target string |
tree dir |
FS.TREE vol /dir |
Nested array structure |
tree -L 2 dir |
FS.TREE vol /dir DEPTH 2 |
Limits recursion depth |
du -sh / df |
FS.INFO vol |
File/dir/symlink counts + total bytes |
Usage examples
Write and read:
redis-cli FS.ECHO vol /hello.txt "Hello, World!"
redis-cli FS.CAT vol /hello.txtAppend:
redis-cli FS.ECHO vol /log.txt "first line" APPEND
redis-cli FS.ECHO vol /log.txt "second line" APPENDDirectories:
redis-cli FS.MKDIR vol /data/projects PARENTS
redis-cli FS.LS vol /data LONGSearch by filename:
redis-cli FS.FIND vol / "*.md"
redis-cli FS.FIND vol /src "*.go" TYPE fileSearch file contents:
redis-cli FS.GREP vol / "*TODO*"
redis-cli FS.GREP vol /src "*error*" NOCASEWorkspace search via AFS CLI:
afs grep --workspace vol "TODO auth"
afs grep --workspace vol --path /logs "disk full"
afs grep --workspace vol -E "timeout|retry"
afs grep --workspace vol -l "TODO"Copy, move, delete:
redis-cli FS.CP vol /config.json /config.json.bak
redis-cli FS.CP vol /src /src-backup RECURSIVE
redis-cli FS.MV vol /draft.txt /final.txt
redis-cli FS.RM vol /temp RECURSIVESymlinks:
redis-cli FS.LN vol /config.json /current-config
redis-cli FS.READLINK vol /current-configFilesystem overview:
redis-cli FS.INFO vol
redis-cli FS.TREE vol / DEPTH 2Bulk Import via redis-cli
If you need to import files directly via redis-cli without the afs migrate command:
cd /path/to/local/dir && find . -type f | while read -r f; do
redis-cli FS.ECHO vol "/${f#./}" "$(cat "$f")"
doneOptional — preserve permissions:
cd /path/to/local/dir && find . -type f | while read -r f; do
path="/${f#./}"
redis-cli FS.CHMOD vol "$path" "0$(stat -c '%a' "$f")"
redis-cli FS.CHOWN vol "$path" "$(stat -c '%u' "$f")" "$(stat -c '%g' "$f")"
doneOr use afs migrate <dir> which handles import, archiving, and mounting in one step. If the source directory contains a .afsignore, matching files and directories are skipped using .gitignore-style patterns.
Volume Management
redis-cli DEL vol # delete entire filesystem
redis-cli EXPIRE vol 3600 # auto-expire after 1 hour
redis-cli SCAN 0 TYPE redis-fs0 # list all filesystem volumes
redis-cli RENAME staging production # rename a volumeKey Differences and Gotchas
- All paths must be absolute — start with
/. There is no working directory. - No
cdorpwd— every command takes the full path explicitly. - Grep uses glob patterns, not regex — use
*error*not.*error.*. - FS.GREP returns triples — each match is
[filepath, line_number, line]. - FS.FIND matches basename only —
FS.FIND vol / "*.md"matches/docs/README.md. - FS.ECHO auto-creates parents — no need to
mkdir -pbefore writing. - Symlinks resolve at read time — max 40 levels; cycles produce an error.
- Large recursive operations block Redis — partition across multiple keys for millions of files.
- No streaming reads —
FS.CATreturns the entire file at once. - Permission bits are metadata only —
FS.CHMOD/FS.CHOWNstore values but Redis does not enforce them.
Troubleshooting
| Problem | Solution |
|---|---|
no configuration found |
Run afs setup or create afs.config.json next to the binary |
module not loaded |
Load with: redis-cli MODULE LOAD /path/to/module/fs.so |
cannot connect to Redis |
Start Redis yourself, then point redis.addr at that instance |
cannot find agent-filesystem-mount |
Run make mount in the repo root |
mount did not become ready |
Check mountLog path for errors |
agent-filesystem is already running |
Run afs down first |