Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions packages/gittensory-miner/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,21 @@ docker compose -f docker-compose.miner.yml up -d --build

**Scaling to N parallel workers.** `docker compose -f docker-compose.miner.yml up -d --scale miner=N` gives every replica the **same** `miner-data` volume — and the miner's SQLite ledgers are **not** safe for concurrent access, so N replicas on one volume will contend/corrupt. To run N **isolated** workers, give each its own state: run N separate compose projects (`docker compose -p miner-1 …`, `-p miner-2 …` — `-p` namespaces the volume) or point each at a distinct `GITTENSORY_MINER_CONFIG_DIR` on its own mount. For built-in isolated horizontal scaling, use the Kubernetes StatefulSet in [`k8s/`](../../k8s/) (per-pod volumes).

## Bare-host (systemd, no Docker)

To run the miner continuously on a plain Linux host without Docker, supervise `gittensory-miner loop` — the autonomous discover → attempt → manage daemon (#5135) — with systemd. [`systemd/gittensory-miner.service.example`](../../systemd/gittensory-miner.service.example) is a ready-to-adapt persistent unit; its header carries the full install steps:

```sh
npm install -g @jsonbored/gittensory-miner
gittensory-miner init
sudo cp systemd/gittensory-miner.service.example /etc/systemd/system/gittensory-miner.service
sudo $EDITOR /etc/systemd/system/gittensory-miner.service # set User / WorkingDirectory / ExecStart / secrets
sudo systemctl daemon-reload
sudo systemctl enable --now gittensory-miner.service
```

Because `loop` is a **long-running daemon that schedules its own cycles**, it is a persistent `Type=simple` service (with `Restart=on-failure`) — **not** a oneshot unit driven by a `.timer`, unlike the periodic `gittensory-docker-prune.*.example` hygiene job in [`systemd/`](../../systemd/). Keep `GITHUB_TOKEN` (and any coding-agent credentials) in a root-owned `0600` `EnvironmentFile`, never in the unit file. Follow the loop with `journalctl -u gittensory-miner -f`; `systemctl stop` sends SIGTERM, which the loop handles cleanly at its next kill-switch check.

## Invariants

- Core miner bookkeeping (claims, plans, queues, ledgers) works offline after install.
Expand Down
49 changes: 49 additions & 0 deletions systemd/gittensory-miner.service.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Runs `gittensory-miner loop` -- the autonomous discover -> attempt -> manage supervising daemon (#5135) --
# continuously on a bare host, no Docker required. This is the non-Docker counterpart to DEPLOYMENT.md's
# fleet-mode docker-compose invocation.
#
# WHY A PERSISTENT SERVICE, NOT A .timer: `gittensory-miner loop` is a long-running daemon that schedules its
# own cycles internally (kill-switch check -> run-loop boundary gate -> attempt -> loop-closure -> reentry), so
# it is a Type=simple service supervised by systemd, NOT a oneshot unit fired by a .timer. (Contrast
# gittensory-docker-prune.service.example, which IS a genuine periodic batch and therefore does pair with a
# .timer.) See DEPLOYMENT.md's "Bare-host (systemd, no Docker)" section.
#
# Install (adjust the placeholders below to your host):
# npm install -g @jsonbored/gittensory-miner # puts the `gittensory-miner` bin on PATH
# gittensory-miner init # one-time local state-dir setup
# gittensory-miner doctor # confirm Node/state-dir/SQLite readiness
# sudo cp systemd/gittensory-miner.service.example /etc/systemd/system/gittensory-miner.service
# sudo $EDITOR /etc/systemd/system/gittensory-miner.service # fix User/WorkingDirectory/ExecStart/env
# sudo systemctl daemon-reload
# sudo systemctl enable --now gittensory-miner.service
# journalctl -u gittensory-miner -f # follow the loop's output

[Unit]
Description=Gittensory miner autonomous loop (discover -> attempt -> manage)
# The loop makes GitHub/API calls, so wait for the network to be routable before starting.
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
# REQUIRED: run as a dedicated non-root user that owns the state directory below.
User=gittensory
# REQUIRED: point this at a writable working directory for the miner (repo clones, worktrees, local state).
WorkingDirectory=/var/lib/gittensory-miner
# REQUIRED: absolute path to the globally-installed bin (see `which gittensory-miner`; typically
# /usr/bin or /usr/local/bin, or an nvm/asdf shim path).
ExecStart=/usr/bin/gittensory-miner loop
# A daemon should come back after a transient crash; the loop is idempotent across restarts.
Restart=on-failure
RestartSec=30
# REQUIRED: GITHUB_TOKEN (and any coding-agent credentials) must be present in the service's environment.
# Keep secrets out of this unit file -- use a root-owned 0600 EnvironmentFile instead:
# EnvironmentFile=/etc/gittensory-miner.env
# Optional overrides (see the package README for the full list):
# Environment=GITTENSORY_MINER_CONFIG_DIR=/var/lib/gittensory-miner/config
# Environment=MINER_CODING_AGENT_PROVIDER=claude-code
# Kill switch: `systemctl stop` sends SIGTERM; the loop checks its kill switch each cycle and exits cleanly.
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target