File Sync
Share state across multiple instances of an agent-native app through a database.
Overview
File sync watches your local files, pushes changes to a database, and pulls remote updates back to disk. Files stay the source of truth — the database is just a sync target.
Out of the box it uses Drizzle with SQLite — no external services, no extra packages. Just add a config file with your sync patterns and you're done.
Setup
Create a sync-config.json in your content root (typically data/) with the file patterns you want to sync:
{
"syncFilePatterns": [
"data/**/*.json",
"data/**/*.md",
"!data/local-only/**"
]
}That's it. When Drizzle is set up (which it is by default), the sync engine detects the config file and starts syncing automatically. The SQLite database is created at data/sync.db on first run.
The default template server already calls createFileSync() — no server code changes needed.
Configuration
| Field | Description |
|---|---|
syncFilePatterns | Glob patterns for files to sync. Supports negation with ! prefix. |
privateSyncFilePatterns | Patterns for files that sync to a per-user channel instead of the shared channel. |
Denylist
The sync engine always blocks sensitive files regardless of your patterns — .env*, *.key, node_modules/, .git/, *.db, and editor/OS junk files. This prevents accidental credential leaks.
External Backends
For multi-server deployments or real-time team sync, you can swap the default SQLite backend for an external database. Set FILE_SYNC_BACKEND in your .env:
Firestore
pnpm add firebase-adminFILE_SYNC_BACKEND=firestore
GOOGLE_APPLICATION_CREDENTIALS=./service-account.jsonThe collection is created automatically — no additional setup needed.
Supabase
pnpm add @supabase/supabase-jsFILE_SYNC_BACKEND=supabase
SUPABASE_URL=https://xyz.supabase.co
SUPABASE_PUBLISHABLE_KEY=sb_publishable_...Supabase requires a files table:
CREATE TABLE files (
id TEXT PRIMARY KEY,
path TEXT,
content TEXT,
app TEXT,
owner_id TEXT,
last_updated BIGINT,
created_at BIGINT
);
CREATE INDEX idx_files_app_owner ON files(app, owner_id);Convex
pnpm add convexFILE_SYNC_BACKEND=convex
CONVEX_URL=https://your-project.convex.cloudConvex requires a schema and functions — see the repo README for the full Convex setup.
Custom Adapters
If the built-in backends don't fit, you can implement the FileSyncAdapter interface from @agent-native/core/adapters/sync for any database. An adapter is a single class with five methods: query, get, set, delete, and subscribe. The sync engine handles file watching, conflict resolution, and retry queues — your adapter just talks to the database.
import type { FileSyncAdapter, FileRecord, FileChange, Unsubscribe }
from "@agent-native/core/adapters/sync";
class MyAdapter implements FileSyncAdapter {
async query(appId: string, ownerId: string) { /* return all records */ }
async get(id: string) { /* return one record or null */ }
async set(id: string, record: Partial<FileRecord>) { /* upsert */ }
async delete(id: string) { /* remove */ }
subscribe(appId: string, ownerId: string,
onChange: (changes: FileChange[]) => void,
onError: (error: any) => void): Unsubscribe { /* listen for changes */ }
}Pass your adapter to FileSync and call initFileSync() — the engine does the rest.