Virtual filesystem engine for exposing application capabilities and Agent Skills as files.
skills-fs is a Go library and CLI that turns application capabilities into a virtual filesystem. Read a file to query data. Write to a file to perform an action. List a directory to discover what's available. Every filesystem operation is dispatched to a provider — local code, a remote HTTP endpoint, or a stream — through a radix-tree router with POSIX semantics.
AI agents interact best with flat, discoverable interfaces. A filesystem is
that interface: cat to read, > to write, ls to explore. skills-fs makes
any application capability — a bot API, a database query, a live event stream
— available through these primitives, so agents need only filesystem knowledge
to operate.
Example: NapCat QQ Bot → filesystem
cat napcat/events # read recent messages
echo '{"group_id":123,"message":"hello"}' > napcat/send_group # send message
cat napcat/status # check bot statusEach node in the virtual filesystem is a mount entry with a kind:
| Kind | Description | Example |
|---|---|---|
| Blob | Static file with inline content. Read-only. | /SKILL.md, /AGENTS.md |
| API | Content produced by provider read action; writes forwarded as provider write action. Optional JSON payload forwarding (writeParams: "json"). |
napcat/send_group, napcat/events |
| Dir | Static directory containing nested mounts. | /napcat/, /napcat/groups/ |
| DynamicDir | Provider-backed directory. On readdir, invokes a provider action that returns JSON entries. Entries matched against registered mounts to determine kind. |
groups/{group_id}/, friends/{user_id}/ |
| Stream | Bounded ring buffer. Supports block, drop, or error backpressure. Multiple handles share one buffer (FIFO semantics). |
napcat/events, napcat/alerts |
| Link | Symbolic link to another mount path. | /my-skill -> /skills/real-skill |
- Radix-tree router: fast prefix matching for path resolution.
- Sharded handles: 16-map sharding for concurrent open/read/write.
- Advisory flock: per-path shared/exclusive locks, auto-released on close.
- Serial queue: per-mount serialization prevents race conditions while allowing cross-path concurrency.
- Write buffering: payloads coalesced until size threshold, delay, or newline triggers flush.
- Event bus: create/write/remove broadcast with path prefix filtering.
- Provider cache: TTL-based
(action, params)cache per mount. - Prometheus metrics:
/metricsendpoint on WebDAV and WebSocket servers.
Three transport layers let skills-fs reach any client:
- FUSE (Linux) — native mount via
go-fuse/v2. Includes inotify forwarding. - WebDAV — full HTTP server with Basic Auth, TLS, gzip, CORS, rate limiting, ETags, Range requests, property caching.
- WebSocket — streaming operations, JSON/binary messages, per-message deflate (RFC 7692), subscription IDs for event watching.
-
HTTP provider — forwards
Invokecalls to a remote HTTP endpoint as JSON POST requests ({ "action": "...", "params": {...} }). Configurable retry with exponential backoff and jitter; circuit breaker on consecutive failures. -
Local provider — executes in-process Go functions.
skills-fs compiles to a C shared library (libgobridge.so) via Go's cgo
export mechanism. Callers use dense, monotonic uintptr handles — no pointer
management required.
| Binding | Technology | Interface |
|---|---|---|
| Python | ctypes | skills_fs.py — object-oriented wrapper |
| Node.js | N-API (node-addon-api) | index.js — async methods, path() calls |
| Go | Direct import | github.com/skills-fs/skills-fs/core |
A Skill is a declarative bundle that generates filesystem mounts from a template. Skills define their capabilities, documentation, and agent guidance — then skills-fs generates the actual mount structure at runtime.
{
"name": "napcat-cli",
"description": "NapCat QQ bot messaging",
"bodyTemplate": "# NapCat CLI Skill\n\nAccess QQ bot via filesystem...",
"agentsTemplate": "# Agent Guide\n\n## Daemon requirements...",
"exposeAtRoot": true,
"allowedTools": ["read_file", "write_file", "list_directory"]
}The Skill generator writes a SKILL.md to disk (YAML frontmatter + template
body) and optionally an AGENTS.md with agent-specific guidance. When
exposeAtRoot is true, these files are also mounted at /SKILL.md and
/AGENTS.md in the virtual filesystem.
The napcat-cli project demonstrates a complete skills-fs integration:
-
Watch daemon (
daemon/watch.py) connects to NapCat's WebSocket, writes events to disk, and runs an HTTP server implementing the skills-fs provider contract. -
skills-fs config (
skills-fs-config.json) declares the provider URL (http://127.0.0.1:18821/invoke) and includes a fragment file (skills-fs-fragment.json) with the full mount tree for napcat-cli. -
Fragment file defines dozens of mounts:
napcat/send_group,napcat/events,napcat/groups/{group_id}/{time_range}/{message_id}, etc. Dynamic directories let agents browse message history by group and time range. -
FUSE mount makes all paths available as real filesystem paths to the AI agent, which interacts using only
cat,ls, andecho >.
import "github.com/skills-fs/skills-fs/core"
fs, _ := core.NewFileSystem(core.Config{
MaxOpenHandles: 1024,
DefaultUID: 1000,
DefaultGID: 1000,
})
fs.Mount(core.MountEntry{
Path: "/hello",
Kind: core.KindBlob,
Mode: 0o644,
BlobData: []byte("world"),
})
data, _ := fs.OpenRead("/hello")go run ./cmd/skills-fs webdav -addr :8080
go run ./cmd/skills-fs websocket -addr :8081
go run ./cmd/skills-fs fuse -mountpoint /tmp/skills-fs
go run ./cmd/skills-fs webdav -config config.json
go run ./cmd/skills-fs validate -config config.jsonfrom skills_fs import SkillsFs
import json
cfg = json.loads(open("config.json").read())
fs = SkillsFs(cfg)
print(fs.read("/hello")) # b"world"const SkillsFs = require("skills-fs");
const fs = new SkillsFs(require("./config.json"));
console.log(fs.read("/hello").toString()); // "world"The config file is JSON with these top-level keys:
| Key | Description |
|---|---|
providers |
Array of provider definitions (id, url). |
mounts |
Array of mount entries (path, kind, mode, read/write actions, provider). |
skills |
Array of skill definitions. |
skillsRoot |
Directory where skill SKILL.md files are generated. |
includes |
Additional config files to merge (resolved relative to parent). |
defaultUID / defaultGID |
Default ownership for generated mounts. |
maxOpenHandles |
Handle budget (default: 1024). |
lockTimeout |
Advisory lock timeout (default: 30s). |
serialQueue |
Per-mount serial queue size (default: 1). |
Multiple skills can share one skills-fs instance without a single global
config. The includes array loads and merges additional config files:
{
"skillsRoot": "$HOME/.skills",
"providers": [{"id": "napcat", "url": "http://127.0.0.1:18821/invoke"}],
"includes": ["skills-fs.d/napcat-cli.json"]
}SIGINT/SIGTERM: graceful shutdownSIGHUP: reload configuration file (webdav / websocket commands)
Prometheus text format at /metrics on WebDAV and WebSocket servers.
make all # lint + test + vulncheck
make quick # fmt + vet + core/registry/provider tests (fast)
make ci # fmt + lint + test + coverage + race + vulncheck + bench (full)
make lint # go vet + staticcheck
make test # run all tests
make race # core tests with race detector
make coverage # check core coverage against 85% gate
make vulncheck # scan dependencies for vulnerabilities
make bench # run benchmarks
make bench-gate # compare benchmarks against baseline (benchstat)
make gen-docs # regenerate API reference docs
make binding-node # build Node.js N-API addon
make binding-python # build Python ctypes module
make clean # remove build artifactscorepackage: >91% statement coverage; total >85%.- Fuzz tests for router and path normalization.
- Benchmarks: path resolution, stat, write, lock contention, serial queue, event emit, stream read/write, handle open/close, HTTP provider roundtrip.
Generated from source. Located in docs/api/:
- core — FileSystem, MountEntry, Handle, Config, events, locks, streams, metrics, skills
- adapter — MountOptions, adapter interface
- adapter/fuse
- adapter/webdav
- adapter/websocket
- provider/http
- provider/local
- provider/cache