Getting started
This page builds Gin from source, starts a local daemon and runs a first headless session: create a workspace, look at it, save a file.
Platform status
Read this first
- Linux is the primary target. The first milestone is a Linux, headless slice.
- Durable stores need a Unix filesystem. The object store and workspace journals fail closed elsewhere. On Windows, use WSL and keep Gin's data on the native Linux filesystem (not
/mnt/c). - macOS builds and has platform-specific flush handling, but is less exercised than Linux.
- Windows binaries build, but native Windows storage is refused today. A Windows machine can still act as a client of a remote
gin serve. - Local IPC (Unix domain sockets) is Unix-only.
Build from source
The Rust toolchain is pinned in rust-toolchain.toml (currently Rust 1.98.1 with rustfmt and clippy). With rustup installed, cargo fetches it on first use.
sh
rustup toolchain install # optional: fetch the pinned toolchain now
cargo build --workspace # build everything
cargo run -p gin # print the versiongin with no arguments prints its version, for example gin 0.1.0. gin help prints the usage of every command.
For a binary you can put on your PATH, build the gin package in release mode:
sh
cargo build --release -p gin
./target/release/ginTwo ways to run
Gin runs the same core in two modes.
| Mode | How | When to use it |
|---|---|---|
| Embedded | The CLI, MCP server or your Rust program opens a worker's stores in-process with --host DIR. | One trusted local owner per workspace; no network, no daemon. |
| Daemon | gin serve owns the stores. Clients connect with --endpoint URL --token-file FILE. | Several agents or machines; authenticated access; lifecycle operations over the network. |
In embedded mode the command opens the workspace under its exclusive journal lock, without a mount or Git checkout. Only one process can hold a worker's stores at a time. Never open a daemon's data directory from another process while the daemon runs.
Embedded workspaces are created by a Rust coordinator (for example with Host::fork_root), which then hands the workspace to the CLI or MCP tool. The daemon exposes workspace creation over the API, so it is the easiest way to try Gin from a shell. The rest of this page uses it.
A first session
1. Configure an identity
The daemon authenticates every remote call with a machine-identity token. Create a token file and an auth file. Tokens are 32 to 1024 visible ASCII bytes; use a random value of at least 256 bits.
sh
mkdir -p ~/gin-trial && cd ~/gin-trial
head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' > client.token
chmod 600 client.token
export GIN_REPOSITORY=gin:blake3:v1:repository:$(printf '01%.0s' $(seq 32))
export GIN_WORKSPACE=gin:blake3:v1:workspace:$(printf '02%.0s' $(seq 32))
cat > auth.conf <<EOF
identity me token-file $PWD/client.token
grant me repository $GIN_REPOSITORY operations *
EOFRepository and workspace IDs are typed identities of the form gin:blake3:v1:<kind>:<64 hex digits>. For a trial, fixed values are fine; use a fresh workspace ID for every new workspace.
2. Start the daemon
sh
gin serve --listen 127.0.0.1:50051 --data ~/gin-trial/data --auth ~/gin-trial/auth.confThe daemon logs to stderr and announces gin serve: gRPC on 127.0.0.1:50051 when it is ready. Plain HTTP is allowed only on loopback; see Running the daemon for TLS.
3. Point the CLI at it
In another shell, select the remote session through environment variables:
sh
export GIN_ENDPOINT=http://127.0.0.1:50051
export GIN_TOKEN_FILE=~/gin-trial/client.token
export GIN_REPOSITORY=... # the same values as above
export GIN_WORKSPACE=...
unset GIN_HOST GIN_DATA GIN_PRINCIPAL # remote sessions refuse local options
gin capabilitiesgin capabilities lists the operations the server actually supports and the grants you hold.
4. Create a workspace
Workspace creation (fork) goes through gin call, which sends a complete gin.v1 request in protobuf JSON:
sh
gin call --json "{\"majorVersion\":1,\"requestId\":\"fork-1\",\"repositoryId\":\"$GIN_REPOSITORY\",
\"mutation\":{\"attemptId\":\"fork-1\",\"expected\":[{\"resourceId\":\"$GIN_WORKSPACE\",
\"resourceKind\":\"RESOURCE_KIND_WORKSPACE\",\"absent\":true}]},
\"fork\":{\"workspaceId\":\"$GIN_WORKSPACE\"}}"The response's state carries the workspace version. Its related_ids hold the store incarnation and the lease epoch, in that order. Keep them: remote saves need them as a write fence.
5. Look, then save
sh
gin context # incarnation_id, generation, epoch_id, parents, sections
gin read # the root directory of the private treeA save names the workspace state it expects (from gin context) and, for every path, what it expects to find there: absent, or the current content identity (which gin read PATH lists first in stable_ids).
sh
gin save --incarnation INCARNATION --generation GENERATION --epoch EPOCH \
--lease-incarnation STORE_INCARNATION --lease-epoch LEASE_EPOCH \
--request-id save-1 \
--path hello.txt --expect absent --text 'hello from an agent'
gin read hello.txt --contentINCARNATION, GENERATION and EPOCH come from gin context; STORE_INCARNATION and LEASE_EPOCH come from the fork's related_ids. If anything changed since you looked, the save is refused and the response tells you the current state. Re-read and try again with a new request ID. Rerunning the same --request-id with the same arguments returns the recorded outcome instead of saving twice.
6. Next
Saves stay private. To publish your work and build on it from another workspace, follow the agent workflow.
Durability mode
Native stores default to cached writes: every acknowledgement survives a process kill, but an OS crash or power loss can lose recent work, and recovery exposes a consistent state. To request device flushes before acknowledging, opt in to full durability:
sh
export GIN_DURABILITY=full # or "cached"Without the variable, Gin reads the nearest ancestor gin-durability.toml containing durability = "cached" or durability = "full". Invalid values are refused. Put repository-wide configuration above both the worker directories and the shared data directory.
On macOS, SQLite full-device flushing is off by default; set GIN_SQLITE_APPLE_FULLFSYNC=on before starting Gin to opt in.
Running the tests
sh
cargo nextest run --workspace --locked --profile ci # the gate tests
cargo test --doc --workspace --locked # doc-tests
cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warningsPersistence tests must run on Unix (WSL counts, with test data on the native Linux filesystem).