A small Go daemon that keeps a local folder (~/crate/ by default) in sync with your Crate cloud folder. Cloud is canonical; the daemon is a continuously-updated local mirror. macOS + Linux today; Windows is on the v1.x roadmap.
The daemon never holds your bucket credentials. It authenticates to a transport (nakli-hub or nakli-cf-worker) via a pairing token issued by the browser Crate. That's a security property, not a layering accident — losing the daemon doesn't expose your R2 access keys.
For an illustrated walk-through of pairing the daemon with your Crate folder, see the user guide § The desktop daemon. What follows is the terse install + reference.
The simplest path: download a prebuilt binary for your OS from the latest release.
# macOS (Apple Silicon)
curl -L -o crate-agent https://github.com/NakliTechie/crate-agent/releases/latest/download/crate-agent-darwin-arm64
chmod +x crate-agent
sudo mv crate-agent /usr/local/bin/
# macOS (Intel)
curl -L -o crate-agent https://github.com/NakliTechie/crate-agent/releases/latest/download/crate-agent-darwin-amd64
chmod +x crate-agent && sudo mv crate-agent /usr/local/bin/
# Linux (x86_64)
curl -L -o crate-agent https://github.com/NakliTechie/crate-agent/releases/latest/download/crate-agent-linux-amd64
chmod +x crate-agent && sudo mv crate-agent /usr/local/bin/
# Linux (ARM64)
curl -L -o crate-agent https://github.com/NakliTechie/crate-agent/releases/latest/download/crate-agent-linux-arm64
chmod +x crate-agent && sudo mv crate-agent /usr/local/bin/Confirm the install:
crate-agent versionOr build from source — single statically-linked binary, no CGO, no runtime dependencies:
make build # current host only → dist/crate-agent
make all # cross-compile all four targets → dist/# 1. Open your Crate in the browser (https://crate.naklios.dev/), unlock the
# folder, click "Pair an agent". The modal shows a CRATE-PAIR-… token.
# Copy it.
# 2. Pair the daemon to that folder. You'll be prompted for the token
# + your folder passphrase.
crate-agent pair
# Paste pairing token: CRATE-PAIR-…
# Folder passphrase: ********
# ✓ Paired with crate-<bucket-name>
# 3. Verify the install is healthy.
crate-agent doctor
# 4. Start syncing. Foreground first — Ctrl-C to stop.
crate-agent start
# Watching ~/crate, syncing to <bucket>…Drop a file into ~/crate/, it encrypts + uploads. Edit a file in the browser, it downloads + decrypts to ~/crate/ within ~15 s. Bidirectional, byte-identical wire format on both sides.
When you're happy with foreground behaviour, install as a long-running service:
crate-agent install-service # launchd on macOS, systemd-user on Linux
crate-agent status # confirm it's running
crate-agent uninstall-service # remove the unit| Command | What |
|---|---|
crate-agent pair |
Redeem a CRATE-PAIR-… token from the browser. Writes config + identity key. |
crate-agent start |
Run the watcher + sync loop + capability-refresh runner. Ctrl-C to stop gracefully. |
crate-agent stop |
Signal the running daemon to terminate (reads $XDG_STATE_HOME/nakli/crate-agent.pid, sends SIGTERM, waits for drain). |
crate-agent status |
Process state + queue depth + recent conflicts + capability expiry. --json for machine-readable output. |
crate-agent doctor |
Self-check: config valid, identity readable, transport reachable, state DB readable, watcher functional. |
crate-agent install-service |
Generate + load a user-level supervisor unit. |
crate-agent uninstall-service |
Remove the unit. |
crate-agent version |
Version + build date + git SHA + Go runtime + OS/arch. |
crate-agent --help |
Full command reference. |
status doesn't need the folder passphrase — it reads daemon state only. Everything that touches encrypted bytes (pair, start, doctor) needs the passphrase.
┌──────────────┐ ┌─────────────┐
~/crate/ ◄──►│ crate-agent │◄──── token ────►│ transport │◄──── ciphertext ────► R2 / B2 / …
│ (Go daemon) │ │ (Hub or CF) │
└──────────────┘ └─────────────┘
▲
│ same encrypted bytes
▼
┌────────────────────┐
│ browser Crate tab │
│ at crate.naklios.dev │
└────────────────────┘
- Watcher (
internal/watcher/) monitors~/crate/for changes via fsnotify; ignores files matching.crateignorepatterns. - Syncer (
internal/syncer/) walks the change queue, encrypts new/modified files under the master key, appends a signed manifest event, PUTs the ciphertext + the new manifest. ETag-conditional with replay-on-412 so concurrent writes from the browser don't clobber. - Puller (
internal/puller/) polls the manifest every ~15 s, diffs against the local state, downloads + decrypts changed objects, writes to disk atomically. - Refresh runner (
internal/refresh/) re-mints the daemon's transport capability when <20 % of TTL remains, re-encrypts under the in-memory master key, atomic-rewrites config. - State DB (
internal/state/, SQLite viamattn/go-sqlite3) tracks per-file content hashes, the last-synced manifest UUID, the daemon's pending change queue. Crash-resumable.
The wire format on the bucket is byte-identical to what the browser Crate reads and writes. The daemon has no way to read a folder the browser hasn't first set up; the browser has no need for the daemon. They're independent, interoperable surfaces over the same cryptographic invariant.
| Path | Mode | What |
|---|---|---|
~/.config/nakli/crate-agent.toml |
0600 | Config: encrypted capability + salt + nonce + transport endpoint |
~/.config/nakli/identity.key |
0600 | FIF-wrapped ephemeral Ed25519 keypair (unlocks with the folder passphrase) |
$XDG_STATE_HOME/nakli/state.db |
0600 | SQLite — change queue, content hashes, manifest cache |
$XDG_STATE_HOME/nakli/crate-agent.pid |
0644 | Pidfile (atomic create; stale-file detection) |
~/crate/ |
0700 | The synced folder. Contents follow your bucket; ignore patterns in .crateignore. |
Configurable via the TOML — the wizard's defaults work for most users.
- No bucket credentials on disk. The daemon holds an encrypted transport capability — a macaroon scoped to your bucket's sync primitive. Losing the daemon leaks the capability (encrypted under your passphrase) but never your R2 access keys.
- Passphrase + identity key required to start.
crate-agent startdecrypts the capability with the passphrase you set at pairing. No passphrase, no daemon. The master key lives only in the daemon process's memory. - Capability refresh, not capability storage. Capabilities have a 1-year TTL with auto-refresh at 80 %. Stolen capabilities can be revoked from the browser ("Pair an agent" → revoke device).
- AGPL-3.0-or-later. The whole code path is auditable; transport contract is documented in
docs/specs/and the crate-agent wire-protocol audit.
| Daemon (this) | NakliTechie/crate-agent |
| Browser surface | NakliTechie/crate — live at crate.naklios.dev |
| Transports + Hub | NakliTechie/private-mesh |
AGPL-3.0-or-later. See LICENSE.