SwarmOps

Page kind: Guide

A store as a remote: clone, push and sync another store#

Describes stable at 05c8644c, ; latest release v0.11.0what changedLast changed View as Markdown

After this page a store uses another store as its remote through swarmops serve: clone, fetch, push and sync over ssh or HTTPS, and know what crosses between them.

You need: SwarmOps 0.12.0 or newer on both hosts; GitHub as a remote; a store served over ssh or over HTTP.

The commands are those of GitHub as a remote: so clone, so fetch, so push, so sync. Only the remote differs: a store that swarmops serve serves over the git wire. This page says what changes when the remote is a store.

Text
 host A                        host B                              host C
 ┌──────────────┐  so push,   ┌──────────────────────────┐  so push,  ┌──────────────┐
 │ store        │  so sync    │ swarmops serve           │  so sync   │ store        │
 │  + git mirror│ ──── git ─▶ │  access.toml: the key's  │ ◀─ git ─── │  + git mirror│
 │              │ ◀── git ─── │  identity, its grants    │ ─── git ─▶ │              │
 └──────────────┘  so fetch   │  the served store        │  so fetch  └──────────────┘
                              └──────────────────────────┘

Each client store keeps a git mirror of the served store and moves packs with its own git; the served store records what each key pushed.

In short#

  • Git commit ids are shared. A commit on the served main has the same id and tree in every store that fetched it; changeset ids are each store's own.
  • The client lands. so sync lands the store's changes onto the remote's head and pushes them by compare-and-swap; the remote does not land a store's push.
  • The key is the identity. The served store knows the caller by its ssh key or web login, never by the client's --as: every agent of one store is one identity there.
  • The op log, agents, workspaces and land keys stay local.

Before you start#

  • A served store. The operator serves it as Serve over ssh or Serve over HTTP describe. Branches other than main and release/<name> need a FORMAT 2 store on both sides.
  • A grant for the key. The served store's access.toml names the key's identity: read for clone and fetch, push on main for so push and so sync of main, push on a branch pattern such as feature/** for that branch (Write access.toml).
  • git 2.39 or later on the client host, and the key or web login git uses for the URL (Credentials).
  • An identity for the commands that move main (clone, sync): --as, else SWARMOPS_AGENT. It names the agent in the client store only.

Clone#

Shell
export SWARMOPS_AGENT=operator/ada
so clone ssh://so-host/acme.git --into ~/.local/state/swarmops/repos/acme
export SWARMOPS_REPO=~/.local/state/swarmops/repos/acme

Over HTTPS the URL is the web server's prefix and the store's name, https://so-host/so/acme.git. clone imports the served main into the new store's main, commit by commit, and records the remote as origin. Each imported changeset keeps the served commit's git id. --branch feature/m follows another served branch; a merge commit on it is imported with both its parents.

Fetch, push and sync#

Shell
so fetch origin                  # the served store's new commits; no store ref moves
so sync status origin            # ahead and behind, offline
so push origin                   # main to the served main, forward only
so push origin feature/x         # a branch of its own (FORMAT 2), created or moved forward
so sync origin                   # land onto the served head, then push

They behave as against GitHub (Fetch, Push, Sync). Against a served store:

  • The remote records the key. Each push is a ref_move in the served store's op log, whose actor is the key's identity (person/ada); the changesets it brought are commit operations with the agent unknown/unknown. No operation of the served store names the client's agent.
  • push sends branches. main, release/<name> and, in a FORMAT 2 store, any branch. A tag, a remote-tracking ref or a target that starts with refs/ is refused before git runs: E_INVALID_ARG with details.reason source_kind or branch_target. main:refs/land/main is one of them: over the git wire the remote does not land a store's push; so sync lands on the client.
  • Nothing is rewritten. rewritten is 0 on a push of a history the store fetched whole.

Several hosts at once#

When two stores sync one served store at once, the second to push finds the remote moved: git's lease refuses it, or the served store answers stale: refs/heads/main moved; fetch first. Either reads as remote_moved, and so sync fetches, lands again onto the new head and pushes, up to --max-attempts times (attempts counts them). Each change is then on the served main once.

A push held up after git read the served head can still lose to a push that moved main past it: the served store then answers non-fast-forward: main moves along first parents; push to refs/land/main, which reads as E_REMOTE_REJECTED declined, not retried. main moved in the client store already (details.main_moved): run so sync origin again, and it lands what is left onto the winner's head (known limitations).

Refusals#

The client seesWhyWhat to do
E_REMOTE_DIVERGED, remote_moved, exit 3The served branch moved since the last fetchso sync origin, or fetch and push again
E_REMOTE_DIVERGED, diverged, exit 3The fetched histories differso sync origin
E_CONFLICT, exit 3A change does not merge onto the served headResolve as When sync stops says
E_REMOTE_REJECTED, declined, exit 5, remote_messages holding E_POLICY_DENIED: person/bob may not push mainThe key's grants do not cover the refPush to a branch the grant covers, or the operator widens it
E_REMOTE_REJECTED, auth, exit 5No grant names the key, or the store has no access.tomlThe operator adds a grant
E_REMOTE_REJECTED, not_found, exit 5No store of that name is servedCheck the name with the operator
E_INVALID_ARG, source_kind or branch_targetA tag or a refs/ targetPush a branch

The served store's own refusals are in Refusals a client sees; the store client reads each by the code at the start of its line.

When a transfer is cut#

A cut clone or fetch is E_REMOTE_UNREACHABLE (exit 4, retryable) and changes no store ref. A cut clone leaves the store it made, with the remote and no changeset of the cut pack (details.created): run the same command again into the same directory, and it imports the whole history. git starts the pack again from its start.

A push whose answer was lost, a killed client or a dropped connection after the served store moved, leaves the remote holding the change. Run the push again:

Shell
so push origin

up_to_date with old different from new: the remote already held the commit, from a push whose answer was lost; the tracking ref moved, nothing was sent. Run once more, old is new.

What crosses, and what does not#

Over the git wire (this page)Native sync (planned)
Who landsso sync: the client lands onto the remote's head, then pushes by compare-and-swap; the remote records a ref_move by the caller. A push to refs/land/<target> is refused before gitThe remote lands every change, keyed
IdsA commit on the remote's main has one git id and one tree in every store that fetched it. A change made in a store keeps its changeset id there; other stores import its commit under ids of their own, and stores are compared by git commit id. A change that loses a race gets a new git commit id and a new changeset id; the old id is dropped locally and only the SwarmOps-Changeset trailer names it. No lineage is recordedNew ids upstream, the origin recorded
Op logDoes not cross. The remote logs commit (agent unknown/unknown) and ref_move by the ssh or web identityCrosses with identity, grant and acting records
AgentKept as the git author (authors.toml); not an agent on the other sideKept
AccessThe remote's access.toml binds the key or web login. All agents of one store are that one identity on the remote; per-agent rules, grants and makers do not reach through a storePer agent
ApprovalsDo not crossCarried
Land keysIn the trailer; so sync drops what the remote holds. The remote does not index them for its own land --key: a keyed land retried against the remote is not a replay thereExactly once by key on the upstream
Workspaces, unlanded commitsLocal. A branch push (feature/x) shares unlanded workLocal
ChecksRun by the client (land-check, ws land --check); the remote runs none on a served pushThe upstream's gate
Resumegit's: a cut clone or fetch starts its pack againNo object fetched twice
OfflineEvery operation but fetch, push and syncThe same

Native sync is on the roadmap.

Limits to know#

  • Tags do not cross. A served store's tags stay in the client's mirror; push sends no tag.
  • No remote landing from a store. refs/land/<target> takes a git client's push (Serve over ssh), not a store's.
  • git push --atomic, push options and annotated tags are not served (Not served).
  • More in the known limitations.

Next#

Next: Serve over ssh: serve a store to other stores and to stock git.

On this page