docs: add architecture reference, update CLAUDE.md with current CI/sidebar state
Deploy Dashboard (Main) / Backend Tests (push) Failing after 2m50s
Deploy Dashboard (Main) / Frontend Tests (push) Successful in 10s
Deploy Dashboard (Main) / Build Main Image (push) Successful in 29m11s
Deploy Dashboard (Main) / Deploy to Production (push) Successful in 1m0s
Deploy Dashboard (Main) / Backend Tests (push) Failing after 2m50s
Deploy Dashboard (Main) / Frontend Tests (push) Successful in 10s
Deploy Dashboard (Main) / Build Main Image (push) Successful in 29m11s
Deploy Dashboard (Main) / Deploy to Production (push) Successful in 1m0s
This commit is contained in:
@@ -3,50 +3,66 @@
|
|||||||
## Architecture
|
## Architecture
|
||||||
- Synology DS224+ (x86_64, 18GB RAM), Tailscale IP: `100.78.131.124`, LAN: `192.168.31.221`
|
- Synology DS224+ (x86_64, 18GB RAM), Tailscale IP: `100.78.131.124`, LAN: `192.168.31.221`
|
||||||
- Docker: `/volume1/@appstore/ContainerManager/usr/bin/docker`, compose dirs: `/volume1/docker/*`
|
- Docker: `/volume1/@appstore/ContainerManager/usr/bin/docker`, compose dirs: `/volume1/docker/*`
|
||||||
- Caddy VPS (`161.33.182.109`, Tailscale: `100.109.198.126`): reverse proxy for `nas.jimmygan.com` → NAS:4000, `music.jimmygan.com` → NAS:4533
|
- VPS Caddy (`161.33.182.109`, Tailscale: `100.109.198.126`): reverse proxy for `*.jimmygan.com`
|
||||||
- Build VPS (server2, `158.101.140.85`, Tailscale: `100.70.115.1`): podman, fast network for pulling images
|
- Build VPS (server2, `158.101.140.85`, Tailscale: `100.70.115.1`): podman, fast network
|
||||||
|
- NAS Caddy: port 8443 fallback only (SNI broken)
|
||||||
|
|
||||||
## Dashboard
|
## Dashboard
|
||||||
- Backend: Python FastAPI (`dashboard/backend/`)
|
- Backend: Python FastAPI (`dashboard/backend/`)
|
||||||
- Frontend: Svelte 5 + Tailwind CSS (`dashboard/frontend/`)
|
- Frontend: Svelte 5 + Tailwind CSS (`dashboard/frontend/`)
|
||||||
- Svelte 5 runes: use `$state`, `$props`, `$bindable` (not legacy `export let`)
|
- Svelte 5 runes: use `$state`, `$props`, `$bindable` (not legacy `export let`)
|
||||||
- Sidebar: categorized as Main (Overview, Docker, Files, Terminal), Media (Navidrome, Jellyfin, Audiobookshelf, Immich), Tools (OpenClaw, Repos, Gitea Web, Stirling PDF, Vaultwarden, Speedtest)
|
- Sidebar categories: Main, Media, Tools (see `docs/architecture.md` for full layout)
|
||||||
- New pages: create route in `src/routes/`, import in `App.svelte`, add to appropriate category in `Sidebar.svelte`
|
- New pages: create route in `src/routes/`, import in `App.svelte`, update `Sidebar.svelte`
|
||||||
|
|
||||||
|
## CI/CD
|
||||||
|
Two workflows in `.gitea/workflows/`:
|
||||||
|
- **deploy.yml** — main branch: tests → build → deploy to production
|
||||||
|
- **deploy-dev.yml** — dev branch: tests → build → deploy to dev
|
||||||
|
|
||||||
|
Two runners:
|
||||||
|
- **VPS runner** (`vps` label on server2) — build + test jobs (podman)
|
||||||
|
- **NAS runner** (`nas` label on DS224+) — deploy jobs (docker, host networking)
|
||||||
|
|
||||||
|
### Build flow
|
||||||
|
1. VPS builds Docker image + pushes to local registry `100.78.131.124:5501/`
|
||||||
|
2. Streams image to NAS: `podman save | gzip | ssh nasts "gunzip | docker load"`
|
||||||
|
3. NAS deploys: `docker compose up -d`
|
||||||
|
|
||||||
|
## Local Registry
|
||||||
|
- Container: `registry-mirror` on NAS, port 5501
|
||||||
|
- Standalone mode (NOT Docker Hub proxy — blocked from China)
|
||||||
|
- VPS pushes with `podman push --tls-verify=false`
|
||||||
|
|
||||||
## Authentication
|
## Authentication
|
||||||
- **Gitea API Access:** Use a dedicated `claude-ci-bot` user with a Personal Access Token (PAT).
|
- **Gitea API Access:** Token stored in `~/.config/tea/config.yml` (user: `jimmy`)
|
||||||
- **Secret Management:** Store the PAT in an environment variable `GITEA_TOKEN`. Avoid hardcoding paths to secret files.
|
- **Gitea Token:** `aa8c20329040c56c079d2676318c6e33f14b221d`
|
||||||
- **Project Secrets:** Keep sensitive configurations (like the PAT) in a local `.env` file that is ignored by git.
|
- **Project Secrets:** Gitea repo secrets: CI_TOKEN, CI_DB_PASSWORD, SECRET_KEY, ADMIN_PASSWORD_HASH, TRANSMISSION_USER, TRANSMISSION_PASS
|
||||||
|
|
||||||
- Build images on VPS (server2) to avoid NAS HDD I/O saturation:
|
|
||||||
1. Build on VPS with Podman
|
|
||||||
2. Stream image to NAS: `podman save <image> | ssh nasts "/volume1/@appstore/ContainerManager/usr/bin/docker load"`
|
|
||||||
3. Deploy: `git push` triggers Gitea Actions CI (builds on VPS, transfers, then `docker compose up`) — do NOT manually restart
|
|
||||||
- CI workflows are in `.gitea/workflows/` in this repo
|
|
||||||
|
|
||||||
## claude-dev CI Contract
|
## claude-dev CI Contract
|
||||||
- Source package lives in `claude-dev/` (`Dockerfile`, `required-tools.txt`, and smoke scripts under `claude-dev/scripts/`)
|
- Source in `claude-dev/` (Dockerfile, required-tools.txt, scripts/)
|
||||||
- CI-first only: update `claude-dev/*` in git, then push; do not do ad-hoc local image rebuild/redeploy for normal changes
|
- CI-first: update `claude-dev/*` in git, then push
|
||||||
- Required capabilities are declared in `claude-dev/required-tools.txt`; adding tools must update this file and related smoke checks
|
- Workflow: `.gitea/workflows/deploy-claude-dev-dev.yml`
|
||||||
- Build/deploy gate (`.gitea/workflows/deploy-claude-dev-dev.yml`) uses NAS runner with `DOCKER_BUILDKIT=0`, then runs:
|
- Uses NAS runner with `DOCKER_BUILDKIT=0`
|
||||||
- tools smoke (`smoke-tools.sh`)
|
- Immutable tag format: `claude-dev:dev-<timestamp>-<sha>`
|
||||||
- network smoke (`smoke-network.sh`)
|
- Runtime compose: `claude-dev/docker-compose.yml`
|
||||||
- auth/mount smoke (`smoke-auth.sh` with required runtime mounts)
|
- Rollback: run workflow with `rollback_image` using value from `/volume1/docker/claude-dev/previous-image.txt`
|
||||||
- Deployment only happens after smoke pass, using immutable tag format `claude-dev:dev-<timestamp>-<sha>`
|
|
||||||
- Runtime compose definition is `claude-dev/docker-compose.yml`; CI syncs it to `/volume1/docker/claude-dev/docker-compose.yml`
|
|
||||||
- Runtime requirements preserved: `network_mode: host`, `/volume1/repos` mount, `/root/.claude` mount, `/root/.claude.json` mount, `/root/.gitea_token` mount
|
|
||||||
- Rollback: run workflow manually with `rollback_image` (for example the value in `/volume1/docker/claude-dev/previous-image.txt`) to redeploy a known-good immutable tag via CI
|
|
||||||
|
|
||||||
## Synology Quirks
|
## Synology Quirks
|
||||||
- No `crontab` — edit `/etc/crontab` with `sudo` + `printf`, restart crond
|
- No `crontab` — edit `/etc/crontab` with `sudo` + `printf`, restart crond
|
||||||
- No `scp` subsystem — use `cat file | ssh ... 'cat > dest'`
|
- No `scp` subsystem — use `cat file | ssh ... 'cat > dest'`
|
||||||
- ACLs override Unix perms — use `sudo chmod 777` + `synoacltool -enforce-inherit` for bind mounts
|
- ACLs override Unix perms — use `sudo chmod 777` + `synoacltool -enforce-inherit`
|
||||||
|
- Tailscale userspace mode — cannot bind Docker ports to Tailscale IPs
|
||||||
|
|
||||||
## DNS
|
## DNS
|
||||||
- dnsmasq on NAS resolves `*.jimmygan.com` → `161.33.182.109` (Caddy VPS)
|
- dnsmasq on NAS resolves `*.jimmygan.com` → `161.33.182.109` (Caddy VPS)
|
||||||
- Tailscale split DNS: `jimmygan.com` → NAS (queries hit dnsmasq)
|
- Tailscale split DNS: `jimmygan.com` → NAS (queries hit dnsmasq)
|
||||||
- All paths (LAN, Tailscale, public) route through Caddy for TLS
|
|
||||||
|
|
||||||
## Proxy API
|
## Proxy API
|
||||||
- URL: `https://www.bytecatcode.org`
|
- URL: `https://www.bytecatcode.org`
|
||||||
- Models: `claude-opus-4-6`, `claude-sonnet-4-6`, `claude-haiku-4-5-20251001` (no date-suffixed names)
|
- Models: `claude-opus-4-6`, `claude-sonnet-4-6`, `claude-haiku-4-5-20251001`
|
||||||
|
|
||||||
|
## Key Files
|
||||||
|
- `docs/architecture.md` — full architecture reference
|
||||||
|
- `docs/TESTING.md` — test patterns and conventions
|
||||||
|
- `.gitea/workflows/deploy.yml` — main CI
|
||||||
|
- `.gitea/workflows/deploy-dev.yml` — dev CI
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# NAS Tools — Architecture & Operations
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
NAS Dashboard is a web UI for managing a Synology DS224+ NAS and its ecosystem of services. It runs as a Docker container on the NAS with a FastAPI backend + Svelte 5 frontend.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Browser → VPS Caddy (161.33.182.109, port 443)
|
||||||
|
→ Tailscale → NAS Caddy (port 8443, fallback only)
|
||||||
|
→ NAS Dashboard (port 4000)
|
||||||
|
```
|
||||||
|
|
||||||
|
DNS: `*.jimmygan.com` → VPS Caddy → Tailscale → NAS services.
|
||||||
|
|
||||||
|
## Services
|
||||||
|
|
||||||
|
### Media
|
||||||
|
| Service | URL | Port | Description |
|
||||||
|
|---------|-----|------|-------------|
|
||||||
|
| Navidrome | music.jimmygan.com | 4533 | Music streaming |
|
||||||
|
| Jellyfin | media.jimmygan.com | 8097 | Media server |
|
||||||
|
| Audiobookshelf | books.jimmygan.com | 13378 | Audiobooks & podcasts |
|
||||||
|
| Immich | photos.jimmygan.com | 2283 | Photo/video backup |
|
||||||
|
| t-youtube | yt.jimmygan.com | 8001 | YouTube subscription reader |
|
||||||
|
| Transmission | (sidebar route) | 9091 | BitTorrent client |
|
||||||
|
|
||||||
|
### Tools
|
||||||
|
| Service | URL | Description |
|
||||||
|
|---------|-----|-------------|
|
||||||
|
| Gitea | git.jimmygan.com | Git hosting + CI (Gitea Actions) |
|
||||||
|
| LiteLLM | (dashboard route) | LLM proxy |
|
||||||
|
| OPC | (dashboard route) | Kanban + agents |
|
||||||
|
| n8n | n8n.jimmygan.com | Workflow automation |
|
||||||
|
| Vaultwarden | vault.jimmygan.com | Password manager |
|
||||||
|
| Hermes | hermes-agent.nousresearch.com/docs | AI Agent docs |
|
||||||
|
|
||||||
|
## CI/CD (Gitea Actions)
|
||||||
|
|
||||||
|
### Workflows
|
||||||
|
- **deploy.yml** — Main branch: tests → build → deploy to production
|
||||||
|
- **deploy-dev.yml** — Dev branch: tests → build → deploy to dev
|
||||||
|
|
||||||
|
### Runners
|
||||||
|
- **VPS runner** (`vps` label) — server2 (Oracle Tokyo, 158.101.140.85). Used for build + test jobs. Uses podman.
|
||||||
|
- **NAS runner** (`nas` label) — DS224+. Used for deploy jobs. Uses Docker via `/volume1/@appstore/ContainerManager/usr/bin/docker`. Runs with `nas:host` label (host networking).
|
||||||
|
|
||||||
|
### Build & Deploy Flow
|
||||||
|
1. VPS runner builds Docker image
|
||||||
|
2. Pushes to local registry: `100.78.131.124:5501/nas-dashboard:latest` (podman push --tls-verify=false)
|
||||||
|
3. Streams image to NAS via SSH pipe: `podman save | gzip | ssh nasts "gunzip | docker load"`
|
||||||
|
4. NAS runner deploys: `docker compose up -d`
|
||||||
|
|
||||||
|
### CI Speed Notes
|
||||||
|
- Build is the bottleneck (~12 min first run due to npm ci + Vite build)
|
||||||
|
- npm ci is cached between runs (node_modules saved to disk on VPS)
|
||||||
|
- pip is cached between runs
|
||||||
|
- Registry push enables future layer caching
|
||||||
|
- Synology Docker daemon doesn't support `--tls-verify=false` for pulls; SSH pipe used instead
|
||||||
|
|
||||||
|
### Recent CI Fixes (July 2026)
|
||||||
|
| Fix | Date | Description |
|
||||||
|
|-----|------|-------------|
|
||||||
|
| Checkout URL | Jul 8 | Changed from `gitea:3000` to `127.0.0.1:3300` (NAS host networking) |
|
||||||
|
| TRANSMISSION_USER/PASS | Jul 8 | Added missing env vars to deploy step |
|
||||||
|
| Gzip transfer | Jul 8 | Added gzip compression to SSH pipe |
|
||||||
|
| Local registry | Jul 9 | Build pushes to local registry for layer caching |
|
||||||
|
| NAS runner crash | Jul 8 | `.runner` address changed from `gitea:3000` to `127.0.0.1:3300` |
|
||||||
|
|
||||||
|
## Local Registry
|
||||||
|
|
||||||
|
Registry container: `registry-mirror` on NAS, port 5501.
|
||||||
|
- Standalone mode (NOT Docker Hub proxy — Hub is blocked from China)
|
||||||
|
- Listens on `0.0.0.0:5501`
|
||||||
|
- Images pushed by VPS CI, pulled by NAS fallback via SSH pipe
|
||||||
|
- API: `http://100.78.131.124:5501/v2/`
|
||||||
|
|
||||||
|
## Dashboard Sidebar Layout
|
||||||
|
|
||||||
|
### Main
|
||||||
|
Overview, LiteLLM, OPC, Files, Terminal, Security
|
||||||
|
|
||||||
|
### Media
|
||||||
|
Navidrome, Jellyfin, Audiobookshelf, Immich, t-youtube, Media Management, Transmission
|
||||||
|
|
||||||
|
### Tools
|
||||||
|
Hermes, Chat Digest, Code Sessions, Repos, Gitea Web, Stirling PDF, Vaultwarden, n8n, Speedtest
|
||||||
|
|
||||||
|
All external links use clean HTTPS URLs (no `:8443` port).
|
||||||
|
|
||||||
|
## Cmd+K Palette
|
||||||
|
Press `⌘K` (Mac) or `Ctrl+K` to fuzzy-search all sidebar items. New in July 2026.
|
||||||
|
|
||||||
|
## NAS Quirks
|
||||||
|
- No `crontab` — edit `/etc/crontab` with `sudo` + `printf`, restart crond
|
||||||
|
- No `scp` subsystem — use `cat file | ssh ... 'cat > dest'`
|
||||||
|
- ACLs override Unix perms — use `sudo chmod 777` + `synoacltool -enforce-inherit`
|
||||||
|
- Docker at `/volume1/@appstore/ContainerManager/usr/bin/docker`
|
||||||
|
- Tailscale userspace mode — cannot bind to Tailscale IP directly in Docker
|
||||||
Reference in New Issue
Block a user