infra: rename truenas-burnin → nas-burnin (1.0.0-41)

Matches the 1.0.0-38 product display rename. Touches every
infrastructure identifier:

- container_name: truenas-burnin → nas-burnin
- forge URL in /api/v1/updates/check
- security-scan: REPO_URL, REPO, DEPLOY_DIR, systemd unit description
- run-tests.sh default container name
- doc paths in README/SPEC/CLAUDE
- in-app instruction strings (login.html, settings.html, auth_cli.py)

Maple migration done in lockstep:
  docker compose down (truenas-burnin)
  mv ~/docker/stacks/{truenas-burnin,nas-burnin}
  systemd unit ExecStart updated + daemon-reload
  docker compose up -d --build → container nas-burnin
  Old image truenas-burnin-app removed (~12 GB reclaimed)
  Stale top-level orphans cleaned (config.py, poller.py, routes.py,
  truenas.py, tests/) — all dead since pre-split refactors

Forge repo rename (git.hellocomputer.xyz/brandon/truenas-burnin →
nas-burnin) is a separate UI-only step. Forgejo redirects the old
URL after rename, so this commit can be pushed to the existing
remote first; remote URL gets updated locally once you rename.
This commit is contained in:
Brandon Walter 2026-05-04 07:16:02 -07:00
parent d38807f957
commit 944e4c1541
13 changed files with 37 additions and 37 deletions

View file

@ -11,8 +11,8 @@ A self-hosted web dashboard for running and tracking hard-drive burn-in tests
against a TrueNAS SCALE 25.10 instance. Deployed on **maple.local** (10.0.0.138).
- **App URL**: http://10.0.0.138:8084 (or http://burnin.hellocomputer.xyz)
- **Stack path on maple.local**: `~/docker/stacks/truenas-burnin/`
- **Source (local mac)**: `~/Desktop/claudesandbox/truenas-burnin/`
- **Stack path on maple.local**: `~/docker/stacks/nas-burnin/`
- **Source (local mac)**: `~/Desktop/claudesandbox/nas-burnin/`
- **Compose synced to maple.local** via `scp` or manual copy
### Stages completed
@ -36,7 +36,7 @@ against a TrueNAS SCALE 25.10 instance. Deployed on **maple.local** (10.0.0.138)
## File Map
```
truenas-burnin/
nas-burnin/
├── docker-compose.yml # two services: mock-truenas + app
├── Dockerfile # app container
├── requirements.txt
@ -222,18 +222,18 @@ All read from `.env` via `pydantic-settings`. See `.env.example` for full list.
### First deploy (already done)
```bash
# On maple.local
cd ~/docker/stacks/truenas-burnin
cd ~/docker/stacks/nas-burnin
docker compose up -d --build
```
### Redeploy after code changes
```bash
# Copy changed files from mac to maple.local first, e.g.:
scp -P 2225 -r app/ brandon@10.0.0.138:~/docker/stacks/truenas-burnin/
scp -P 2225 -r app/ brandon@10.0.0.138:~/docker/stacks/nas-burnin/
# Then on maple.local:
ssh brandon@10.0.0.138 -p 2225
cd ~/docker/stacks/truenas-burnin
cd ~/docker/stacks/nas-burnin
docker compose up -d --build
```
@ -242,7 +242,7 @@ docker compose up -d --build
# On maple.local — stop containers first
docker compose stop app
# Delete DB using alpine (container owns the file, sudo not available)
docker run --rm -v ~/docker/stacks/truenas-burnin/data:/data alpine rm -f /data/app.db
docker run --rm -v ~/docker/stacks/nas-burnin/data:/data alpine rm -f /data/app.db
docker compose start app
```
@ -350,7 +350,7 @@ async def burnin_get(job_id: int, ...): ...
### `requirements.txt` is unpinned
Every `docker compose up -d --build` pulls latest of fastapi, starlette, jinja2, asyncssh, etc. The Starlette 1.0 regression on 2026-04-27 is a direct consequence. **Either pin to known-good versions, or audit installed versions immediately after each rebuild** with:
```bash
docker exec truenas-burnin python3 -c "import fastapi, starlette, jinja2; print(fastapi.__version__, starlette.__version__, jinja2.__version__)"
docker exec nas-burnin python3 -c "import fastapi, starlette, jinja2; print(fastapi.__version__, starlette.__version__, jinja2.__version__)"
```
### Local source ↔ maple host can drift
@ -358,8 +358,8 @@ The deploy convention is `scp -r app/` from mac to maple, but if you ever edit o
**Always `diff -u` before bulk scp:**
```bash
ssh -p 2225 brandon@10.0.0.138 'cat ~/docker/stacks/truenas-burnin/app/routes.py' > /tmp/deployed_routes.py
diff -u /tmp/deployed_routes.py ~/Desktop/claudesandbox/truenas-burnin/app/routes.py
ssh -p 2225 brandon@10.0.0.138 'cat ~/docker/stacks/nas-burnin/app/routes.py' > /tmp/deployed_routes.py
diff -u /tmp/deployed_routes.py ~/Desktop/claudesandbox/nas-burnin/app/routes.py
```
When sides have conflicting edits, prefer **patching the host file in place + rebuild** over a destructive scp.
@ -427,7 +427,7 @@ SMART attrs stored as JSON blob in `drives.smart_attrs`. Updated by `final_check
Settings page has a "Check for Updates" button that fetches:
```
GET https://git.hellocomputer.xyz/api/v1/repos/brandon/truenas-burnin/releases/latest
GET https://git.hellocomputer.xyz/api/v1/repos/brandon/nas-burnin/releases/latest
```
Compares tag name against `settings.app_version`; shows "up to date" or "v{tag} available".

View file

@ -37,7 +37,7 @@ open http://localhost:8084 # or your host's IP
If you set `INITIAL_ADMIN_*` env vars *and* the users table is empty, that
account is created on startup automatically. After that the env vars are
ignored — change passwords from the UI ("Change password" header link) or
the CLI (`docker exec -it truenas-burnin python -m app.auth_cli reset
the CLI (`docker exec -it nas-burnin python -m app.auth_cli reset
<username>`).
---
@ -172,17 +172,17 @@ Configure SMTP in Settings → Email. Includes a "Test SMTP" button.
### Logs
```bash
docker logs -f truenas-burnin
docker logs -f nas-burnin
# JSON-structured. Filter with jq:
docker logs truenas-burnin 2>&1 | jq -rR 'fromjson? | "\(.ts) \(.level) \(.msg)"'
docker logs nas-burnin 2>&1 | jq -rR 'fromjson? | "\(.ts) \(.level) \(.msg)"'
```
### User management
```bash
docker exec -it truenas-burnin python -m app.auth_cli list
docker exec -it truenas-burnin python -m app.auth_cli add <username>
docker exec -it truenas-burnin python -m app.auth_cli reset <username>
docker exec -it nas-burnin python -m app.auth_cli list
docker exec -it nas-burnin python -m app.auth_cli add <username>
docker exec -it nas-burnin python -m app.auth_cli reset <username>
```
Passwords are read from a TTY prompt; never accept them on the command

View file

@ -251,8 +251,8 @@ The API makes this app a strong candidate for MCP server integration, allowing a
Docker Compose. Minimum viable setup:
```bash
git clone https://github.com/yourusername/truenas-burnin
cd truenas-burnin
git clone https://github.com/yourusername/nas-burnin
cd nas-burnin
cp .env.example .env
# Edit .env for system-level settings (TrueNAS URL, poll interval, etc.)
docker compose up -d

View file

@ -1,9 +1,9 @@
"""Password reset / user management CLI.
Run inside the container:
docker exec -it truenas-burnin python -m app.auth_cli reset <username>
docker exec -it truenas-burnin python -m app.auth_cli list
docker exec -it truenas-burnin python -m app.auth_cli add <username>
docker exec -it nas-burnin python -m app.auth_cli reset <username>
docker exec -it nas-burnin python -m app.auth_cli list
docker exec -it nas-burnin python -m app.auth_cli add <username>
Reads the password from a TTY prompt never accept it on the command
line so it doesn't leak into shell history.

View file

@ -83,7 +83,7 @@ class Settings(BaseSettings):
ssh_key: str = "" # PEM private key content (paste full key including headers)
# Application version — used by the /api/v1/updates/check endpoint
app_version: str = "1.0.0-40"
app_version: str = "1.0.0-41"
# ---- Authentication (1.0.0-22) ----
# session_secret: HMAC key for signing session cookies. Empty = generate

View file

@ -112,7 +112,7 @@ async def check_updates():
try:
async with httpx.AsyncClient(timeout=8.0) as client:
r = await client.get(
"https://git.hellocomputer.xyz/api/v1/repos/brandon/truenas-burnin/releases/latest",
"https://git.hellocomputer.xyz/api/v1/repos/brandon/nas-burnin/releases/latest",
headers={"Accept": "application/json"},
)
if r.status_code == 200:

View file

@ -59,7 +59,7 @@
<div class="login-footer">
Authentication is local to this dashboard. Forgot your password?
Reset it via the container DB:<br>
<code class="login-code">docker exec truenas-burnin python -m app.auth_cli reset &lt;user&gt;</code>
<code class="login-code">docker exec nas-burnin python -m app.auth_cli reset &lt;user&gt;</code>
</div>
</main>

View file

@ -343,7 +343,7 @@
<div id="restart-banner" style="display:none;margin-top:12px;padding:12px 16px;background:rgba(255,170,0,0.12);border:1px solid var(--yellow);border-radius:8px;color:var(--text-strong)">
<strong>&#9888; Container restart required</strong> — system settings are saved but won't take effect until you restart the app container:
<pre style="margin:8px 0 0;padding:8px 10px;background:var(--bg-card);border-radius:5px;font-size:12px;color:var(--text-strong);user-select:all">docker compose restart app</pre>
<span style="font-size:11px;color:var(--text-muted)">Run this on <strong>maple.local</strong> from <code>~/docker/stacks/truenas-burnin/</code></span>
<span style="font-size:11px;color:var(--text-muted)">Run this on <strong>maple.local</strong> from <code>~/docker/stacks/nas-burnin/</code></span>
</div>
</form>

View file

@ -11,7 +11,7 @@ services:
app:
build: .
container_name: truenas-burnin
container_name: nas-burnin
ports:
- "8084:8084"
env_file: .env

View file

@ -14,7 +14,7 @@
set -euo pipefail
REMOTE_HOST="${REMOTE_HOST:-maple}"
CONTAINER="${CONTAINER:-truenas-burnin}"
CONTAINER="${CONTAINER:-nas-burnin}"
REMOTE_TMP="/tmp/tnb-tests-$$.tgz"
CONTAINER_TMP="/tmp/tnb-tests.tgz"
PATTERN="${1:-}"

View file

@ -1,5 +1,5 @@
[Unit]
Description=Security scan of truenas-burnin (pip-audit + bandit + gitleaks)
Description=Security scan of nas-burnin (pip-audit + bandit + gitleaks)
After=network-online.target docker.service
Wants=network-online.target
@ -7,7 +7,7 @@ Wants=network-online.target
Type=oneshot
# Wire SECURITY_SCAN_WEBHOOK here if you want findings POSTed somewhere.
# Environment=SECURITY_SCAN_WEBHOOK=https://chat.example/hooks/abc
ExecStart=%h/docker/stacks/truenas-burnin/scripts/security-scan.sh
ExecStart=%h/docker/stacks/nas-burnin/scripts/security-scan.sh
# Tools cache + container pulls — give them headroom.
TimeoutStartSec=600
StandardOutput=journal

View file

@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Daily security scan of the deployed truenas-burnin source on maple.
# Daily security scan of the deployed nas-burnin source on maple.
# Mirrors the .forgejo/workflows/security-scan.yml CI pipeline so a finding
# the runner-less forge would have flagged still surfaces here.
#
@ -18,8 +18,8 @@
set -uo pipefail
REPO_URL="${REPO_URL:-https://git.hellocomputer.xyz/brandon/truenas-burnin.git}"
REPO="${REPO:-$HOME/scan-checkouts/truenas-burnin}"
REPO_URL="${REPO_URL:-https://git.hellocomputer.xyz/brandon/nas-burnin.git}"
REPO="${REPO:-$HOME/scan-checkouts/nas-burnin}"
OUT_BASE="${OUT_BASE:-$HOME/security-scans}"
DATE="$(date +%Y-%m-%d)"
OUT_DIR="$OUT_BASE/scan-$DATE"
@ -29,7 +29,7 @@ GITLEAKS_VERSION="${GITLEAKS_VERSION:-8.21.2}"
mkdir -p "$OUT_DIR" "$(dirname "$REPO")"
# Maintain a dedicated checkout for scanning. The deploy at
# ~/docker/stacks/truenas-burnin/ is just the bind-mounted source — no
# ~/docker/stacks/nas-burnin/ is just the bind-mounted source — no
# .git, no history — so gitleaks can't scan there. We keep a separate
# clone, fast-forward it to origin/main each run.
if [ ! -d "$REPO/.git" ]; then
@ -58,7 +58,7 @@ date -Iseconds >> "$OUT_DIR/summary.txt"
echo >> "$OUT_DIR/summary.txt"
# --- pip-audit against the lockfile in a throwaway container ------------
# Previously we did `docker exec truenas-burnin pip install pip-audit`
# Previously we did `docker exec nas-burnin pip install pip-audit`
# which mutated the live production container with a transient package.
# Now scan the lockfile in an ephemeral container — same coverage of
# pinned versions + their transitives, no side effects on prod.
@ -77,7 +77,7 @@ echo " exit=$PIPS ($OUT_DIR/pip-audit.txt)" | tee -a "$OUT_DIR/summary.txt"
# forge HEAD and maple. B608 (SQL injection via dynamic strings) is
# skipped globally: every dynamic SQL build in this codebase uses
# bound parameters for data and structural placeholders only.
DEPLOY_DIR="${DEPLOY_DIR:-$HOME/docker/stacks/truenas-burnin}"
DEPLOY_DIR="${DEPLOY_DIR:-$HOME/docker/stacks/nas-burnin}"
echo "--- bandit (deploy: $DEPLOY_DIR) ---" | tee -a "$OUT_DIR/summary.txt"
docker run --rm \
-v "$DEPLOY_DIR/app:/src:ro" \

View file

@ -1,5 +1,5 @@
[Unit]
Description=Daily security scan of truenas-burnin
Description=Daily security scan of nas-burnin
Requires=security-scan.service
[Timer]