From 25aa001135f8d154f3abcb61f34bd216a1bd4249 Mon Sep 17 00:00:00 2001 From: Steve Cliff Date: Fri, 1 May 2026 00:03:59 +0100 Subject: [PATCH] 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) --- .editorconfig | 18 +++++ .gitea/workflows/ci.yml | 80 +++++++++++++++++++++ .gitignore | 27 +++++++ .golangci.yml | 42 +++++++++++ .pre-commit-config.yaml | 25 +++++++ CONTRIBUTING.md | 30 ++++++++ LICENSE | 120 ++++++++++++++++++++++++++++++++ Makefile | 68 ++++++++++++++++++ README.md | 67 ++++++++++++++++++ cmd/agent/main.go | 33 +++++++++ cmd/server/main.go | 33 +++++++++ deploy/Dockerfile.server | 37 ++++++++++ deploy/docker-compose.yml | 18 +++++ go.mod | 3 + internal/agent/runner/doc.go | 3 + internal/agent/scheduler/doc.go | 4 ++ internal/agent/service/doc.go | 3 + internal/api/doc.go | 4 ++ internal/auth/doc.go | 3 + internal/crypto/doc.go | 3 + internal/restic/doc.go | 3 + internal/server/http/doc.go | 2 + internal/server/ui/doc.go | 3 + internal/server/ws/doc.go | 3 + internal/store/doc.go | 3 + tasks.md | 12 ++-- 26 files changed, 641 insertions(+), 6 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitea/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .golangci.yml create mode 100644 .pre-commit-config.yaml create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 cmd/agent/main.go create mode 100644 cmd/server/main.go create mode 100644 deploy/Dockerfile.server create mode 100644 deploy/docker-compose.yml create mode 100644 go.mod create mode 100644 internal/agent/runner/doc.go create mode 100644 internal/agent/scheduler/doc.go create mode 100644 internal/agent/service/doc.go create mode 100644 internal/api/doc.go create mode 100644 internal/auth/doc.go create mode 100644 internal/crypto/doc.go create mode 100644 internal/restic/doc.go create mode 100644 internal/server/http/doc.go create mode 100644 internal/server/ui/doc.go create mode 100644 internal/server/ws/doc.go create mode 100644 internal/store/doc.go diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3a8717e --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..f005b04 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b5e793 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..45a99f6 --- /dev/null +++ b/.golangci.yml @@ -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 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..4f7542c --- /dev/null +++ b/.pre-commit-config.yaml @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ccc9d39 --- /dev/null +++ b/CONTRIBUTING.md @@ -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. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2d7bb43 --- /dev/null +++ b/LICENSE @@ -0,0 +1,120 @@ +PolyForm Noncommercial License 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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9af7fb1 --- /dev/null +++ b/Makefile @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..4d2ad32 --- /dev/null +++ b/README.md @@ -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. diff --git a/cmd/agent/main.go b/cmd/agent/main.go new file mode 100644 index 0000000..91340b1 --- /dev/null +++ b/cmd/agent/main.go @@ -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") +} diff --git a/cmd/server/main.go b/cmd/server/main.go new file mode 100644 index 0000000..825c106 --- /dev/null +++ b/cmd/server/main.go @@ -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") +} diff --git a/deploy/Dockerfile.server b/deploy/Dockerfile.server new file mode 100644 index 0000000..e541d2a --- /dev/null +++ b/deploy/Dockerfile.server @@ -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"] diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 0000000..f754741 --- /dev/null +++ b/deploy/docker-compose.yml @@ -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 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c2d0cd3 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module gitea.dcglab.co.uk/steve/restic-manager + +go 1.23 diff --git a/internal/agent/runner/doc.go b/internal/agent/runner/doc.go new file mode 100644 index 0000000..438c50a --- /dev/null +++ b/internal/agent/runner/doc.go @@ -0,0 +1,3 @@ +// Package runner spawns restic processes, parses their --json output +// stream, and forwards events to the server over WebSocket. +package runner diff --git a/internal/agent/scheduler/doc.go b/internal/agent/scheduler/doc.go new file mode 100644 index 0000000..525d23f --- /dev/null +++ b/internal/agent/scheduler/doc.go @@ -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 diff --git a/internal/agent/service/doc.go b/internal/agent/service/doc.go new file mode 100644 index 0000000..ad299bd --- /dev/null +++ b/internal/agent/service/doc.go @@ -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 diff --git a/internal/api/doc.go b/internal/api/doc.go new file mode 100644 index 0000000..9679685 --- /dev/null +++ b/internal/api/doc.go @@ -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 diff --git a/internal/auth/doc.go b/internal/auth/doc.go new file mode 100644 index 0000000..4ff4536 --- /dev/null +++ b/internal/auth/doc.go @@ -0,0 +1,3 @@ +// Package auth handles password hashing (argon2id), session cookies, +// CSRF tokens, and bearer-token verification for agents. +package auth diff --git a/internal/crypto/doc.go b/internal/crypto/doc.go new file mode 100644 index 0000000..d295b21 --- /dev/null +++ b/internal/crypto/doc.go @@ -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 diff --git a/internal/restic/doc.go b/internal/restic/doc.go new file mode 100644 index 0000000..2e8026c --- /dev/null +++ b/internal/restic/doc.go @@ -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 diff --git a/internal/server/http/doc.go b/internal/server/http/doc.go new file mode 100644 index 0000000..066fc91 --- /dev/null +++ b/internal/server/http/doc.go @@ -0,0 +1,2 @@ +// Package http hosts the chi-based REST handlers for the control plane. +package http diff --git a/internal/server/ui/doc.go b/internal/server/ui/doc.go new file mode 100644 index 0000000..04a068c --- /dev/null +++ b/internal/server/ui/doc.go @@ -0,0 +1,3 @@ +// Package ui renders the HTMX/Tailwind frontend from server-side +// html/templates. +package ui diff --git a/internal/server/ws/doc.go b/internal/server/ws/doc.go new file mode 100644 index 0000000..2c0b2bc --- /dev/null +++ b/internal/server/ws/doc.go @@ -0,0 +1,3 @@ +// Package ws hosts the WebSocket transport for agent ↔ server and the +// browser-facing live job log stream. +package ws diff --git a/internal/store/doc.go b/internal/store/doc.go new file mode 100644 index 0000000..fce290b --- /dev/null +++ b/internal/store/doc.go @@ -0,0 +1,3 @@ +// Package store is the SQLite persistence layer +// (modernc.org/sqlite, no CGo). +package store diff --git a/tasks.md b/tasks.md index 8199606..0859f52 100644 --- a/tasks.md +++ b/tasks.md @@ -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`) ---