diff --git a/compose/README.md b/compose/README.md index 2bedd4d..78c4433 100644 --- a/compose/README.md +++ b/compose/README.md @@ -10,6 +10,7 @@ files: every secret is a `${VAR}` reference resolved from the **server-local |---------|------|----------------|-----------| | `vaultwarden` | us2 (`/opt/vaultwarden`) | `compose/vaultwarden/compose.yml` | static file + `compose-deploy.yml` | | `pdns` | hk2 (`/opt/pdns`) | `compose/pdns/compose.yml` | static file + `compose-deploy.yml` | +| `pgdb` | pgdb (`/opt/database`, 无 ansible) | `compose/pgdb/compose.yml` | static file(手动部署:scp → `docker compose config -q` → `up -d`;服务器文件名 `docker-compose.yml`) | | `adguardhome` | dns.windy.lan (`/opt/adguardhome`) | — (待从 LAN 提取) | static file (pending) | | `unifi` | ubnt (`/home/windy/unifi-9`) | — (待从 LAN 提取) | static file (pending) | | `wireguard` | us4 (`/opt/wireguard`) | `ansible/templates/wireguard-compose.yml.j2` | role-rendered (inventory vars) | diff --git a/compose/pgdb/.env.example b/compose/pgdb/.env.example new file mode 100644 index 0000000..c37281e --- /dev/null +++ b/compose/pgdb/.env.example @@ -0,0 +1,5 @@ +# pgdb compose secrets — copy to /opt/database/.env on the host, chmod 600. +# NEVER commit the real values. Generate: openssl rand -hex 24 +POSTGRES_PASSWORD=change-me-strong-hex +PGWEB_AUTH_USER=pgweb +PGWEB_AUTH_PASS=change-me-strong-hex diff --git a/compose/pgdb/compose.yml b/compose/pgdb/compose.yml new file mode 100644 index 0000000..d8800ea --- /dev/null +++ b/compose/pgdb/compose.yml @@ -0,0 +1,65 @@ +# pgdb (192.168.55.15) — TimescaleDB + pgweb GUI + nightly backup +# +# Deploy: copy this file to /opt/database/docker-compose.yml on pgdb, +# create /opt/database/.env (chmod 600) from .env.example, plus +# /opt/database/pgweb-bookmarks/{hass,scribe}.toml (chmod 600, contains DB password). +# Then: docker compose config --quiet && docker compose up -d +# +# Rollback: previous launch command is kept at /opt/database/run +# (container is stateless; data lives on /srv/pgdata). +services: + timescaledb: + image: timescale/timescaledb:latest-pg18 + container_name: timescaledb + restart: unless-stopped + ports: + - "192.168.55.15:5432:5432" # bind VM IP only (no IPv6 wildcard) + environment: + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - /srv/pgdata:/var/lib/postgresql # data disk (ext4 /dev/sdb1) + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 30s + timeout: 5s + retries: 5 + start_period: 10s + + pgweb: + image: sosedoff/pgweb:latest + container_name: pgweb + restart: unless-stopped + # bind/listen/readonly/sessions/bookmarks-only/bookmarks-dir are CLI flags (no env equivalent in v0.17.0) + command: ["pgweb", "--bind", "0.0.0.0", "--listen", "8081", "--readonly", "--sessions", "--bookmarks-only", "--bookmarks-dir", "/bookmarks"] + ports: + - "192.168.55.15:8081:8081" # LAN only + basic auth (see .env) + environment: + PGWEB_AUTH_USER: ${PGWEB_AUTH_USER} + PGWEB_AUTH_PASS: ${PGWEB_AUTH_PASS} + PGWEB_BOOKMARKS_DIR: /bookmarks + volumes: + - ./pgweb-bookmarks:/bookmarks:ro # bookmark .toml files (contain DB password, keep 0600) + depends_on: + timescaledb: + condition: service_healthy + + pg-backup: + image: prodrigestivill/postgres-backup-local:latest # latest = postgres 18 base (pg_dump 18.x) + container_name: pg-backup + restart: unless-stopped + environment: + POSTGRES_HOST: timescaledb + POSTGRES_DB: "hass scribe postgres" + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_EXTRA_OPTS: "-Fc" # custom-format dumps (pg_restore) + SCHEDULE: "0 2 * * *" # nightly 02:00 (TZ=Asia/Shanghai -> local 02:00) + BACKUP_ON_START: "TRUE" # immediate backup on first start + BACKUP_SUFFIX: ".dump" + HEALTHCHECK_PORT: "80" # go-cron health endpoint for the image healthcheck + TZ: "Asia/Shanghai" # match original host-cron 02:00 local (container default is UTC) + volumes: + - /opt/database/backups:/backups # POSIX fs required; root disk, separate from data disk + depends_on: + timescaledb: + condition: service_healthy diff --git a/docs/lan-overview.md b/docs/lan-overview.md index 4acaca5..1f31575 100644 --- a/docs/lan-overview.md +++ b/docs/lan-overview.md @@ -61,11 +61,12 @@ from each section below. | **dns.windy.lan** | AdGuard Home LAN DNS + Mihomo explicit proxy — **PVE VM 120** (`pihole`) | `ssh -4 windy@192.168.66.36` | `192.168.66.36` | [hosts/dns.windy.lan.md](../hosts/dns.windy.lan.md) | | **ubnt** | UniFi Network Controller — **PVE VM 160** | `ssh -4 windy@192.168.66.46` | `192.168.66.46` | [hosts/ubnt.md](../hosts/ubnt.md) | | **hass.windy.lan** | Home Assistant (HAOS) — **x88 Pro physical box** (LAN55) | `ssh hassio@hass.windy.lan` | `192.168.55.11` | [hosts/hass.windy.lan.md](../hosts/hass.windy.lan.md) | +| **pgdb** | TimescaleDB PG18 (Docker) — HA recorder 后端 — **PVE VM** (LAN55) | `ssh -4 windy@192.168.55.15` | `192.168.55.15` | [hosts/pgdb.md](../hosts/pgdb.md) | | **NAS/FreeNAS** | NAS; `transmission` jail runs here (`.51`) | — | — | — | | **U6 Lite** | UniFi AP (LAN66) | `ssh -4 zhiqiangf@192.168.66.6` | `192.168.66.6` | [docs/unifi-network.md](../docs/unifi-network.md) | | **UAP-AC-Lite** | UniFi AP (LAN55) | `ssh -4 zhiqiangf@192.168.55.5` | `192.168.55.5` | [docs/unifi-network.md](../docs/unifi-network.md) | -> **Positioning facts:** `dns`/`ubnt`/`gfw` are VMs on PVE; `haos` is a **physical x88 Pro +> **Positioning facts:** `dns`/`ubnt`/`gfw`/`pgdb` are VMs on PVE; `haos` is a **physical x88 Pro > box** (HAOS bare-metal, `machine: green`), not a PVE VM (corrected 2026-08-15). > `transmission` is a FreeNAS/NAS jail. Physical SE5420 ports: gw, PVE, haos, NAS, > U6, UAP-AC-Lite, and wired PCs. See [lan-se5420-deployment-guide.md](lan-se5420-deployment-guide.md) §1. diff --git a/hosts/hass.windy.lan.md b/hosts/hass.windy.lan.md index 5a08461..3890272 100644 --- a/hosts/hass.windy.lan.md +++ b/hosts/hass.windy.lan.md @@ -319,6 +319,35 @@ deployed 2026-08-18 from `216cc99` (backup reworked in v0.3.1/v0.3.2 to wait for a peer-initiated inbound SAS from Element with emoji comparison), see [docs/home-assistant-matrix.md § Device verification](../docs/home-assistant-matrix.md). +### Scribe long-term history (verified 2026-08-29) + +- **Scribe 3.8.0** (`/homeassistant/custom_components/scribe/`), configured from + `configuration.yaml` (`scribe:` block; config entry + `01KC2VFJWEQ3XDHY6TQKHPDVRB`, `source: import` — UI "Configure → Advanced" + edits are overridden by the YAML on restart; treat YAML as authoritative). +- TimescaleDB at `192.168.55.15:5432/scribe` (DB user `hass`; host in inventory, + see [hosts/pgdb.md](../hosts/pgdb.md)). Database re-initialized 2026-08-29 14:06 CST + (user-handled; earlier `relation "entities" does not exist` errors resolved). + Health: `binary_sensor.scribe_database_connection`. +- 2026-08-29 config applied (backup `/homeassistant/configuration.yaml.bak-20260829-scribe`): + - `record_events: true` with `include_events` whitelist: `automation_triggered`, + `matrix_e2ee_command`, `matrix_e2ee_message_received`, + `matrix_e2ee_verification_done`, `script_started`, `tag_scanned`, + `mobile_app_notification_action`, `homeassistant_start`, `homeassistant_stop`. + - State noise trimmed: `exclude_domains` update/button; glob + `sensor.zigbee2mqtt_bridge_*`; 4 hassio cpu/mem-percent entities. + - Global `exclude_attributes` drops tianqi `hourly_*` arrays (~19 KB/state — + the recorder-side `_unrecorded_attributes` patch does not apply to Scribe). + - `enable_stats_io` + `enable_stats_size` on → 14 `sensor.scribe_*` stats + entities (`scribe_states_written`, `scribe_events_written`, rates, sizes). + - Verified post-restart 14:23 CST: writer started, `scribe_events_written=1` + (homeassistant_start), states ~110/min, buffer 3, no scribe log errors. +- **Scribe 3.8.0 has no retention option** (upstream feature, newer versions + only). Retention requires upgrading Scribe first; the DB is a fresh 3.x-schema + so an upgrade is safe. Do not expect retention YAML keys to validate on 3.8.0. +- Recorder stays external-Postgres with `purge_keep_days: 30` for daily UI + history; Scribe is the permanent archive. + ## Known issues **Bluetooth hci0 instability — RTL8821CS (verified 2026-08-13, W1N-74):** diff --git a/inventory/hosts.md b/inventory/hosts.md index 323ad3b..962cdb7 100644 --- a/inventory/hosts.md +++ b/inventory/hosts.md @@ -32,6 +32,7 @@ the software deployed there, see [the LAN overview](../docs/lan-overview.md). | **gw** | EdgeRouter X primary LAN gateway | `ssh -4 zhiqiang@192.168.66.254` | `192.168.66.254` | — (EdgeOS, no ansible) | **active** | [hosts/gw.md](../hosts/gw.md) | | **ubnt** | UniFi Network Controller (PVE VM 160) | `ssh -4 windy@192.168.66.46` | `192.168.66.46` | ✓ (ubnt) | **active** | [hosts/ubnt.md](../hosts/ubnt.md) | | **hass.windy.lan** | Home Assistant (HAOS, x88 Pro physical box, LAN55) | `ssh hassio@hass.windy.lan` | `192.168.55.11` | — (HAOS, no ansible) | **active** | [hosts/hass.windy.lan.md](../hosts/hass.windy.lan.md) | +| **pgdb** | TimescaleDB PG18 (Docker) — HA recorder backend (PVE VM, LAN55) | `ssh -4 windy@192.168.55.15` | `192.168.55.15` | — (no ansible) | **active** | [hosts/pgdb.md](../hosts/pgdb.md) | `status: stub` = known to exist; fill `hosts/.md` when next touched.