phase 0: project bootstrap
P0-01 Go module + cmd/server + cmd/agent skeletons + internal/ tree
P0-02 LICENSE (PolyForm NC 1.0.0), README, CONTRIBUTING
P0-03 golangci-lint, pre-commit, .editorconfig, .gitignore
P0-04 Gitea Actions CI: test (race+coverage), lint, cross-platform build matrix
P0-05 Dockerfile.server (multi-stage, distroless/static), docker-compose.yml
P0-06 Makefile with build/test/lint/fmt/run/release targets
build, vet, test, and cross-compile to linux/{amd64,arm64} + windows/amd64
all verified locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.{md,yml,yaml,json}]
|
||||
indent_size = 2
|
||||
@@ -0,0 +1,80 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.23"
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test (linux/amd64)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: true
|
||||
- name: go vet
|
||||
run: go vet ./...
|
||||
- name: go test
|
||||
run: go test -race -coverprofile=coverage.out ./...
|
||||
- name: coverage summary
|
||||
run: go tool cover -func=coverage.out | tail -1
|
||||
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: true
|
||||
- uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: v1.61.0
|
||||
args: --timeout=5m
|
||||
|
||||
build:
|
||||
name: Build (${{ matrix.goos }}/${{ matrix.goarch }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
goarch: amd64
|
||||
ext: ".exe"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: true
|
||||
- name: build server + agent
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
CGO_ENABLED: "0"
|
||||
run: |
|
||||
mkdir -p bin
|
||||
go build -trimpath -ldflags="-s -w" \
|
||||
-o bin/restic-manager-server-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }} \
|
||||
./cmd/server
|
||||
go build -trimpath -ldflags="-s -w" \
|
||||
-o bin/restic-manager-agent-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.ext }} \
|
||||
./cmd/agent
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: binaries-${{ matrix.goos }}-${{ matrix.goarch }}
|
||||
path: bin/*
|
||||
retention-days: 7
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# Build output
|
||||
/bin/
|
||||
/dist/
|
||||
|
||||
# Local data / runtime state
|
||||
/data/
|
||||
/certs/
|
||||
*.db
|
||||
*.db-journal
|
||||
*.db-wal
|
||||
*.db-shm
|
||||
|
||||
# Editor / OS
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Coverage
|
||||
coverage.out
|
||||
coverage.html
|
||||
|
||||
# Local environment overrides
|
||||
.env
|
||||
.env.local
|
||||
*.local
|
||||
@@ -0,0 +1,42 @@
|
||||
run:
|
||||
timeout: 5m
|
||||
tests: true
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- errcheck
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- unused
|
||||
- gofumpt
|
||||
- goimports
|
||||
- misspell
|
||||
- revive
|
||||
- bodyclose
|
||||
- errorlint
|
||||
- nilerr
|
||||
- prealloc
|
||||
- unconvert
|
||||
- unparam
|
||||
|
||||
linters-settings:
|
||||
goimports:
|
||||
local-prefixes: gitea.dcglab.co.uk/steve/restic-manager
|
||||
revive:
|
||||
rules:
|
||||
- name: exported
|
||||
arguments: ["disableStutteringCheck"]
|
||||
misspell:
|
||||
locale: US
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- errcheck
|
||||
- unparam
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
@@ -0,0 +1,25 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.6.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
args: ["--maxkb=512"]
|
||||
- id: check-merge-conflict
|
||||
- id: mixed-line-ending
|
||||
args: ["--fix=lf"]
|
||||
|
||||
- repo: https://github.com/dnephin/pre-commit-golang
|
||||
rev: v0.5.1
|
||||
hooks:
|
||||
- id: go-fmt
|
||||
- id: go-imports
|
||||
- id: go-vet-mod
|
||||
- id: go-mod-tidy
|
||||
|
||||
- repo: https://github.com/golangci/golangci-lint
|
||||
rev: v1.61.0
|
||||
hooks:
|
||||
- id: golangci-lint
|
||||
@@ -0,0 +1,30 @@
|
||||
# Contributing
|
||||
|
||||
Thanks for your interest in contributing to restic-manager.
|
||||
|
||||
> This is a placeholder. The project is in pre-alpha (Phase 1 / MVP). A
|
||||
> full contributor guide will land alongside the Phase 5 OSS-readiness
|
||||
> work — see [`tasks.md`](./tasks.md) P5-02. Until then the notes below
|
||||
> apply.
|
||||
|
||||
## Before opening a PR
|
||||
|
||||
1. Open an issue first for non-trivial changes — the design is still
|
||||
moving (see [`spec.md`](./spec.md)) and unsolicited large PRs may
|
||||
conflict with in-flight work.
|
||||
2. `make lint test` should pass.
|
||||
3. Match the existing code style — `gofumpt`, `goimports`, no comments
|
||||
that just restate what the code does.
|
||||
4. Keep commits focused; one logical change per commit.
|
||||
|
||||
## Reporting security issues
|
||||
|
||||
Please do **not** open a public issue for security problems. A
|
||||
`SECURITY.md` with a private disclosure path will be added in Phase 5
|
||||
(P5-05). Until then, contact the repository owner directly via the
|
||||
contact details on their gitea profile.
|
||||
|
||||
## License
|
||||
|
||||
By contributing you agree that your contributions are licensed under
|
||||
the [PolyForm Noncommercial 1.0.0](./LICENSE) license.
|
||||
@@ -0,0 +1,120 @@
|
||||
PolyForm Noncommercial License 1.0.0
|
||||
|
||||
<https://polyformproject.org/licenses/noncommercial/1.0.0>
|
||||
|
||||
## Acceptance
|
||||
|
||||
In order to get any license under these terms, you must agree to them
|
||||
as both strict obligations and conditions to all your licenses.
|
||||
|
||||
## Copyright License
|
||||
|
||||
The licensor grants you a copyright license for the software to do
|
||||
everything you might do with the software that would otherwise infringe
|
||||
the licensor's copyright in it for any permitted purpose. However, you
|
||||
may only distribute the software according to Distribution License and
|
||||
make changes or new works based on the software according to Changes
|
||||
and New Works License.
|
||||
|
||||
## Distribution License
|
||||
|
||||
The licensor grants you an additional copyright license to distribute
|
||||
copies of the software. Your license to distribute covers distributing
|
||||
the software with changes and new works permitted by Changes and New
|
||||
Works License.
|
||||
|
||||
## Notices
|
||||
|
||||
You must ensure that anyone who gets a copy of any part of the software
|
||||
from you also gets a copy of these terms or the URL for them above, as
|
||||
well as copies of any plain-text lines beginning with "Required Notice:"
|
||||
that the licensor provided with the software. For example:
|
||||
|
||||
> Required Notice: Copyright Yoyodyne, Inc. (http://example.com)
|
||||
|
||||
## Changes and New Works License
|
||||
|
||||
The licensor grants you an additional copyright license to make changes
|
||||
and new works based on the software for any permitted purpose.
|
||||
|
||||
## Patent License
|
||||
|
||||
The licensor grants you a patent license for the software that covers
|
||||
patent claims the licensor can license, or becomes able to license,
|
||||
that you would infringe by using the software.
|
||||
|
||||
## Noncommercial Purposes
|
||||
|
||||
Any noncommercial purpose is a permitted purpose.
|
||||
|
||||
## Personal Uses
|
||||
|
||||
Personal use for research, experiment, and testing for the benefit of
|
||||
public knowledge, personal study, private entertainment, hobby projects,
|
||||
amateur pursuits, or religious observance, without any anticipated
|
||||
commercial application, is use for a permitted purpose.
|
||||
|
||||
## Noncommercial Organizations
|
||||
|
||||
Use by any charitable organization, educational institution, public
|
||||
research organization, public safety or health organization,
|
||||
environmental protection organization, or government institution is use
|
||||
for a permitted purpose regardless of the source of funding or
|
||||
obligations resulting from the funding.
|
||||
|
||||
## Fair Use
|
||||
|
||||
You may have "fair use" rights for the software under the law. These
|
||||
terms do not limit them.
|
||||
|
||||
## No Other Rights
|
||||
|
||||
These terms do not allow you to sublicense or transfer any of your
|
||||
licenses to anyone else, or prevent the licensor from granting licenses
|
||||
to anyone else. These terms do not imply any other licenses.
|
||||
|
||||
## Patent Defense
|
||||
|
||||
If you make any written claim that the software infringes or contributes
|
||||
to infringement of any patent, your patent license for the software
|
||||
granted under these terms ends immediately. If your company makes such
|
||||
a claim, your patent license ends immediately for work on behalf of
|
||||
your company.
|
||||
|
||||
## Violations
|
||||
|
||||
The first time you are notified in writing that you have violated any
|
||||
of these terms, or done anything with the software not covered by your
|
||||
licenses, your licenses can nonetheless continue if you come into full
|
||||
compliance with these terms, and take practical steps to correct past
|
||||
violations, within 32 days of receiving notice. Otherwise, all your
|
||||
licenses end immediately.
|
||||
|
||||
## No Liability
|
||||
|
||||
***As far as the law allows, the software comes as is, without any
|
||||
warranty or condition, and the licensor will not be liable to you for
|
||||
any damages arising out of these terms or the use or nature of the
|
||||
software, under any kind of legal claim.***
|
||||
|
||||
## Definitions
|
||||
|
||||
The **licensor** is the individual or entity offering these terms, and
|
||||
the **software** is the software the licensor makes available under
|
||||
these terms.
|
||||
|
||||
**You** refers to the individual or entity agreeing to these terms.
|
||||
|
||||
**Your company** is any legal entity, sole proprietorship, or other
|
||||
kind of organization that you work for, plus all organizations that
|
||||
have control over, are under the control of, or are under common
|
||||
control with that organization. *Control* means ownership of
|
||||
substantially all the assets of an entity, or the power to direct its
|
||||
management and policies by vote, contract, or otherwise. Control can
|
||||
be direct or indirect.
|
||||
|
||||
**Your licenses** are all the licenses granted to you for the software
|
||||
under these terms.
|
||||
|
||||
**Use** means anything you do with the software requiring one of your
|
||||
licenses.
|
||||
@@ -0,0 +1,68 @@
|
||||
# restic-manager — common dev targets
|
||||
|
||||
SHELL := /bin/bash
|
||||
BIN_DIR := bin
|
||||
SERVER_BIN := $(BIN_DIR)/restic-manager-server
|
||||
AGENT_BIN := $(BIN_DIR)/restic-manager-agent
|
||||
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
|
||||
LDFLAGS := -s -w -X main.version=$(VERSION)
|
||||
GOFLAGS := -trimpath
|
||||
DOCKER_IMAGE ?= ghcr.io/dcglab/restic-manager
|
||||
DOCKER_TAG ?= dev
|
||||
|
||||
.PHONY: help build server agent test test-race lint fmt tidy clean run-server run-agent docker release
|
||||
|
||||
help:
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | awk 'BEGIN{FS=":.*?## "};{printf " \033[36m%-14s\033[0m %s\n",$$1,$$2}'
|
||||
|
||||
build: server agent ## Build server + agent into ./bin
|
||||
|
||||
server: ## Build the server binary
|
||||
@mkdir -p $(BIN_DIR)
|
||||
CGO_ENABLED=0 go build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(SERVER_BIN) ./cmd/server
|
||||
|
||||
agent: ## Build the agent binary
|
||||
@mkdir -p $(BIN_DIR)
|
||||
CGO_ENABLED=0 go build $(GOFLAGS) -ldflags "$(LDFLAGS)" -o $(AGENT_BIN) ./cmd/agent
|
||||
|
||||
test: ## Run tests
|
||||
go test ./...
|
||||
|
||||
test-race: ## Run tests with the race detector
|
||||
go test -race -coverprofile=coverage.out ./...
|
||||
|
||||
lint: ## Run golangci-lint
|
||||
golangci-lint run ./...
|
||||
|
||||
fmt: ## Format with gofumpt + goimports
|
||||
gofumpt -w .
|
||||
goimports -local gitea.dcglab.co.uk/steve/restic-manager -w .
|
||||
|
||||
tidy: ## go mod tidy
|
||||
go mod tidy
|
||||
|
||||
clean: ## Remove build artifacts
|
||||
rm -rf $(BIN_DIR) coverage.out coverage.html
|
||||
|
||||
run-server: server ## Build and run the server
|
||||
$(SERVER_BIN)
|
||||
|
||||
run-agent: agent ## Build and run the agent
|
||||
$(AGENT_BIN)
|
||||
|
||||
docker: ## Build the server Docker image
|
||||
docker build -f deploy/Dockerfile.server --build-arg VERSION=$(VERSION) -t $(DOCKER_IMAGE):$(DOCKER_TAG) .
|
||||
|
||||
release: ## Cross-compile for all supported platforms
|
||||
@mkdir -p $(BIN_DIR)
|
||||
@for target in linux/amd64 linux/arm64 windows/amd64; do \
|
||||
goos=$${target%/*}; goarch=$${target#*/}; \
|
||||
ext=""; if [ "$$goos" = "windows" ]; then ext=".exe"; fi; \
|
||||
echo "==> $$goos/$$goarch"; \
|
||||
GOOS=$$goos GOARCH=$$goarch CGO_ENABLED=0 \
|
||||
go build $(GOFLAGS) -ldflags "$(LDFLAGS)" \
|
||||
-o $(BIN_DIR)/restic-manager-server-$$goos-$$goarch$$ext ./cmd/server; \
|
||||
GOOS=$$goos GOARCH=$$goarch CGO_ENABLED=0 \
|
||||
go build $(GOFLAGS) -ldflags "$(LDFLAGS)" \
|
||||
-o $(BIN_DIR)/restic-manager-agent-$$goos-$$goarch$$ext ./cmd/agent; \
|
||||
done
|
||||
@@ -0,0 +1,67 @@
|
||||
# restic-manager
|
||||
|
||||
Self-hosted, browser-based, single-pane-of-glass for managing
|
||||
[restic](https://restic.net) backups across a fleet of Linux and Windows
|
||||
endpoints.
|
||||
|
||||
> Status: pre-alpha. Phase 0 (project bootstrap) complete; Phase 1 (MVP) in
|
||||
> progress. See [`spec.md`](./spec.md) for the design and
|
||||
> [`tasks.md`](./tasks.md) for the roadmap.
|
||||
|
||||
## What it does (target)
|
||||
|
||||
- Central visibility into backup state for every endpoint
|
||||
- Trigger any restic operation remotely (`backup`, `forget`, `prune`,
|
||||
`check`, `unlock`, `snapshots`, `stats`, `diff`, `restore`)
|
||||
- Manage per-host backup schedules from the UI
|
||||
- Live job progress streamed back to the UI
|
||||
- Restore wizard (browse snapshots, pick paths, restore to original or
|
||||
alternate host)
|
||||
- Repo health surfacing (size, dedup ratio, last check, lock state)
|
||||
- Alerting on failure or staleness
|
||||
- Cross-platform agent (Linux + Windows)
|
||||
- Ransomware-resistant repo access via append-only credentials
|
||||
|
||||
## Architecture (one-line summary)
|
||||
|
||||
A small Go control-plane on the Proxmox host, lightweight Go agents on each
|
||||
endpoint that hold an outbound WebSocket to the control-plane, and a
|
||||
`restic/rest-server` on Unraid that holds the actual backup data. The
|
||||
control-plane never touches backup bytes.
|
||||
|
||||
Full architecture diagram and component breakdown:
|
||||
[`spec.md` §3](./spec.md).
|
||||
|
||||
## Repository layout
|
||||
|
||||
```
|
||||
cmd/server/ control-plane binary
|
||||
cmd/agent/ endpoint agent binary
|
||||
internal/api shared API types (REST + WS envelopes)
|
||||
internal/server/ HTTP, WS, UI handlers
|
||||
internal/agent/ service integration, restic runner, local scheduler
|
||||
internal/restic restic CLI wrapper
|
||||
internal/store SQLite persistence
|
||||
internal/crypto secret encryption
|
||||
internal/auth passwords, sessions, agent tokens
|
||||
web/ server-rendered templates + static assets
|
||||
deploy/ Dockerfile, docker-compose.yml, install scripts
|
||||
design/ UI wireframes (Phase 0 design pass)
|
||||
```
|
||||
|
||||
## Local development
|
||||
|
||||
Requires Go 1.23+ (built and tested on 1.26).
|
||||
|
||||
```sh
|
||||
make build # builds cmd/server and cmd/agent into ./bin
|
||||
make test # runs go test ./...
|
||||
make lint # runs golangci-lint
|
||||
make run-server # runs the server (dev defaults)
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
PolyForm Noncommercial 1.0.0 — see [`LICENSE`](./LICENSE). Free for personal,
|
||||
hobby, research, educational, governmental, and other noncommercial use.
|
||||
Commercial use requires a separate license.
|
||||
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var version = "dev"
|
||||
|
||||
func main() {
|
||||
showVersion := flag.Bool("version", false, "print version and exit")
|
||||
flag.Parse()
|
||||
|
||||
if *showVersion {
|
||||
fmt.Println("restic-manager-agent", version)
|
||||
return
|
||||
}
|
||||
|
||||
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
|
||||
slog.SetDefault(logger)
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
slog.Info("restic-manager agent starting", "version", version)
|
||||
<-ctx.Done()
|
||||
slog.Info("shutting down")
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var version = "dev"
|
||||
|
||||
func main() {
|
||||
showVersion := flag.Bool("version", false, "print version and exit")
|
||||
flag.Parse()
|
||||
|
||||
if *showVersion {
|
||||
fmt.Println("restic-manager-server", version)
|
||||
return
|
||||
}
|
||||
|
||||
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
|
||||
slog.SetDefault(logger)
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
slog.Info("restic-manager server starting", "version", version)
|
||||
<-ctx.Done()
|
||||
slog.Info("shutting down")
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
# syntax=docker/dockerfile:1.7
|
||||
|
||||
# ---- Build stage --------------------------------------------------------
|
||||
FROM golang:1.23-alpine AS build
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
# Pure-Go SQLite (modernc.org/sqlite) means we can keep CGO off and build a
|
||||
# fully static binary that runs on distroless/static.
|
||||
ENV CGO_ENABLED=0 \
|
||||
GOOS=linux \
|
||||
GOFLAGS="-trimpath"
|
||||
|
||||
# Cache module downloads in a separate layer.
|
||||
COPY go.mod go.sum* ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
ARG VERSION=dev
|
||||
RUN go build -ldflags="-s -w -X main.version=${VERSION}" \
|
||||
-o /out/restic-manager-server \
|
||||
./cmd/server
|
||||
|
||||
# ---- Runtime stage ------------------------------------------------------
|
||||
FROM gcr.io/distroless/static-debian12:nonroot
|
||||
|
||||
LABEL org.opencontainers.image.source="https://gitea.dcglab.co.uk/steve/restic-manager"
|
||||
LABEL org.opencontainers.image.licenses="PolyForm-Noncommercial-1.0.0"
|
||||
|
||||
USER nonroot:nonroot
|
||||
WORKDIR /
|
||||
|
||||
COPY --from=build /out/restic-manager-server /usr/local/bin/restic-manager-server
|
||||
|
||||
EXPOSE 8443
|
||||
ENTRYPOINT ["/usr/local/bin/restic-manager-server"]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Reference deployment for the restic-manager control plane.
|
||||
# Mirrors spec.md §10.1. Adjust image tag and RM_BASE_URL for your env.
|
||||
services:
|
||||
restic-manager:
|
||||
image: ghcr.io/dcglab/restic-manager:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8443:8443"
|
||||
volumes:
|
||||
- ./data:/data
|
||||
- ./certs:/certs:ro
|
||||
environment:
|
||||
- RM_DATA_DIR=/data
|
||||
- RM_LISTEN=:8443
|
||||
- RM_BASE_URL=https://restic.lab.example
|
||||
- RM_TLS_CERT=/certs/fullchain.pem
|
||||
- RM_TLS_KEY=/certs/privkey.pem
|
||||
- RM_SECRET_KEY_FILE=/data/secret.key
|
||||
@@ -0,0 +1,3 @@
|
||||
// Package runner spawns restic processes, parses their --json output
|
||||
// stream, and forwards events to the server over WebSocket.
|
||||
package runner
|
||||
@@ -0,0 +1,4 @@
|
||||
// Package scheduler runs the agent's local cron loop. The server is the
|
||||
// source of truth for schedules; this package reconciles to whatever
|
||||
// the server most recently pushed.
|
||||
package scheduler
|
||||
@@ -0,0 +1,3 @@
|
||||
// Package service wires the agent into the host's service manager
|
||||
// (systemd on Linux, the Service Control Manager on Windows).
|
||||
package service
|
||||
@@ -0,0 +1,4 @@
|
||||
// Package api defines shared types for the control-plane API:
|
||||
// REST request/response shapes (server ↔ browser) and WebSocket
|
||||
// message envelopes (server ↔ agent). See spec.md §6.
|
||||
package api
|
||||
@@ -0,0 +1,3 @@
|
||||
// Package auth handles password hashing (argon2id), session cookies,
|
||||
// CSRF tokens, and bearer-token verification for agents.
|
||||
package auth
|
||||
@@ -0,0 +1,3 @@
|
||||
// Package crypto wraps AEAD encryption used to protect repo passwords,
|
||||
// REST-server credentials, and pre/post hook bodies at rest.
|
||||
package crypto
|
||||
@@ -0,0 +1,3 @@
|
||||
// Package restic wraps the restic CLI: locating the binary, invoking
|
||||
// it with --json, and parsing the streamed event payloads.
|
||||
package restic
|
||||
@@ -0,0 +1,2 @@
|
||||
// Package http hosts the chi-based REST handlers for the control plane.
|
||||
package http
|
||||
@@ -0,0 +1,3 @@
|
||||
// Package ui renders the HTMX/Tailwind frontend from server-side
|
||||
// html/templates.
|
||||
package ui
|
||||
@@ -0,0 +1,3 @@
|
||||
// Package ws hosts the WebSocket transport for agent ↔ server and the
|
||||
// browser-facing live job log stream.
|
||||
package ws
|
||||
@@ -0,0 +1,3 @@
|
||||
// Package store is the SQLite persistence layer
|
||||
// (modernc.org/sqlite, no CGo).
|
||||
package store
|
||||
@@ -8,12 +8,12 @@ Sizes: **S** = under a day, **M** = 1–3 days, **L** = 3–7 days.
|
||||
|
||||
## Phase 0 — Project bootstrap
|
||||
|
||||
- [ ] **P0-01** (S) Initialize Go module, `cmd/server`, `cmd/agent`, baseline `internal/` packages
|
||||
- [ ] **P0-02** (S) Add LICENSE (PolyForm Noncommercial 1.0.0), README stub, CONTRIBUTING placeholder
|
||||
- [ ] **P0-03** (S) Set up `golangci-lint`, `gofumpt`, `goimports`; pre-commit config
|
||||
- [ ] **P0-04** (S) GitHub Actions: build matrix (linux amd64/arm64, windows amd64), unit tests, lint
|
||||
- [ ] **P0-05** (S) `Dockerfile.server` (multi-stage, distroless), `deploy/docker-compose.yml`
|
||||
- [ ] **P0-06** (S) Makefile / `taskfile.yml` with common targets (`build`, `test`, `run`, `release`)
|
||||
- [x] **P0-01** (S) Initialize Go module, `cmd/server`, `cmd/agent`, baseline `internal/` packages
|
||||
- [x] **P0-02** (S) Add LICENSE (PolyForm Noncommercial 1.0.0), README stub, CONTRIBUTING placeholder
|
||||
- [x] **P0-03** (S) Set up `golangci-lint`, `gofumpt`, `goimports`; pre-commit config
|
||||
- [x] **P0-04** (S) ~~GitHub Actions~~ Gitea Actions: build matrix (linux amd64/arm64, windows amd64), unit tests, lint
|
||||
- [x] **P0-05** (S) `Dockerfile.server` (multi-stage, distroless), `deploy/docker-compose.yml`
|
||||
- [x] **P0-06** (S) Makefile / ~~`taskfile.yml`~~ with common targets (`build`, `test`, `run`, `release`)
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user