Commands¶
Every command, two-shell where it applies. Run from the repo root unless noted. Full walkthroughs: §02 — Getting Started, §10 — Development Workflow, §14 — Deployment, §15 — Operations.
First run¶
The Engine listens on http://localhost:5257, which redirects to the web client at /app. Migrations apply automatically at startup. LM Studio is not started for you — load a model on :1234 first.
Build¶
Build the web client before publishing
dotnet build does not build Aetherium.Web, and .NET bakes its static-asset manifest at
publish time — so bun run build must run before dotnet publish or the client silently
is not in the output. scripts/deploy.sh does this in order.
Web client¶
Regenerate the API types after changing a controller:
scripts/generate-contract.sh # server -> docs/contract/openapi.v1.json
bun run generate:api # that -> src/api/schema.d.ts
Run / hot reload¶
Test¶
Two suites — the server's and the client's.
EF migrations¶
Target the server .csproj (it is its own startup project — no --startup-project).
Publish¶
Publish the .csproj, never the .slnx
dotnet publish against the .slnx builds every project but never runs the server's Publish target — no runnable output, no wwwroot/ (browser client 404s). build/watch work against either; publish must target the server .csproj.
Deploy (Linux · systemd)¶
Production runs on Linux under systemd; these are bash-only by design.
./scripts/deploy.sh --dry-run # report, change nothing
./scripts/deploy.sh # deploy current commit
# flags: --no-pull --no-restart --force(over dirty tree)
sudo systemctl daemon-reload
sudo systemctl enable --now aetherium.service
sudo systemctl status aetherium.service
sudo systemctl restart aetherium.service
cat /opt/aetherium/deployed-commit.txt # currently-deployed SHA
Roll back: git checkout <good-sha> && ./scripts/deploy.sh --no-pull.
Operations (Linux)¶
journalctl -u aetherium.service -f -n 0 # follow from tail
journalctl -u aetherium.service -n 200 --no-pager | grep -iE "error|exception"
journalctl -u aetherium.service -f | grep "\[MODEL-ROUTING\]" # one concern
curl -s http://localhost:5257/api/health # API + Postgres
curl -s http://localhost:5257/api/health/detailed | jq # read the body
Data backup & restore (Linux)¶
# PostgreSQL
docker exec <postgres-container> pg_dump -U aetherium_user -d aetherium_refactor_db \
--clean --if-exists --no-owner --no-privileges > aetherium-db-backup.sql
docker exec -i <postgres-container> psql -U aetherium_user -d aetherium_refactor_db < aetherium-db-backup.sql
# ChromaDB (stop-copy-start) + workspaces
docker stop <chroma-container>
tar czf chroma-backup.tar.gz -C /var/lib/aetherium chroma-data
docker start <chroma-container>
tar czf workspaces-backup.tar.gz -C /var/lib/aetherium workspaces
# Prune the unbounded audit table (no retention exists)
docker exec -i <postgres-container> psql -U aetherium_user -d aetherium_refactor_db <<'SQL'
DELETE FROM "ToolSecurityAuditEntries" WHERE "Timestamp" < NOW() - INTERVAL '90 days';
VACUUM (ANALYZE) "ToolSecurityAuditEntries";
SQL
Helper scripts¶
| Script | Purpose |
|---|---|
start-aetherium-linux.sh |
Start the Engine (Linux); also brings up SearXNG |
scripts/deploy.sh |
Deploy a Git commit to the running service |
scripts/render-gpu-env.py |
Write each GPU unit's card-pin environment file from the Engine config (--validate and --check write nothing; deploy.sh runs it). --host NAME renders the units of the cards whose Host is NAME, --list-hosts prints each card Host, --export prints only the config sections it reads. A GPU service on a slot with no Card gets a file without a pin when no configured card is in this machine |
scripts/gpu-node-units.sh |
The GPU-unit step of deploy.sh — pin files, leased GPU service units, llama-server routers — for the machine it runs on; deploy.sh runs it locally and, with --host, on each card Host over SSH |
scripts/sync-chroma.{ps1,sh} · scripts/sync-db.{ps1,sh} |
Move ChromaDB / Postgres state between machines |