Docker er driftsformen, ikke bare en testrigg
Announcer skal kjøre standalone på klienten som er koblet til anlegget, i Docker – aldri på utviklingsmaskina. install.sh installerte nativt med apt og uv, og det passer ikke lenger. - install.sh er nå en Docker-bootstrap: installerer Docker om den mangler, lager .env med riktig bruker-ID og portbinding, oppretter data/, bygger, starter, og setter ufw. Den rydder også bort gamle native enheter, både announcer.service og sg-announcer.service. - Dockerfile kopierer koden inn istedenfor å låne den fra et bind-mount, så det som kjører i drift er uavhengig av hva som ligger i mappa. - Alt som skal overleve en ny container ligger i data/ på verten. Bind-mount og ikke navngitt volum, med vilje: da kan man lese data/logs/announcements.jsonl og slippe egne jingler i data/chimes/ uten å gå inn i containeren, og eierskapet følger brukeren istedenfor root. - Testriggen er flyttet til compose.test.yaml, med eget prosjektnavn, så den ikke kan forveksles med eller forstyrre drift. Lyden ut av containeren krever tre ting, og de er dokumentert i AGENTS.md: hele XDG_RUNTIME_DIR montert inn (ikke bare pipewire-socketen – PulseAudio trenger pulse/native og en cookie i samme mappe, og da virker begge lydservere), user: PUID:PGID lik eieren av socketen, og en innlogget grafisk økt. Containeren starter ved boot, altså før innlogging, så avspilling feiler til noen logger inn – det retter seg selv, siden player_cmd() slås opp per melding. Verifisert: lyden spiller fra driftscontaineren. Brannmuren er den viktige endringen fra nativ tid. Dockers publiserte porter settes opp i nat-tabellen, før ufws regler, så en ufw-regel på porten gjør ingenting. Tilgangen styres nå av BIND_IP i .env, og install.sh setter den til NetBird-adressen når wt0 finnes. ufw settes fortsatt for ssh, men er ikke involvert i tilgangen til announcer. Testriggen bindes nå til 127.0.0.1. Den sto med PIN 1234 publisert på 0.0.0.0, og gikk altså forbi ufw ut på LAN-et. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6aaa98d8b1
commit
6c1253f54c
7 changed files with 372 additions and 177 deletions
122
AGENTS.md
122
AGENTS.md
|
|
@ -22,45 +22,98 @@ Filer:
|
|||
| `config.example.toml` | kommentert mal; kopier til `config.toml` |
|
||||
| `docs/crew-integration.md` | kontrakten mot crew-panelet |
|
||||
| `tests/test_auth.py` | `uv run pytest` |
|
||||
| `compose.yaml` + `Dockerfile` | testoppsett, se under. Ikke produksjon |
|
||||
| `Dockerfile` + `compose.yaml` | **drift** – appen kjøres i Docker |
|
||||
| `.env.example` | bruker-ID, portbinding og hemmeligheter |
|
||||
| `compose.test.yaml` | testoppsett med dummy-IdP, se under |
|
||||
| `docker/dummy_idp.py` | falsk crew-panel, så OIDC kan prøves |
|
||||
|
||||
## Kjøring
|
||||
|
||||
Tjenesten er en systemd-brukerenhet: `~/.config/systemd/user/announcer.service`
|
||||
(enabled, port 8090). `systemctl --user restart announcer` etter endringer i
|
||||
koden. Manuelt: `uv run announcer.py --port 8090`.
|
||||
**Announcer kjører i Docker, på en egen klient som er koblet til
|
||||
høyttaleranlegget – aldri på kitsune.** Kitsune er utviklingsmaskin; der er
|
||||
det bare koden som bor.
|
||||
|
||||
Ny maskin (Debian/Ubuntu/Mint): `git clone https://git.1x1.no/kent/announcer.git`
|
||||
og `./install.sh` gjør alt (apt, uv, Python 3.13, stemmer, enhet, ufw). Finnes
|
||||
NetBird (`wt0`) skrur det på ufw med deny incoming og åpner 22, 22022 og porten
|
||||
kun på `wt0`. Kan kjøres på nytt, og rydder da bort en gammel
|
||||
`sg-announcer.service` hvis den ligger der. Avspilling bruker `pw-play` når
|
||||
PipeWire kjører, ellers `paplay` (`player_cmd()`), så Mint 21 med PulseAudio
|
||||
virker også.
|
||||
Ny klient (Debian/Ubuntu/Mint):
|
||||
|
||||
### Omdøpingen: ett punkt gjenstår
|
||||
```bash
|
||||
git clone https://git.1x1.no/kent/announcer.git
|
||||
cd announcer && ./install.sh
|
||||
```
|
||||
|
||||
Mappa er flyttet til `~/Work/announcer`, enheten heter `announcer.service`, og
|
||||
remote peker på `https://git.1x1.no/kent/announcer.git`. **Men repoet i Forgejo
|
||||
heter `annuoncer`** – en skrivefeil. Rett navnet i Forgejo, så virker remote;
|
||||
Forgejo legger selv inn en omdirigering fra det gamle navnet.
|
||||
`install.sh` installerer Docker om den mangler, lager `.env` med riktig
|
||||
bruker-ID og portbinding, oppretter `data/`, bygger bildet, starter
|
||||
containeren og setter ufw. Den rydder også bort en eventuell gammel nativ
|
||||
`announcer.service`/`sg-announcer.service`. Kan kjøres på nytt; en `.env` som
|
||||
finnes blir ikke overskrevet.
|
||||
|
||||
Flytter du mappa igjen: `.venv` har absolutte stier i `pyvenv.cfg` og i
|
||||
shebangen til hvert konsollskript, så `uv sync` alene er ikke nok. Da må
|
||||
`rm -rf .venv && uv sync` til, og tjenesten startes om.
|
||||
Drift:
|
||||
|
||||
```bash
|
||||
docker compose logs -f # følg loggen
|
||||
docker compose restart # etter endringer i data/config.toml
|
||||
docker compose up -d --build # etter endringer i koden
|
||||
cat data/logs/announcements.jsonl # hvem som sendte hva
|
||||
```
|
||||
|
||||
Alt som skal overleve en ny container ligger i `data/` på verten
|
||||
(bind-mount, ikke navngitt volum, så du kan lese loggen og slippe egne
|
||||
jingler i `data/chimes/` uten å gå inn i containeren):
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `data/voices/` | Piper-modellene, ~670 MB, lastes ned ved første oppstart |
|
||||
| `data/cache/` | ferdige WAV-er etter innholdshash. Trygt å slette |
|
||||
| `data/chimes/` | egne jinglefiler |
|
||||
| `data/logs/` | revisjonsloggen |
|
||||
| `data/instance/` | sesjonsnøkkelen |
|
||||
| `data/config.toml` | valgfri. Uten den er sida åpen |
|
||||
|
||||
### Lyden ut av containeren
|
||||
|
||||
Det eneste virkelig uvanlige i oppsettet. Tre ting må stemme:
|
||||
|
||||
1. Hele vertens `XDG_RUNTIME_DIR` monteres inn på samme sti. Ikke bare
|
||||
`pipewire-0`: PulseAudio bruker `pulse/native` pluss en cookie i samme
|
||||
mappe, og ved å ta hele mappa virker begge lydservere uten at compose må
|
||||
vite hvilken maskina har. `player_cmd()` velger `pw-play` eller `paplay`.
|
||||
2. `user: "${PUID}:${PGID}"` må være brukeren som **eier** socketen på
|
||||
verten. Stemmer ikke ID-en, kommer det ingen lyd. `install.sh` fyller inn
|
||||
`id -u`/`id -g`.
|
||||
3. Maskina må være logget inn grafisk, ellers finnes socketen ikke.
|
||||
Containeren starter ved boot, altså før innlogging, og avspilling feiler
|
||||
til noen logger inn. Det retter seg selv: `player_cmd()` slås opp per
|
||||
melding, så første utrop etter innlogging virker uten omstart. Derfor
|
||||
anbefaler `install.sh` automatisk innlogging.
|
||||
|
||||
### Brannmur: BIND_IP, ikke ufw
|
||||
|
||||
**Dockers publiserte porter går forbi ufw.** De settes opp i nat-tabellen,
|
||||
før ufws regler, så `ufw deny` på porten gjør ingenting. Tilgangen styres
|
||||
derfor av `BIND_IP` i `.env`:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `0.0.0.0` | alle nett maskina er på |
|
||||
| `100.75.x.x` | bare NetBird. `install.sh` velger dette når `wt0` finnes |
|
||||
| `127.0.0.1` | bare maskina selv |
|
||||
|
||||
ufw settes fortsatt for maskinens øvrige porter (ssh på `wt0`), men den er
|
||||
ikke involvert i tilgangen til announcer. Dette er en endring fra den native
|
||||
tida, da porten måtte åpnes eksplisitt i ufw.
|
||||
|
||||
## Testing i Docker
|
||||
|
||||
`docker compose up --build` gir to containere:
|
||||
`docker compose -f compose.test.yaml up --build` gir to containere:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `http://localhost:8091` | announcer med PIN, JWT **og** OIDC slått på (`docker/config.test.toml`) |
|
||||
| `http://localhost:9000` | `docker/dummy_idp.py`, som later som den er crew-panelet |
|
||||
|
||||
Port 8091 med vilje, så den ikke kolliderer med tjenesten på 8090.
|
||||
Egen fil, eget prosjektnavn og port 8091, så den ikke kan forveksles med
|
||||
eller forstyrre den som kjører i drift. Begge porter er bundet til
|
||||
`127.0.0.1`, så testriggen – som har PIN `1234` – aldri ligger åpen på
|
||||
LAN-et.
|
||||
|
||||
Dummy-IdP-en finnes fordi crew-panelet ikke har OIDC ennå: uten den kan
|
||||
OIDC-veien ikke prøves i det hele tatt. Den kjører en ekte
|
||||
|
|
@ -80,9 +133,11 @@ Lyd: `/run/user/1000` er montert inn, så `pw-play` i containeren spiller på
|
|||
vertens anlegg. Kommenter ut den linja i `compose.yaml` hvis du bare skal
|
||||
teste innlogging.
|
||||
|
||||
`UV_PROJECT_ENVIRONMENT=/opt/venv` i bildet er ikke pynt: `/app` er et
|
||||
bind-mount av repoet, og uten det ville containerens `uv sync` overskrevet
|
||||
`.venv` som den kjørende tjenesten bruker.
|
||||
`compose.test.yaml` bind-monterer koden, så endringer slår inn med en
|
||||
restart. Driftsbildet kopierer koden inn i stedet, så det som kjører er
|
||||
uavhengig av hva som ligger i mappa. `UV_PROJECT_ENVIRONMENT=/opt/venv` er
|
||||
derfor ikke pynt: med bind-mount ville containerens `uv sync` ellers
|
||||
overskrevet `.venv` på verten.
|
||||
|
||||
## Konfigurasjon
|
||||
|
||||
|
|
@ -138,6 +193,14 @@ Uten den logges alle ut ved hver `systemctl restart`. `SESSION_COOKIE_SECURE`
|
|||
settes bare når `server.behind_tls` er sann – appen kjører på rå HTTP over LAN
|
||||
og NetBird, og med Secure på ville cookien aldri blitt sendt.
|
||||
|
||||
## Utvikling på kitsune
|
||||
|
||||
`uv run announcer.py --port 8099` kjører appen direkte, uten Docker. Det er
|
||||
veien for å prøve en endring kjapt; `uv run pytest` for testene. Men
|
||||
**ingen tjeneste skal stå og kjøre på kitsune** – det var slik det var før, og
|
||||
det er ryddet bort. Skal noe prøves med innlogging, bruk
|
||||
`compose.test.yaml`.
|
||||
|
||||
## Hvordan det henger sammen
|
||||
|
||||
- `/api/config` er alt sida trenger for å tegne seg selv: branding, stemmer,
|
||||
|
|
@ -187,10 +250,15 @@ Vil Kent ha enda bedre lyd: ElevenLabs (nøkkel, best norsk) eller OpenAI
|
|||
|
||||
## Feller
|
||||
|
||||
- **ufw**: appen kjører nativt, så Docker-smutthullet gjelder ikke. Porten må
|
||||
åpnes eksplisitt for LAN-et (`sudo ufw allow from 10.20.70.0/23 to any port 8090 proto tcp`).
|
||||
- **ufw styrer ikke Docker-porter.** Se «Brannmur» over: tilgangen avgrenses
|
||||
med `BIND_IP` i `.env`. En `ufw deny` på porten gir falsk trygghet.
|
||||
- **Ingen lyd?** Nesten alltid `PUID`/`PGID` i `.env` som ikke er brukeren
|
||||
som eier lydsocketen, eller at maskina ikke er logget inn grafisk. Sjekk
|
||||
hva containeren velger:
|
||||
`docker compose exec announcer python -c "import announcer,pathlib;print(announcer.player_cmd(pathlib.Path('/tmp/x.wav')))"`
|
||||
- `pkill -f announcer.py` fra et skall som selv har `announcer.py` i
|
||||
kommandolinja dreper skallet. Bruk `systemctl --user`.
|
||||
kommandolinja dreper skallet. Gjelder utvikling med `uv run`; i drift er
|
||||
det `docker compose`.
|
||||
- `player = Player()` står på modulnivå, så avspillingstråden starter ved
|
||||
`import announcer` – også i `install.sh`, som importerer modulen for å laste
|
||||
ned stemmer. Harmløst, men greit å vite.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue