Merge pull request 'chore(release): prepare RC packaging and dev compose' (#38) from codex/block-11-rc-packaging into main
Reviewed-on: #38 Reviewed-by: tony <1+tony@noreply.localhost>
This commit was merged in pull request #38.
This commit is contained in:
+3
-2
@@ -1,7 +1,8 @@
|
||||
# Public deployment settings only. Internal paths and secrets are automatic.
|
||||
DOGAMA_VERSION=latest
|
||||
# Pin a published release before production deployment.
|
||||
DOGAMA_VERSION=0.2.1
|
||||
DOGAMA_HTTP_PORT=8080
|
||||
TZ=UTC
|
||||
TZ=Europe/Paris
|
||||
DOGAMA_DATA_PATH=./data
|
||||
DOGAMA_SERVERS_PATH=./servers
|
||||
DOGAMA_BACKUPS_PATH=./backups
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# Development Caddy PKI and configuration are named Docker volumes. Ignore
|
||||
# equivalent local directories if a developer exports them for inspection.
|
||||
deploy/dev/caddy-data/
|
||||
deploy/dev/caddy-config/
|
||||
@@ -23,7 +23,7 @@ The repository currently includes the official banner above but no maintained pr
|
||||
|
||||
## Installation
|
||||
|
||||
Install Docker Engine with the Compose plugin on a Linux host. Create a directory and save the repository's canonical [`compose.yaml`](compose.yaml) there; optionally copy [`.env.example`](.env.example) to `.env`. A complete start is then:
|
||||
Install Docker Engine with the Compose plugin on a Linux host. Create a directory and save the repository's canonical [`compose.yaml`](compose.yaml) there; optionally copy [`.env.example`](.env.example) to `.env` and set durable host paths. A complete start is then:
|
||||
|
||||
```sh
|
||||
mkdir dogama
|
||||
@@ -35,6 +35,8 @@ docker compose up -d
|
||||
The default web address is `http://HOST:8080`. DoGaMa generates its internal agent token and encryption key automatically on first start; do not create or add them to `.env`.
|
||||
The repository `compose.yaml` is the single source of truth for service, volume, network and hardening settings. No separate initialization command, host user, internal UID/GID or secret preparation is required.
|
||||
|
||||
For production installation, reverse-proxy and recovery procedures, see [deployment and release](docs/operations/deployment-and-release.md). For local HTTPS development and browser testing, use the separate [development Compose guide](docs/contributing/development.md); production Compose does not include or require Caddy.
|
||||
|
||||
## Initial setup
|
||||
|
||||
Open `/setup`, create the first administrator with a valid email address and preferred language, then sign in. Configure global labels and notification channels from Settings. Create game instances only after checking the host paths, ports and backup policy shown by the deployment preview.
|
||||
@@ -83,7 +85,7 @@ Managed game ports are selected per instance from validated templates.
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
|---|---|---|
|
||||
| `DOGAMA_VERSION` | `latest` | Image version; pin a release in production |
|
||||
| `DOGAMA_VERSION` | `0.2.1` | Image version; pin a published release in production |
|
||||
| `DOGAMA_HTTP_PORT` | `8080` | Published web port |
|
||||
| `TZ` | `UTC` | IANA container timezone (also used for Audit display, e.g. `Europe/Paris`) |
|
||||
| `DOGAMA_DATA_PATH` | `./data` | Host path for DoGaMa data |
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Development and local integration-test overlay. Use with compose.yaml; it is
|
||||
# deliberately not a production reverse-proxy configuration.
|
||||
services:
|
||||
dogama:
|
||||
build:
|
||||
context: .
|
||||
target: dogama
|
||||
pull_policy: build
|
||||
ports: !reset []
|
||||
|
||||
agent:
|
||||
build:
|
||||
context: .
|
||||
target: dogama-agent
|
||||
pull_policy: build
|
||||
|
||||
caddy:
|
||||
image: caddy:2.10.2-alpine@sha256:4c6e91c6ed0e2fa03efd5b44747b625fec79bc9cd06ac5235a779726618e530d
|
||||
restart: unless-stopped
|
||||
read_only: true
|
||||
ports:
|
||||
- "${DOGAMA_HTTPS_PORT:-443}:8443"
|
||||
volumes:
|
||||
- ./deploy/dev/Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
tmpfs:
|
||||
- /tmp:rw,noexec,nosuid,nodev,size=16m
|
||||
networks:
|
||||
- dogama
|
||||
depends_on:
|
||||
dogama:
|
||||
condition: service_started
|
||||
|
||||
volumes:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
dogama:
|
||||
image: git.zaynet.fr/dogama/dogama:${DOGAMA_VERSION:-latest}
|
||||
image: git.zaynet.fr/dogama/dogama:${DOGAMA_VERSION:-0.2.1}
|
||||
restart: unless-stopped
|
||||
read_only: true
|
||||
ports:
|
||||
@@ -27,7 +27,7 @@ services:
|
||||
condition: service_started
|
||||
|
||||
agent:
|
||||
image: git.zaynet.fr/dogama/dogama-agent:${DOGAMA_VERSION:-latest}
|
||||
image: git.zaynet.fr/dogama/dogama-agent:${DOGAMA_VERSION:-0.2.1}
|
||||
restart: unless-stopped
|
||||
read_only: true
|
||||
environment:
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
# Use unprivileged listener ports inside the container. Docker publishes host
|
||||
# 443 to this internal HTTPS listener; the HTTP listener is intentionally unexposed.
|
||||
http_port 8080
|
||||
https_port 8443
|
||||
skip_install_trust
|
||||
}
|
||||
|
||||
dogama.lan {
|
||||
tls internal
|
||||
reverse_proxy dogama:8080
|
||||
}
|
||||
@@ -5,7 +5,7 @@ Read this compact operational baseline before starting a milestone. Open detaile
|
||||
## Baseline
|
||||
|
||||
- Current reference: post-v0.1.1 release-gate correction based on tagged baseline `88a45c7`.
|
||||
- SQLite is initialized directly from one embedded current schema. Development databases from earlier revisions are intentionally unsupported until versioned production migrations are introduced.
|
||||
- SQLite is initialized directly from one embedded current schema and can be reopened safely after first initialization. Development databases from earlier revisions are intentionally unsupported until versioned production migrations are introduced.
|
||||
- Roadmap milestones 1-10 are implemented; this is the V1 feature baseline.
|
||||
|
||||
## Architecture
|
||||
@@ -41,6 +41,7 @@ Read this compact operational baseline before starting a milestone. Open detaile
|
||||
- Linux black-box bootstrap/authentication E2E coverage plus a documented disposable-Docker V1 release verification matrix.
|
||||
- Deterministic Linux `amd64`/`arm64` archives with embedded build identity, SPDX module SBOM and SHA-256 checksums.
|
||||
- Minimal production Compose containing only `dogama` and `agent`; `docker compose up -d` is the complete first-start workflow with no initializer, setup command, host UID/GID preparation or user-managed internal secrets.
|
||||
- `compose-dev.yml` is a local-only overlay for the production Compose: it builds the two local image targets and adds Caddy 2.10.2 with `tls internal` at `https://dogama.lan`. Caddy PKI and configuration are isolated in development named volumes; production imposes no reverse proxy.
|
||||
- Service-owned persistent secrets: the agent atomically creates and validates its mode-`0640` shared token in the internal `agent_auth` volume; the application mounts only that secret directory read-only and independently creates and validates its mode-`0600` master key below the application data path. The application tolerates concurrent first start by waiting up to 60 seconds for the token and authenticated agent health.
|
||||
- The agent token is exactly 32 opaque random bytes. Readers preserve terminal carriage-return and newline byte values instead of treating the secret as text.
|
||||
- Two service networks: an administrator-named application/reverse-proxy network plus a private Compose control network. The agent safely ensures the fixed `DOGAMA_GAMES_NETWORK` exists and applies it to every game-container create or replacement; it is not caller-selectable through the lifecycle API.
|
||||
@@ -66,7 +67,7 @@ Read this compact operational baseline before starting a milestone. Open detaile
|
||||
- The application generates and retains its 32-byte master key outside SQLite; the agent never receives it. Ciphertext is authenticated AES-GCM and secrets are never returned by list APIs.
|
||||
- `agent_state` and `agent_auth` remain durable security state: the former stores the MAC-protected instance-to-container registry and the latter stores its shared authentication key. Losing either must never trigger automatic adoption.
|
||||
- The canonical public deployment has exactly two services. Setup sidecars, init containers, host bootstrap scripts and user-managed internal secrets require an explicit architectural decision.
|
||||
- `compose.yaml` is the sole canonical Compose definition; documentation references it instead of duplicating it.
|
||||
- `compose.yaml` is the canonical minimal production Compose definition; `compose-dev.yml` is its intentionally small local HTTPS/testing overlay. Documentation references the canonical files instead of duplicating either configuration.
|
||||
- `DOGAMA_NETWORK` names the application-facing network. `DOGAMA_GAMES_NETWORK` names the single agent-approved network for all created and recreated game containers; API input cannot override either bootstrap boundary.
|
||||
- DoGaMa services never recursively change ownership of application, game-server or backup roots.
|
||||
- Notification delivery attempts are capped at five with exponential minute-scale backoff and never determine the originating operation result.
|
||||
|
||||
@@ -36,6 +36,39 @@ go run ./cmd/dogama
|
||||
|
||||
Browser sessions always use `Secure`, `HttpOnly`, and `SameSite=Strict` cookies. Place the application behind a trusted TLS reverse proxy for browser use, including development environments. The application does not accept forwarded client addresses as authoritative for authentication throttling.
|
||||
|
||||
## Local HTTPS Compose environment
|
||||
|
||||
Use the root `compose.yaml` together with [`compose-dev.yml`](../../compose-dev.yml) for local development, manual UI checks, Playwright and integration checks:
|
||||
|
||||
```sh
|
||||
docker compose -f compose.yaml -f compose-dev.yml up -d
|
||||
docker compose -f compose.yaml -f compose-dev.yml ps
|
||||
```
|
||||
|
||||
The development overlay builds the two local image targets and adds Caddy 2.10.2 only. It removes the direct development publication of the DoGaMa HTTP port and publishes Caddy on `https://dogama.lan` (host TCP 443 by default; set `DOGAMA_HTTPS_PORT` only when 443 is unavailable). Production does not include Caddy and remains compatible with an administrator-operated reverse proxy.
|
||||
|
||||
[`deploy/dev/Caddyfile`](../../deploy/dev/Caddyfile) uses `tls internal`, so Caddy automatically creates a development-only local CA and certificate for `dogama.lan`. Its `/data` and `/config` directories use the separate `caddy_data` and `caddy_config` named volumes. Neither the CA nor a private key is committed. Caddy receives the app-facing `dogama` network only; it has no Docker socket or access to the private `control` network. Caddy provides the normal `Host`, `X-Forwarded-For` and `X-Forwarded-Proto` proxy request context; DoGaMa uses the scheme and host for its explicit web-access policy and secure cookies, but does not treat forwarded client addresses as authentication authority.
|
||||
|
||||
`dogama.lan` must resolve to the DevStation address from the browser machine. Use local DNS, or add an entry such as the following on each development client when no LAN DNS exists (replace the address):
|
||||
|
||||
```text
|
||||
192.0.2.10 dogama.lan
|
||||
```
|
||||
|
||||
Verify it before browser testing with `getent hosts dogama.lan`. Do not substitute a `localhost` hostname.
|
||||
|
||||
The Caddy CA is not automatically trusted by client machines. To trust it for an interactive browser, copy its public root certificate after Caddy has started and install it in the operating system/browser trust store. For example on Debian/Ubuntu:
|
||||
|
||||
```sh
|
||||
container=$(docker compose -f compose.yaml -f compose-dev.yml ps -q caddy)
|
||||
docker cp "$container:/data/caddy/pki/authorities/local/root.crt" ./dogama-caddy-root.crt
|
||||
sudo install -m 0644 ./dogama-caddy-root.crt /usr/local/share/ca-certificates/dogama-caddy-local.crt
|
||||
sudo update-ca-certificates
|
||||
rm ./dogama-caddy-root.crt
|
||||
```
|
||||
|
||||
Do not commit the copied certificate. Automated Playwright tests may instead use `ignoreHTTPSErrors: true`; that exception is limited to local tests and must never be used for production verification. DevStation Chromium is available at `/usr/bin/chromium-browser`. To stop the stack while retaining data, run `docker compose -f compose.yaml -f compose-dev.yml down`; adding `-v` also deletes the Compose named volumes, including the Caddy PKI and the agent security state.
|
||||
|
||||
The restricted agent is a separate binary:
|
||||
|
||||
```sh
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Production deployment
|
||||
|
||||
DoGaMa V1 targets one Linux Docker host with the Compose plugin. Use the root `compose.yaml`, place the public HTTP service behind a trusted TLS reverse proxy, and never publish the agent port.
|
||||
DoGaMa V1 targets one Linux Docker host with the Compose plugin. The root `compose.yaml` deliberately contains only `dogama` and `agent`; it does not include a reverse proxy. Place its published HTTP service behind the trusted Traefik, Caddy, Nginx, HAProxy, NAS proxy, or other reverse proxy operated by the administrator. Never publish the agent port.
|
||||
|
||||
```sh
|
||||
cp .env.example .env
|
||||
@@ -21,14 +21,22 @@ Only host-side storage locations, image version, web port, timezone and the two
|
||||
|
||||
Both services use root inside their container namespaces so Docker-created bind directories and ordinary administrator-selected paths work without knowledge of an image-specific UID/GID. They keep read-only root filesystems, `no-new-privileges`, an empty Linux capability set and a `0077` process umask for new files. The main application never receives the Docker socket. DoGaMa creates only directories it needs below the configured roots and never performs an automatic recursive `chown` of application, server or backup data. Game-container UID/GID selection remains a separate per-instance setting.
|
||||
|
||||
For NAS or server-style paths, set ordinary writable locations in `.env`, for example `/srv/apps/dogama/data`, `/srv/games` and `/srv/backups`, then run `docker compose up -d`. No `/etc` or host `/var/lib` setup, system user, systemd unit or bootstrap script is required.
|
||||
For NAS or server-style paths, set ordinary writable locations in `.env`, for example `/srv/apps/dogama/data`, `/srv/games` and `/srv/backups`, then run `docker compose up -d`. `/var/lib/dogama/templates` is inside `DOGAMA_DATA_PATH`, so it persists as `${DOGAMA_DATA_PATH}/templates` and stays available to Catalog Scan. No `/etc` or host `/var/lib` setup, system user, systemd unit or bootstrap script is required.
|
||||
|
||||
The containers intentionally run as root only inside their own namespaces so fresh bind mounts work without host-specific UID/GID settings. Their root filesystems are read-only, all capabilities are dropped, and their private `0077` umask makes newly created data private by default. Host paths must be writable by the Docker daemon; do not recursively change ownership, because game-container UID/GID remains a per-instance choice. Set `TZ` once to a valid IANA timezone (the example uses `Europe/Paris`); it is used by both services and by Audit rendering.
|
||||
|
||||
`docker compose down` removes containers and Compose networks while preserving bind mounts and named volumes. `docker compose down -v` also destroys the `agent_state` and `agent_auth` named volumes; it can make existing managed containers impossible to manage safely and must not be used for a retained installation.
|
||||
|
||||
## Upgrade and rollback
|
||||
|
||||
The two-service deployment validates and reuses secrets created by its normal first-start contract. The v0.1.0 initializer used a different application-image identity, so do not claim an unverified in-place migration for a v0.1.0 deployment where initialization completed; preserve all five stores and validate that case on a copy before changing production. For normal updates after this correction, stop DoGaMa, take a filesystem-consistent backup of all five persistent stores, replace the canonical `compose.yaml`, change only `DOGAMA_VERSION`, then run `docker compose pull` and `docker compose up -d`.
|
||||
The two-service deployment validates and reuses secrets created by its normal first-start contract. The v0.1.0 initializer used a different application-image identity, so do not claim an unverified in-place migration for a v0.1.0 deployment where initialization completed; preserve all five stores and validate that case on a copy before changing production. For normal updates after this correction, stop DoGaMa, take a filesystem-consistent backup of all five persistent stores, replace the canonical `compose.yaml`, change only `DOGAMA_VERSION`, then run `docker compose pull` and `docker compose up -d`. A rollback is a recovery action: restore a backup when required, repin `DOGAMA_VERSION` to the preceding image, and run `docker compose up -d`. Do not assume SQLite schema compatibility in the downgrade direction.
|
||||
|
||||
Startup initializes a new SQLite database directly from the current schema before serving requests. Development databases from older revisions are intentionally unsupported for now: schema changes can require starting again with an empty application database. Versioned migrations will be introduced before production stabilization. Never remove or recreate `agent_state` or `agent_auth`, and never replace an existing master key: doing so would invalidate the agent registry or make encrypted notification data unreadable.
|
||||
|
||||
## Backup scope
|
||||
|
||||
Back up DoGaMa itself as one consistent set: `DOGAMA_DATA_PATH` (SQLite, templates, import staging and the master key), `agent_state`, and `agent_auth`. Back up `DOGAMA_SERVERS_PATH` for game configuration/player data and `DOGAMA_BACKUPS_PATH` for game backup archives separately according to the retention policy. The game-server stores do not replace a backup of DoGaMa's database and security state.
|
||||
|
||||
## Release pipeline
|
||||
|
||||
Normal CI runs for pull requests and pushes to `main`; it does not publish images. `.gitea/workflows/release.yml` runs only for tags matching `v*.*.*`, validates the exact tag commit, then publishes the `dogama` and `dogama-agent` images for Linux `amd64` and `arm64`.
|
||||
|
||||
@@ -52,6 +52,13 @@ func configure(ctx context.Context, db *sql.DB) error {
|
||||
}
|
||||
|
||||
func initialize(ctx context.Context, db *sql.DB) error {
|
||||
var existing int
|
||||
if err := db.QueryRowContext(ctx, "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='system_state'").Scan(&existing); err != nil {
|
||||
return fmt.Errorf("inspect sqlite schema: %w", err)
|
||||
}
|
||||
if existing == 1 {
|
||||
return nil
|
||||
}
|
||||
if _, err := db.ExecContext(ctx, schema); err != nil {
|
||||
return fmt.Errorf("initialize sqlite schema: %w", err)
|
||||
}
|
||||
|
||||
@@ -57,3 +57,26 @@ func TestOpenInitializesCurrentSchemaAndConfiguration(t *testing.T) {
|
||||
t.Fatalf("unexpected pragmas: foreign_keys=%d busy_timeout=%d journal_mode=%s", foreignKeys, busyTimeout, journalMode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenReusesInitializedCurrentSchema(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "dogama.db")
|
||||
first, err := sqlite.Open(context.Background(), path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := first.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
second, err := sqlite.Open(context.Background(), path)
|
||||
if err != nil {
|
||||
t.Fatalf("reopen initialized database: %v", err)
|
||||
}
|
||||
defer second.Close()
|
||||
var found int
|
||||
if err := second.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='system_state'").Scan(&found); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if found != 1 {
|
||||
t.Fatal("system state table is missing after reopen")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user