# 1. Start with a minimal server

[All tutorials](/docs/tutorials.md) · Next: [add authentication](/docs/tutorials/02-add-auth.md)

You will set up a one-server cluster with `scribe-ctl`, create a repository,
submit a file from Alice's working copy, and sync it into Bob's. Everything
stays on plaintext loopback; identities here are author labels, not
authenticated users.

Scribe is centralized: `submit` publishes a durable shared change and `sync`
brings a working copy up to the server's history. There is no separate push.

## 1. Install and create the deployment directory

Install the executables from your source checkout (see [install](/docs/tutorials.md#install))
and check that they are on your PATH:

```sh
scribe-ctl --version
```

In a location for demo data outside the source checkout and any Scribe
workspace, create a fresh directory and make everything you create in it
private:

```sh
umask 077
mkdir scribe-demo
cd scribe-demo
```

All remaining commands run from this directory. See the
[terminal and port conventions](/docs/tutorials.md#working-directory-and-ports).

## 2. Set up the cluster

Look at the plan first. `--plan` runs every check for real and writes nothing:

```sh
scribe-ctl --config scribe-cluster.toml setup --variant storage --plan
```

```text
Scribe · local · setup plan · storage variant · native runtime
  Definition  /home/you/scribe-demo/scribe-cluster.toml
  Paths below are relative to its directory.

  ENDPOINTS
    storage-a  tcp://127.0.0.1:7447

  CHECKS
    ok    definition    setup writes scribe-cluster.toml (storage variant)
    ok    binaries      scribe-ctl, scribe-server 0.1.0 (build 86da462a7d46.dirty-20260927T124201); setup records them as the first binary set in cluster-state/local/bin/86da462a7d46.dirty-20260927T124201
    ok    permissions   0 existing secret files are private
    ok    cluster       stopped
    ok    ports         1 listen addresses are free
    ok    certificates  consistent
    ok    data roots    consistent

  STEPS
     1. Write the cluster definition scribe-cluster.toml
        no definition exists yet
     2. Install scribe-ctl, scribe-auth, scribe-server, scribe-gateway 0.1.0 (build 86da462a7d46.dirty-20260927T124201) side by side in cluster-state/local/bin/86da462a7d46.dirty-20260927T124201 (from /home/you/.local/bin)
        the cluster's executables, kept side by side so an upgrade can return to them
     3. Record 86da462a7d46.dirty-20260927T124201 as the active set (bin/current)
        the runtime starts services from the set bin/current names
     4. Initialize storage storage-a in data/storage-a
        no repository store yet
     5. Write the cluster summary CLUSTER-INFO.txt
        every file the cluster uses, its endpoints and the next commands

  Plan only: nothing was written and no lock was taken. Run setup without --plan to apply it.
```

Every check should read `ok`; a failed one says what it found. Then run it:

```sh
scribe-ctl --config scribe-cluster.toml setup --variant storage
scribe-ctl --config scribe-cluster.toml up
```

```text
Scribe · local · running
  SERVICE    STATE        PID  ENDPOINT
  storage-a  running    16741  tcp://127.0.0.1:7447

  Logs  /home/you/scribe-demo/cluster-state/local/logs
  Binaries  0.1.0 (build 86da462a7d46.dirty-20260927T124201) · /home/you/scribe-demo/cluster-state/local/bin/86da462a7d46.dirty-20260927T124201
```

`up` returns once the server is ready; it keeps running in the background under
the controller's supervisor. `scribe-ctl --config scribe-cluster.toml status`
shows the same table at any time.

### What scribe-ctl did for you

- **The definition.** `scribe-cluster.toml` describes the cluster: one storage
  server named `storage-a`, where it listens, and its data root. Every path in
  it is relative to the file. It never holds a secret; later stages add to it.

  ```toml
  [clusters.local]
  variant = "storage"
  state_dir = "cluster-state/local"
  daemon = true
  startup_timeout_secs = 30

  [[clusters.local.storage]]
  name = "storage-a"
  listen = "127.0.0.1:7447"
  data_dir = "data/storage-a"
  auth = false
  ```

  (The file has a comment on every setting.)
- **The data root.** `data/storage-a` is the authoritative store for every
  repository this server holds: history, file contents, and metadata. Setup ran
  `scribe-server init` once to create it empty. Nothing ever initializes it
  again; `up` only opens it, and one process at a time owns it.
- **The binary set.** Setup copied the executables it found beside
  `scribe-ctl` into `cluster-state/local/bin/<build>/` and records that set as
  active. The services start from there, so installing a newer build does not
  disturb a running cluster; [stage 4](/docs/tutorials/04-use-ctl.md#6-upgrade-to-a-new-build)
  switches sets with `upgrade`.
- **The summary.** `CLUSTER-INFO.txt` lists every file the cluster uses and
  what it is for, the endpoints, and the next commands.
  `scribe-ctl --config scribe-cluster.toml where` prints it, current, at any time.

By hand, the same server is `scribe-server init` followed by `scribe-server
serve` in a terminal of its own ([bare metal, stage 1](/docs/manual/bare-metal.md#1-one-storage-server)),
or a container ([Docker Compose, stage 1](/docs/manual/docker-compose.md#1-one-storage-server)).

## 3. Create a repository and Alice's working copy

```sh
scribe --server tcp://127.0.0.1:7447 repo list
scribe --server tcp://127.0.0.1:7447 repo create game --storage-compression lz4
scribe --server tcp://127.0.0.1:7447 workspace create game ./work/alice-plain --author alice
```

The list is empty at first (`(no repositories)`). `game` is the repository
name; `work/alice-plain` is one local working copy. The selected storage
compression is permanent. Network compression is separate and defaults to
none; see the [compression guide](/docs/client-usage.md#compression).

Create and submit a small asset. The parentheses keep your terminal in the
deployment directory after these workspace commands finish:

```sh
(
  cd work/alice-plain
  mkdir Assets
  printf 'hero mesh v1\n' > Assets/Hero.txt
  scribe add Assets/Hero.txt
  scribe submit -m "Initial hero"
  scribe log
)
```

```text
add     Assets/Hero.txt
checked against change 0: ready
submitted change 1 (1 file, 1 of 1 chunks sent, 208 object bytes sent for 13 logical bytes, 3 server flushes)
change 1  branch main  2026-09-27T12:43:32.472Z  alice  1 entry
    Initial hero
```

A successful submit means the server accepted it durably. If submit reports an
unknown outcome, keep the pending workspace state and rerun submit to resolve it.

## 4. Give Bob a working copy and complete the round trip

```sh
scribe --server tcp://127.0.0.1:7447 workspace create game ./work/bob-plain --author bob
(
  cd work/bob-plain
  scribe sync
  cat Assets/Hero.txt
  scribe edit Assets/Hero.txt
  printf 'hero mesh v2\n' > Assets/Hero.txt
  scribe submit -m "Hero v2"
)
(
  cd work/alice-plain
  scribe sync
  cat Assets/Hero.txt
  scribe log
)
```

Alice's sync ends like this:

```text
synced change 0 -> 2: 1 updated, 0 deleted, 0 unchanged (13 bytes fetched in 1 batches, 0 chunks from cache, 0 bytes reused locally) [full reconcile]
preserved Assets/Hero.txt at /home/you/scribe-demo/work/alice-plain/.scribe/_state/recovery/1790513013174-1cffdd4024a2ee9afe42f2021ce39c6a
hero mesh v2
change 2  branch main  2026-09-27T12:43:33.029Z  bob  1 entry
    Hero v2
change 1  branch main  2026-09-27T12:43:32.472Z  alice  1 entry
    Initial hero
```

Both copies now contain `hero mesh v2`. On Alice's sync, the previous local
copy is kept under `.scribe/_state/recovery`, with its location reported. Keep
recovery contents until you have reviewed them. For how opened paths are
deferred and interrupted updates resume, see
[syncing](/docs/client-usage.md#syncing-and-exporting).

## 5. Stop and check the data root

```sh
scribe-ctl --config scribe-cluster.toml down
scribe-server usage --data-dir ./data/storage-a
scribe-server doctor --data-dir ./data/storage-a --quick
```

`down` drains the server: it finishes started durable work before exiting.
The offline checks need the data root to themselves, so they run only after
`down`:

```text
REPOSITORY    ID                HEAD     PATHS  BRANCHES    OBJECTS       LOOSE      PACKED    METADATA       TOTAL  LAST COMMIT
game          689a965e479c         2         1         1          8         0 B       1 KiB     612 KiB     623 KiB  2026-09-27T12:43:33Z bob
...
doctor: healthy (1 repositories, 0 problem(s), 0 warning(s)) in 2 ms
```

The check is read-only; it does not repair data. For a full byte check, omit
`--quick`.

You can stop here: `scribe-ctl --config scribe-cluster.toml up` starts the same
server again whenever you need it. To share authenticated access, continue with
[2. Add authentication](/docs/tutorials/02-add-auth.md); leave the cluster stopped.
