Skip to content

prietus/borgbox

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

borgbox

Minimal HTTP API daemon for managing Borg backup repositories on a self-hosted server. Designed to be driven by a GUI frontend so the end user never has to SSH into the server to create repos, register keys, or see stats.

Install

On your Linux server (the one that will host the repos):

curl -fsSL https://github.com/cprieto/borgbox/releases/latest/download/install.sh | sudo bash

The installer:

  1. Detects OS/arch and downloads the correct binary from the latest release.
  2. Verifies the SHA256 against SHA256SUMS.
  3. Creates a system borg user (or reuses the existing one).
  4. Creates ~borg/repos/ and ~borg/.ssh/authorized_keys with the right permissions.
  5. Writes /etc/borgbox/{token,borgbox.env} (token is random, 40 chars).
  6. Installs and enables borgbox.service under systemd.
  7. Prints the bearer token on stdout — save it for your client app.

Options:

sudo bash install.sh \
  --addr :9999 \
  --home /var/lib/borg \
  --version v0.1.0

Pin a specific version:

curl -fsSL https://github.com/cprieto/borgbox/releases/download/v0.1.0/install.sh | sudo bash

Install from a local dist/ during development:

sudo ./install.sh --dist ./dist

API

Base URL: http://<host>:9999/api/v1 Auth: Authorization: Bearer <token> (all endpoints except /health).

Method Path Auth Description
GET /api/v1/health no Liveness check.
GET /api/v1/info yes Version, free disk, uptime, borg version.
GET /api/v1/system/stats yes Hostname, host uptime, load avg, storage.
GET /api/v1/sessions yes Active borg serve processes (from /proc).
GET /api/v1/repos yes List all repos with size and init state.
POST /api/v1/repos yes Create repo directory and register pubkey.
GET /api/v1/repos/{name} yes Detailed stats for one repo.
DELETE /api/v1/repos/{name} yes Remove directory and authorized_keys entry.
GET /api/v1/repos/{name}/archives yes List archives via borg list --json.
POST /api/v1/repos/{name}/break-lock yes Run borg break-lock on a stuck repo (sync).
POST /api/v1/repos/{name}/check yes Async borg check. Returns job_id.
POST /api/v1/repos/{name}/prune yes Async borg prune with retention policy.
POST /api/v1/repos/{name}/compact yes Async borg compact.
GET /api/v1/jobs yes List recent jobs (in-memory, max 200).
GET /api/v1/jobs/{id} yes Status, exit code and last 200 log lines.

Create a repo

POST /api/v1/repos
Content-Type: application/json
Authorization: Bearer <token>

{
  "name": "mac",
  "pubkey": "ssh-ed25519 AAAAC3... user@host"
}

201 Created on success. 409 Conflict if a repo with that name already has a key registered. 400 for invalid inputs.

After a successful create, the client app can run:

borg init --encryption=repokey-blake2 ssh://borg@<host>/./repos/mac

The daemon never calls borg init itself — it only prepares the server side so the client's first borg init over SSH succeeds.

List archives

GET /api/v1/repos/mac/archives
Authorization: Bearer <token>
X-Borg-Passphrase: <passphrase>      # only for encrypted repos

Returns the array of archives reported by borg list --json. The header is discarded after the call — borgbox never persists passphrases.

Maintenance jobs (async)

check, prune and compact return 202 Accepted with a job_id. Poll /api/v1/jobs/{id} to follow progress.

POST /api/v1/repos/mac/check
Content-Type: application/json
Authorization: Bearer <token>

{ "repair": false, "verify_data": false, "passphrase": "..." }
POST /api/v1/repos/mac/prune
Content-Type: application/json
Authorization: Bearer <token>

{
  "keep_daily":   7,
  "keep_weekly":  4,
  "keep_monthly": 12,
  "keep_yearly":  2,
  "dry_run":      false,
  "passphrase":   "..."
}
POST /api/v1/repos/mac/compact
Content-Type: application/json
Authorization: Bearer <token>

{ "passphrase": "..." }

Job record:

{
  "id": "j_8f2a4c",
  "repo": "mac",
  "kind": "check",
  "status": "running",
  "started_at": "2026-04-13T10:02:17Z",
  "finished_at": "",
  "exit_code": -1,
  "dry_run": false,
  "log_tail": ["Starting repository check", "..."]
}

status is one of queued, running, done, error. exit_code is -1 while the job is still queued or running. The job registry is in-memory only, capped at 200 jobs (oldest finished are evicted first), and is wiped on borgbox restart.

What borgbox is not

  • Not a borg server. borg serve is invoked by sshd via a forced command in authorized_keys, using the system's /usr/bin/borg binary. borgbox only manages the filesystem layout and the key file.
  • No TLS out of the box. Put it behind Tailscale, Caddy, or nginx if it needs to leave the LAN.
  • No multi-user, quotas, or multi-key-per-repo yet.

Security model

  • Runs as the unprivileged borg user, not root.
  • systemd unit enforces ProtectSystem=strict, NoNewPrivileges, ReadWritePaths limited to the repo root and SSH dir.
  • Each key line in authorized_keys has a command="borg serve --restrict-to-repository ..." prefix plus restrict (no PTY, no forwarding, no X11).
  • Repo names are validated against ^[a-z0-9][a-z0-9-]{0,62}$.
  • SSH public keys are validated by type prefix before being written.
  • Delete removes only the line tagged # borgbox:<name>, so manually-added keys are safe.

Building locally

make build    # native binary
make dist     # cross-compile for linux/darwin × amd64/arm64

Cross-compile outputs land in dist/ with a SHA256SUMS file, mirroring what goreleaser produces in CI.

Releasing

Tag the commit and push:

git tag v0.2.0
git push --tags

GitHub Actions runs goreleaser, which compiles all 4 targets, creates the GitHub release, uploads the binaries, writes SHA256SUMS, and attaches install.sh so the curl | sudo bash URL keeps working for the new version.

Configuration

The daemon reads these env vars (also accepted as CLI flags):

Variable Default Description
BORGBOX_ADDR :9999 Listen address.
BORGBOX_REPO_ROOT ~borg/repos Where repo directories live.
BORGBOX_AUTH_KEYS ~borg/.ssh/authorized_keys File to append SSH keys to.
BORGBOX_TOKEN (required) Bearer token. Prefix with @ to read a file.

The installer writes these to /etc/borgbox/borgbox.env.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors