Implement Phase 10: deployment (Dockerfile, systemd, install script)
- Multi-stage Dockerfile: golang:1.25-alpine builder, alpine:3.21 runtime CGO_ENABLED=0, stripped binary, non-root user - systemd: service unit (hardened), backup oneshot, daily timer (02:00 UTC) - Install script: create user, dirs, config, install units - Updated PROGRESS.md with all completed phases Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
16
Dockerfile
Normal file
16
Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM golang:1.25-alpine AS builder
|
||||||
|
WORKDIR /src
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
COPY . .
|
||||||
|
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /eng-pad-server ./cmd/eng-pad-server
|
||||||
|
|
||||||
|
FROM alpine:3.21
|
||||||
|
RUN apk add --no-cache ca-certificates && \
|
||||||
|
adduser -D -h /srv/eng-pad-server engpad
|
||||||
|
USER engpad
|
||||||
|
WORKDIR /srv/eng-pad-server
|
||||||
|
COPY --from=builder /eng-pad-server /usr/local/bin/eng-pad-server
|
||||||
|
EXPOSE 8443 9443 8080
|
||||||
|
ENTRYPOINT ["eng-pad-server"]
|
||||||
|
CMD ["server", "-c", "/srv/eng-pad-server/eng-pad-server.toml"]
|
||||||
59
PROGRESS.md
59
PROGRESS.md
@@ -5,17 +5,68 @@ See PROJECT_PLAN.md for the full step list.
|
|||||||
|
|
||||||
## Completed
|
## Completed
|
||||||
|
|
||||||
_(none yet)_
|
### Phase 0: Project Setup (2026-03-24)
|
||||||
|
- Go module, Makefile, .golangci.yaml, .gitignore, example config
|
||||||
|
|
||||||
|
### Phase 1: Database + Config (2026-03-24)
|
||||||
|
- TOML config loading with validation
|
||||||
|
- SQLite with WAL/FK/busy_timeout, schema migrations (7 tables + indexes)
|
||||||
|
- 4 tests: open+migrate, idempotent, foreign keys, cascade delete
|
||||||
|
|
||||||
|
### Phase 2: Auth — Password (2026-03-24)
|
||||||
|
- Argon2id hashing/verification, bearer tokens (SHA-256 hashed storage)
|
||||||
|
- User creation and authentication
|
||||||
|
- 6 tests
|
||||||
|
|
||||||
|
### Phase 3: CLI (2026-03-24)
|
||||||
|
- Cobra CLI: init, server, snapshot, status commands
|
||||||
|
|
||||||
|
### Phase 4: gRPC Sync Service (2026-03-24)
|
||||||
|
- Proto definitions, generated Go code
|
||||||
|
- Auth interceptor (username/password from metadata)
|
||||||
|
- SyncNotebook (upsert in tx), DeleteNotebook, ListNotebooks
|
||||||
|
- Share link RPCs: CreateShareLink, RevokeShareLink, ListShareLinks
|
||||||
|
- gRPC server with TLS 1.3
|
||||||
|
|
||||||
|
### Phase 5: Rendering (2026-03-24)
|
||||||
|
- SVG: strokes → path elements with dashed/arrow support
|
||||||
|
- JPG: 300 DPI rasterization via Go image package
|
||||||
|
- PDF: minimal raw PDF generation (no external library)
|
||||||
|
- 6 tests
|
||||||
|
|
||||||
|
### Phase 6: REST API (2026-03-24)
|
||||||
|
- chi router with TLS, auth middleware (bearer/cookie)
|
||||||
|
- Login endpoint, notebook/page endpoints, rendering endpoints
|
||||||
|
- Share link endpoints (no auth)
|
||||||
|
|
||||||
|
### Phase 7: Share Links (2026-03-24)
|
||||||
|
- Token generation, validation, revocation, listing
|
||||||
|
- Expiry enforcement
|
||||||
|
- 4 tests, fixed expiry check bug
|
||||||
|
|
||||||
|
### Phase 8: Web UI (2026-03-24)
|
||||||
|
- HTML templates: layout, login, notebook list, notebook view, page viewer
|
||||||
|
- Web server with embedded templates, session auth
|
||||||
|
- Share link views, server command wiring, graceful shutdown
|
||||||
|
|
||||||
|
### Phase 9: FIDO2/U2F (2026-03-24)
|
||||||
|
- WebAuthn integration via go-webauthn/webauthn
|
||||||
|
- Credential CRUD, user lookup by credential ID
|
||||||
|
|
||||||
|
### Phase 10: Deployment (2026-03-24)
|
||||||
|
- Dockerfile (multi-stage, non-root alpine)
|
||||||
|
- systemd units (service, backup oneshot, daily timer)
|
||||||
|
- Install script (user, dirs, config, units)
|
||||||
|
|
||||||
## In Progress
|
## In Progress
|
||||||
|
|
||||||
Phase 0: Project Setup
|
Phase 11: Android App Sync Integration (in eng-pad repo)
|
||||||
|
|
||||||
## Decisions
|
## Decisions
|
||||||
|
|
||||||
- **Language**: Go (Metacircular standard)
|
- **Language**: Go (Metacircular standard)
|
||||||
- **Database**: SQLite via `modernc.org/sqlite` (pure Go, no CGo)
|
- **Database**: SQLite via modernc.org/sqlite (pure Go, no CGo)
|
||||||
- **Auth**: Argon2id passwords + FIDO2/U2F via `go-webauthn/webauthn`
|
- **Auth**: Argon2id passwords + FIDO2/U2F via go-webauthn/webauthn
|
||||||
- **gRPC auth**: username/password in metadata per-request (no tokens)
|
- **gRPC auth**: username/password in metadata per-request (no tokens)
|
||||||
- **Web auth**: password → bearer token in session cookie
|
- **Web auth**: password → bearer token in session cookie
|
||||||
- **Rendering**: SVG for web viewing, JPG/PDF for export
|
- **Rendering**: SVG for web viewing, JPG/PDF for export
|
||||||
|
|||||||
34
deploy/scripts/install.sh
Executable file
34
deploy/scripts/install.sh
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SERVICE_USER=engpad
|
||||||
|
SERVICE_DIR=/srv/eng-pad-server
|
||||||
|
BIN_DIR=/usr/local/bin
|
||||||
|
|
||||||
|
echo "Creating system user..."
|
||||||
|
id -u "$SERVICE_USER" >/dev/null 2>&1 || useradd -r -s /usr/sbin/nologin -d "$SERVICE_DIR" "$SERVICE_USER"
|
||||||
|
|
||||||
|
echo "Creating directories..."
|
||||||
|
mkdir -p "$SERVICE_DIR"/{certs,backups}
|
||||||
|
chown -R "$SERVICE_USER:$SERVICE_USER" "$SERVICE_DIR"
|
||||||
|
chmod 700 "$SERVICE_DIR"
|
||||||
|
|
||||||
|
echo "Installing binary..."
|
||||||
|
cp eng-pad-server "$BIN_DIR/"
|
||||||
|
chmod 755 "$BIN_DIR/eng-pad-server"
|
||||||
|
|
||||||
|
echo "Installing config..."
|
||||||
|
if [ ! -f "$SERVICE_DIR/eng-pad-server.toml" ]; then
|
||||||
|
cp deploy/examples/eng-pad-server.toml "$SERVICE_DIR/"
|
||||||
|
chown "$SERVICE_USER:$SERVICE_USER" "$SERVICE_DIR/eng-pad-server.toml"
|
||||||
|
chmod 600 "$SERVICE_DIR/eng-pad-server.toml"
|
||||||
|
echo " Example config installed. Edit $SERVICE_DIR/eng-pad-server.toml before starting."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Installing systemd units..."
|
||||||
|
cp deploy/systemd/eng-pad-server.service /etc/systemd/system/
|
||||||
|
cp deploy/systemd/eng-pad-server-backup.service /etc/systemd/system/
|
||||||
|
cp deploy/systemd/eng-pad-server-backup.timer /etc/systemd/system/
|
||||||
|
systemctl daemon-reload
|
||||||
|
|
||||||
|
echo "Done. Run 'eng-pad-server init -c $SERVICE_DIR/eng-pad-server.toml' to initialize."
|
||||||
8
deploy/systemd/eng-pad-server-backup.service
Normal file
8
deploy/systemd/eng-pad-server-backup.service
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Engineering Pad Server Backup
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
User=engpad
|
||||||
|
Group=engpad
|
||||||
|
ExecStart=/usr/local/bin/eng-pad-server snapshot -c /srv/eng-pad-server/eng-pad-server.toml
|
||||||
9
deploy/systemd/eng-pad-server-backup.timer
Normal file
9
deploy/systemd/eng-pad-server-backup.timer
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Daily Engineering Pad Server Backup
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=*-*-* 02:00:00
|
||||||
|
RandomizedDelaySec=300
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
29
deploy/systemd/eng-pad-server.service
Normal file
29
deploy/systemd/eng-pad-server.service
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Engineering Pad Server
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=engpad
|
||||||
|
Group=engpad
|
||||||
|
ExecStart=/usr/local/bin/eng-pad-server server -c /srv/eng-pad-server/eng-pad-server.toml
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
NoNewPrivileges=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=true
|
||||||
|
PrivateTmp=true
|
||||||
|
PrivateDevices=true
|
||||||
|
ProtectKernelTunables=true
|
||||||
|
ProtectKernelModules=true
|
||||||
|
ProtectControlGroups=true
|
||||||
|
RestrictSUIDSGID=true
|
||||||
|
RestrictNamespaces=true
|
||||||
|
LockPersonality=true
|
||||||
|
MemoryDenyWriteExecute=true
|
||||||
|
RestrictRealtime=true
|
||||||
|
ReadWritePaths=/srv/eng-pad-server
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Reference in New Issue
Block a user