Durable POSIX disks for untrusted agents, with identity.
Quickstart · How it works · Architecture · Docs · Contributing · Security
orlop is a multi-tenant, zero-trust file plane for agent sandboxes. Each agent gets one auto-expanding POSIX directory that it mounts and uses like an ordinary disk. The bytes live remotely in a content-addressed chunk store, so when the sandbox dies the data persists and the next run re-mounts the same disk with zero idle compute.
The credential never reaches the agent. Each agent is issued its own short-lived mTLS client certificate whose identity (a SPIFFE SAN) is the only thing that authorizes access, and the server confines every connection to that agent's own path prefix. A compromised agent cannot read another tenant's bytes, cannot widen its own path, and has no key it could exfiltrate to reach the store directly.
- 🔒 Zero-trust by construction: per-agent mTLS identity, server-side path confinement, no shared storage credential to leak.
- 💾 Survives the sandbox: data lives in the remote chunk store and re-mounts on the next run with zero idle compute.
- 🧱 Content-addressed & deduped: bytes stored verbatim and deduped by hash, so keeping full, uncompressed history is nearly free.
- ⚡ Incremental writes: a single-byte edit ships one ~4 MiB chunk, not the whole file; a persistent client cache makes re-reads run at local-disk speed.
- 🔁 Atomic overwrites: versioned, compare-and-swap manifests replace a stale fact in place instead of appending and hoping retrieval picks the latest.
- 🧩 Drop-in POSIX: FUSE on Linux, in-process NFSv3 loopback on macOS; the agent just sees a directory.
Built for agent memory. orlop is the storage substrate for an agent-memory stack (durable, cheap to update, and safe under multi-tenancy), but it does no extraction, ranking, or semantic consolidation; the layer above does. See
docs/agent-memory.mdfor what orlop gives that stack and where it stops.
Install the binaries (orlop, orlop-control, orlop-server) for your OS:
curl -fsSL https://orlop.dev/install.sh | shA complete single-node stack (control + server + one mounted disk) runs on one
host with no external dependencies — the control plane uses its embedded SQLite
backend (DATABASE_URL=sqlite:./orlop.db), so not even Postgres is required:
orlop dev up # control plane + server + a mounted disk, supervised; Ctrl-C tears it down
orlop status # what's running
orlop dev down # graceful teardown (use `dev up --detach` to run it non-interactively)Follow docs/standalone-quickstart.md to write
a file, restart the stack, and watch the data persist. To run each piece by hand
(server register → token issue → orlop mount --from-env), see
docs/manual-bring-up.md.
agent sandbox control plane data plane
┌──────────────┐ enroll ┌───────────────┐ place ┌───────────────┐
│ orlop mount │────token───▶│ orlop-control │──────────▶│ orlop-server │
│ (FUSE/NFS) │ │ CA · alloc · │ │ chunk store · │
│ │◀──mTLS cert─│ auth · enroll │ │ manifests · │
│ /mnt/orlop │ └───────────────┘ │ leases · GC │
└──────┬───────┘ └──────┬────────┘
│ mTLS data path (per-agent client cert) │
└──────────────────────────────────────────────────────▶│
- Control plane (
orlop-control) is the CA and the allocator. It enrolls an agent, mints a short-lived per-agent mTLS client certificate, and places the agent's disk on a data-plane server. - Mount client (
orlop) mounts the disk over FUSE (Linux) or an in-process NFSv3 loopback (macOS) and speaks the data protocol over mTLS. - Data plane (
orlop-server) stores content-addressed chunks plus per-disk SQLite manifests, and confines each connection to the path its certificate names.
| Component | Lang | Role |
|---|---|---|
cmd/orlop-control |
Go | control plane: auth, per-tenant CA, disk allocation, enroll, mount/lease issuance |
cmd/orlop-server |
Go | data plane: chunk store, manifests, journal/pub-sub, GC, lease sweep, mTLS |
src/ (orlop binary) |
Rust | FUSE/NFS mount client (orlop mount, lease refresh, orlop doctor) |
Why Go and Rust
Each layer uses the language that's strongest for its job, and a clean network boundary (mTLS + msgpack over a long-lived connection, no cgo, no FFI) makes that split essentially free:
- Rust for the mount client because it runs inside the untrusted agent sandbox,
on the hot path of every filesystem syscall. No GC pauses to stall I/O, a small
static binary, low memory footprint, and a mature FUSE/NFS/QUIC ecosystem
(
fuser,nfsserve,quinn). - Go for the control and data planes because they're network services where Go's ecosystem and velocity shine (HTTP router, Postgres, migrations, metrics), and the public client SDK is Go too, which is what host integrators orchestrating sandboxes actually want.
The two halves are separate binaries that only share a wire protocol, so they build
and ship independently. Contributing to one side almost never requires the other's
toolchain. See CONTRIBUTING.md.
GOWORK=off go build ./... # Go control + data plane
cargo build --release # Rust mount client (the `orlop` binary)The Go side is a single module; the Rust side is a Cargo workspace
(orlop + orlop-bench).
github.com/liu1700/orlop/client is a small, standard-library-only Go SDK
for the control-plane API: allocate an agent's disk, set quotas, mint the short-lived
per-agent enroll token, and read usage. A host integrates orlop by calling this SDK
and invoking the orlop binary in the sandbox.
import "github.com/liu1700/orlop/client"
c := client.New("https://orlop-control.example", serviceToken)
disk, err := c.AllocateDisk(ctx, agentID, ownerID, 1<<30)
token, err := c.MintEnrollToken(ctx, agentID) // hand this to the sandboxA client.Fake in-memory implementation is provided for consumer tests.
| Doc | What's inside |
|---|---|
standalone-quickstart.md |
Run the whole thing on one host with orlop dev up |
advanced-usage.md |
Beyond the quickstart: install options, overrides, headless use |
manual-bring-up.md |
The same single-node stack, brought up by hand |
database-backends.md |
Postgres vs embedded SQLite: which to use and how |
container-images.md |
Published GHCR images (control, server, mount) and their runtime contracts |
kubernetes.md |
Reference Helm chart + deployment topology for control + server |
upgrade-safety.md |
In-place upgrade guarantee, schema self-check, migration policy |
design.md |
System overview and filesystem layout |
design-data-plane.md |
Chunk store / journal design |
design-auth.md |
Certificate / tenant isolation model |
design-identity.md |
Host identity: verify a host-issued JWT and map it to a tenant |
control-plane.md |
Control-plane API + versioning/compat policy (OpenAPI: openapi/orlop-control.yaml) |
control-plane-runbook.md |
Operator workflows (CA, admin seeding) |
agent-memory.md |
What orlop gives an agent-memory stack, and where it stops |
audit-events.md |
Audit event schema |
See CONTRIBUTING.md, including why the Go/Rust split means you
rarely need both toolchains to contribute.
The isolation model, operator responsibilities, hardening switches, known limits, and
how to report a vulnerability are in SECURITY.md. Read it before
running orlop with real tenants.