# swarmops serve

> Release: 0.11.0 · Last changed: 2026-11-07 · Source: https://github.com/KeiaiLab/SwarmOps/blob/stable/docs/reference/commands/serve.md

Serve the stores under --root to stock git: fetch, clone and push over ssh as a forced command, or over smart HTTP as a CGI program. One process per connection, no daemon.

Over ssh, an authorized\_keys line runs it: command="swarmops serve --root /srv/so --as person/ada",restrict ssh-ed25519 .... The client's git-upload-pack '&lt;store&gt;.git' or git-receive-pack '&lt;store&gt;.git' (SSH\_ORIGINAL\_COMMAND) names a store by its directory under --root. With --cgi, a web server runs it for smart HTTP, its PATH\_INFO starting with the store's name; TLS and authentication are the web server's.

The caller is named by --as alone, or with --cgi by --user-family &lt;family&gt; as &lt;family&gt;/$REMOTE\_USER: the global --as default, $SWARMOPS\_AGENT, does not apply to serve, nor does $SWARMOPS\_RUN\_ID, so that the serving account's environment names no caller. A connection that names none is refused with E\_AUTH (details.reason no\_identity), whatever it asks for. The store's access.toml grants read, push, force and land; a caller it names but does not grant is refused with E\_POLICY\_DENIED.

The branches are served as refs/heads/&lt;name&gt;, main among them, and the tags as refs/tags/&lt;path&gt;. A push moves a store ref by compare-and-swap: main only forward along first parents. A push with one refused ref is refused whole, inside git's quarantine: every ref gets pre-receive hook declined, a remote: line per ref says why, and no object of it enters serve.git. A push to refs/land/&lt;target&gt; lands its commits onto &lt;target&gt; as swarmops land does, and reports a conflict on git's remote: lines.

A refusal before git starts prints one ERR pkt-line on stdout, which git shows as remote error: &lt;CODE&gt;: &lt;message&gt;, and the envelope on stderr; with --cgi, a Status: header and the same text. --hook pre-receive and --hook proc-receive are the entries git runs inside a push, from the hook files serve writes; they take no --as. --stdio, the store-to-store sync, is not served yet.

A connection whose store has not moved since serve.git's last export takes no lock: git/serve.state names that export. serve --prepare makes serve.git ready for the store --repo names before a client connects, so that the connection finds it current and exports nothing; it needs no --root. With --seed-from &lt;GIT\_DIR&gt;, the repository a git import read, it first fetches the imported commits' objects from it and packs them with a bitmap, so that the first clone after an import writes no object. --prepare-wait zero gives up at once when another refresh holds serve.git's lock, and marks it for that one to run again. It prints {"op": "serve\_prepare", ...} on stdout, and exits 0 when it was skipped or found the lock held. Without the store's memo key, on a store whose main head is no imported commit, it exports nothing and warns: run it as the account the agents and serve run as.

```yaml
title: "swarmops serve"
description: "Serve the stores under --root to stock git: fetch, clone and push over ssh as a forced command, or over smart HTTP as a CGI program."
group: "server"
since: "0.7.0"
mutates: true
identity_required: false
mcp_tool: ""
envelope_op: "serve"
exit_codes: [0]
errors: []
syntax: "swarmops serve [OPTIONS] [GIT_ARGS]..."
```

## Syntax

```text
swarmops serve [OPTIONS] [GIT_ARGS]...
```

## Required arguments

None.

## Optional arguments

The command also takes the [global options](../cli.md#global-options).

### `--root`

Syntax: `--root <DIR>`.

The directory whose subdirectories are the stores served, each by its name.

### `--cgi`

Syntax: `--cgi`.

Serve one CGI request: smart HTTP behind a web server.

### `--user-family`

Syntax: `--user-family <FAMILY>`.

With --cgi: name the caller &lt;FAMILY&gt;/$REMOTE\_USER, the user the web server authenticated.

### `--hook`

Syntax: `--hook <HOOK>`.

Run as a hook git runs inside a push (pre-receive, proc-receive: from the hook files serve writes) or a fetch (pack-objects: from the setting serve gives git). Values: `pre-receive`: git's `pre-receive`: it refuses the whole push while its objects are quarantined; `proc-receive`: git's `proc-receive`: it decides each command of a push; `pack-objects`: git's pack-objects hook: it answers a fetch's pack from the pack cache.

### `GIT_ARGS`

Syntax: `<GIT_ARGS>`.

With --hook pack-objects: the pack-objects line git appends.

### `--stdio`

Syntax: `--stdio`.

Serve the store-to-store sync on stdin and stdout: not served yet.

### `--prepare`

Syntax: `--prepare`.

Make serve.git of the store --repo names ready before a client connects, then exit.

### `--seed-from`

Syntax: `--seed-from <GIT_DIR>`.

With --prepare: the git repository an import read, to take the history's objects from.

### `--prepare-wait`

Syntax: `--prepare-wait <WAIT>`.
Default: `bounded`.

With --prepare: how long to wait for serve.git's lock. Values: `zero`: Not at all: a held lock is marked for its holder to run again; `bounded`: As long as a refresh waits, then E\_BUSY.

## Examples

### CLI

No recorded example yet.

## Return information

`swarmops serve` prints no envelope on stdout, which carries git's protocol: a refusal before git starts is one `ERR` pkt-line there (with `--cgi`, a `Status:` header and its text), and the envelope goes to stderr, as for a command line it refuses.

Envelope `op`: `serve`.

Exit codes:

- `0`: Success.

## Errors

No golden step shows this command failing yet.

Any command that opens a store may also fail with a store error, such as [`E_NOT_A_REPO`](../errors.md#e_not_a_repo), [`E_IO`](../errors.md#e_io) or [`E_STORE_CORRUPT`](../errors.md#e_store_corrupt); a command line the grammar refuses is [`E_INVALID_ARG`](../errors.md#e_invalid_arg) (exit 2).

## See also

- [`swarmops mcp`](mcp.md)
- [CLI reference](../cli.md)
- [Error codes](../errors.md)
