- Add scripts/bump-version.sh for semver patch increments - Update AGENTS.md Dev Commands section and add Version Bumping docs - Makefile default VERSION bumped to 1.0.2
3.8 KiB
SyncServer — Agent Guidance
Project Type
Go monolith (single binary) with embedded React SPA frontend. Targets Linux amd64 LXC containers on Proxmox.
Dev Commands
# Clean build and package (Linux amd64)
make clean && VERSION=X.Y.Z make package
# Build only (no package), Linux amd64
make build-all
# Run
./syncserver --data-dir ./data --addr :8080 \
SYNCSERVER_ADMIN_USER=admin SYNCSERVER_ADMIN_PASSWORD=secret
# Tests
go test ./...
Version Bumping
Before each push that contains user-facing changes, run the version bump script:
./scripts/bump-version.sh
This reads the current version from cmd/server/main.go, increments the patch number (semver), and updates both cmd/server/main.go and Makefile. Then commit and push:
git add cmd/server/main.go Makefile
git commit -m 'Bump version to X.Y.Z'
git push
The DEBIAN/control uses @@VERSION@@ placeholder and is substituted at make package time — no manual edit needed.
To verify the LXC is running the expected version:
ssh root@10.5.1.30 '/usr/bin/syncserver --version'
ssh root@10.5.1.30 'journalctl -u syncserver -n 3 --no-pager'
Critical Build Order
- Frontend must be built FIRST:
cd web && npm run build - Output copied to
internal/webui/dist/(must exist before Go build) - Then:
go build ./cmd/server
scripts/build.sh does this automatically. Running go build without the frontend dist will produce a binary without the web UI.
Architecture Notes
- Entry point:
cmd/server/main.go - DB:
modernc.org/sqlite— pure Go, CGO_ENABLED=0. Driver name is"sqlite"(not"sqlite3"). - Migrations: embedded via
//go:embed migrationsininternal/db/migrations.go. Must usemigrationsFSvariable, path"migrations"(not"internal/db/migrations"). - SSH keys: generated at startup if missing; stored in
$DATA_DIR/ssh/id_ed25519. Only the public key is stored in DB. - Sync engine: uses
os/exec.CommandContextto runrsync -e ssh .... SSH key is passed via-iflag, not via SSH config file. - Scheduler: homebrew cron parser (
m h dom mon dowformat, no seconds). Librarygithub.com/robfig/cronis NOT used. - SSE:
GET /api/jobs/streamstreamstext/event-stream. Clients must handleevent:field.
Frontend Stack
- TypeScript + React 18 + Vite + Tailwind CSS
- Source:
web/src/, built output:web/dist/ - API client:
web/src/api/client.ts - No React Router 6 data loaders; fetch hooks are manual in each page component
- Tests: no frontend test suite yet
Key Package Boundaries
| Package | Responsibility |
|---|---|
internal/api/ |
HTTP handlers, chi router, DTOs |
internal/auth/ |
JWT (HS256), bcrypt, middleware |
internal/db/ |
SQLite connection, migrations runner |
internal/models/ |
Data access (raw sql) |
internal/syncengine/ |
Job queue, rsync subprocess, event bus |
internal/scheduler/ |
Cron parsing, tick loop |
internal/sshmanager/ |
Ed25519 key generation, known_hosts |
internal/wol/ |
Magic packet, TCP/ping waiter |
internal/webui/ |
embed.FS for React dist |
DB Schema
Managed via ordered SQL migrations in internal/db/migrations/. Apply on startup via db.RunMigrations(). Table schema_migrations tracks applied versions.
Package Script Quirks
scripts/build.sh: cross-compiles to Linux amd64 viaGOOS=linux GOARCH=amd64. Version injected via-ldflags="-X main.version=$VERSION".scripts/package-deb.sh: callsdpkg-deb --build. Version must be substituted intoDEBIAN/controlbefore build (@@VERSION@@placeholder).
WoL Constraints
Magic packet uses SO_BROADCAST on a plain UDP socket. No raw sockets, no privileged container needed. Requires L2 reachability (same broadcast domain) from the server to the target machine's NIC.