Skip to content

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 version

gin 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/gin

Two ways to run ​

Gin runs the same core in two modes.

ModeHowWhen to use it
EmbeddedThe 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.
Daemongin 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 *
EOF

Repository 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.conf

The 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 capabilities

gin 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 tree

A 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 --content

INCARNATION, 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 warnings

Persistence tests must run on Unix (WSL counts, with test data on the native Linux filesystem).

Part of the Tama toolchain.