# Update Server Self-hosted server for publishing application updates. An administrator uploads versioned release artifacts through a web UI, and client applications discover and download allowed updates through a bearer-authenticated HTTP API. The project is designed for small private deployments: one Go application, one SQLite database, local artifact storage, and HTTPS termination by a reverse proxy such as Caddy or Nginx. ## What It Does - Admin web UI at `/admin`. - Project and tag management. - Release artifact upload per project. - Client API key management with project or tag scope rules. - Client API at `/api/v1`. - Private artifact downloads through the application, not through a public file directory. - SQLite migrations applied automatically on startup. - Docker image and Docker Compose deployment files. ## Current Readiness This is a usable MVP. It is ready to run locally or on a small server with Docker, create an admin user, create projects, upload releases, issue API keys, and let clients check and download updates. You do not need to prepare a separate database or run a separate migration step. The container stores SQLite and uploaded artifacts under `/data`, and the app applies migrations on startup. You do need: - Docker with Docker Compose; - `APP_BASE_URL`, `ADMIN_EMAIL`, and `ADMIN_PASSWORD`; - a persistent `/data` volume; - for public HTTPS: a reverse proxy in front of the app. Post-MVP features such as release channels, signed manifests, delta updates, S3-compatible storage, and multi-user administration are not part of the current minimal version. ## Quick Start With Docker Compose 1. Copy the example environment file: ```bash cp deploy/update-server.env.example .env ``` 2. Edit `.env`. For a local first run: ```env APP_BASE_URL=http://127.0.0.1:8080 ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=change-this-before-production ``` For production behind HTTPS: ```env APP_BASE_URL=https://updates.example.com ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=use-a-real-password ``` If `APP_BASE_URL` starts with `https://`, open the admin UI through that HTTPS URL. Secure cookies are enabled automatically for HTTPS base URLs. 3. Build and start the container: ```bash docker compose up -d --build ``` 4. Check that it is alive: ```bash curl http://127.0.0.1:8080/healthz ``` 5. Open the admin UI: ```text http://127.0.0.1:8080/admin ``` After login, create a project, upload a release, create an API key with download permission, and use that key from a client. ## Docker Run Example Docker Compose is the recommended path, but a direct Docker run works too: ```bash docker build -t update-server:latest . docker volume create update-server-data docker run -d \ --name update-server \ --restart unless-stopped \ -p 127.0.0.1:8080:8080 \ -e APP_ADDR=0.0.0.0:8080 \ -e APP_BASE_URL=http://127.0.0.1:8080 \ -e DATA_DIR=/data \ -e SQLITE_PATH=/data/db.sqlite \ -e ARTIFACTS_DIR=/data/artifacts \ -e MIGRATIONS_DIR=/app/migrations \ -e TEMPLATES_DIR=/app/web/templates \ -e STATIC_DIR=/app/web/static \ -e TRUST_PROXY_HEADERS=false \ -e ADMIN_EMAIL=admin@example.com \ -e ADMIN_PASSWORD=change-this-before-production \ -v update-server-data:/data \ update-server:latest ``` ## Proxmox / Caddy Example A simple home-server layout is: ```text Internet -> router port forward 80/443 -> Caddy reverse proxy LXC -> Docker tools LXC running update-server ``` Example: ```text Caddy LXC: 192.168.1.50 Docker tools LXC: 192.168.1.130 Update Server: 192.168.1.130:18080 Public URL: https://updates.esenin.site ``` In this layout, publish the container port on the Docker tools LXC address: ```yaml ports: - "192.168.1.130:18080:8080" ``` Set production environment values: ```env APP_BASE_URL=https://updates.esenin.site ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=use-a-real-password ``` Then point Caddy at the internal service: ```caddyfile updates.esenin.site { encode gzip zstd request_body { max_size 1GB } reverse_proxy 192.168.1.130:18080 } ``` The admin UI and API can live on the same domain: ```text https://updates.esenin.site/admin https://updates.esenin.site/api/v1 ``` ## Client API The API entrypoint is: ```text GET /api/v1 ``` Protected client endpoints require: ```http Authorization: Bearer ``` Common endpoints: ```text GET /api/v1/projects GET /api/v1/projects/{projectSlug}/releases/latest GET /api/v1/releases/{releaseID} GET /api/v1/releases/{releaseID}/download ``` Example: ```bash curl \ -H "Authorization: Bearer upsk_replace_me" \ https://updates.example.com/api/v1/projects ``` Downloads are intentionally private: uploaded files should not be served by Caddy or Nginx as static files. Clients download through the application so API key permissions and project scopes are enforced. ## Data And Backups Persist the whole `/data` directory or Docker volume. It contains: ```text /data/db.sqlite /data/db.sqlite-wal /data/db.sqlite-shm /data/artifacts/... ``` For live backups, prefer SQLite's online backup command for the database and archive `/data/artifacts` separately. See `docs/DEPLOYMENT.md` for a fuller backup and restore flow. ## Development Useful local commands: ```bash just run just test just build ``` Without `just`: ```bash go run ./cmd/server go test ./... go build -o .bin/update-server ./cmd/server ``` Local defaults use `data-dev/` and `http://127.0.0.1:8080` unless overridden by environment variables. ## More Docs - `docs/API.md` - current client API contract, examples, responses, and errors. - `docs/DEPLOYMENT.md` - deployment, reverse proxy, backup, and restore notes. - `deploy/update-server.env.example` - production environment example. - `deploy/Caddyfile.example` - Caddy reverse proxy example. - `deploy/nginx.update-server.conf.example` - Nginx reverse proxy example. - `example/autoupdate.py` - minimal Python auto-update client helper.