Commit graph

77 commits

Author SHA1 Message Date
68e6c951fd settings: re-assert DB role password on every deploy, not just first install
Hit in production 2026-08-13: settings crash-looped with 'password
authentication failed' after a routine --only settings redeploy. The
settings Postgres role was only ever created once, in deploy_postgres()'s
init SQL (fresh-volume-only) — so if CREDS_FILE's SETTINGS_DB_PASS ever
drifted from the role's actual password, redeploying settings had no way
to self-heal.

Mirrors the CREATE-then-fallback pattern other apps' deploy functions use,
but falls back to ALTER instead of swallowing the error, since CREATE
failing because the role already exists doesn't fix a drifted password.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 12:21:02 +00:00
2088cb5577 deploy_kds: scoped DB role instead of sharing kitchen's credential
- Generate KDS_DB_PASS (matching KITCHEN_DB_PASS's pattern), add to --only
  dispatch's _append_secret list
- Create a scoped `kds` Postgres role: SELECT/INSERT/UPDATE/DELETE on
  kds_tickets/kds_course_bumps, read-only on resos_bookings/resos_opening_hours,
  column-scoped SELECT+UPDATE on just the kds_* columns of kitchen_settings
  (not the NewBook/ResOS/Nextcloud/Dext/SambaPOS/Azure/Anthropic credentials
  that live in the same table). Runs in two passes since kds_tickets/
  kds_course_bumps don't exist until KDS's own migrations create them.
- kds .env now carries DATABASE_URL (scoped `kds` role, runtime) and
  MIGRATION_DATABASE_URL (privileged `kitchen` role, migrations only)
- Fix KDS theme_color seed (teal -> orange, matched kitchen's tile before)

See kitchen-port-log.md E17.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 14:45:07 +00:00
dbf9e90164 Move plant to Hotel category instead of Operations
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 22:25:20 +00:00
5de8105242 Add plant to --only dispatch's VALID allowlist
deploy_plant() and its --only case entry were already wired in, but
the separate VALID allowlist string gating --only wasn't updated,
so --only plant failed with "Unknown service 'plant'".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 22:14:53 +00:00
046d74ad82 Add deploy_plant() for the new plant-room monitoring app
LXC 123, no admin-VLAN NIC needed (plant only talks to the shared
MQTT broker over the internal network). Wired into --only dispatch
and the main deploy sequence after deploy_calendar.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 22:05:00 +00:00
c6c67be8af Thread MQTT admin credentials into settings, reorder broker before it
deploy_settings() now copies the shared deploy SSH key (same as
deploy_management()) so settings can SSH into LXC 104 to manage
dynamic-security clients, and writes MQTT_BROKER_HOST/MQTT_ADMIN_USER/
MQTT_ADMIN_PASS into its .env from the credentials file.

Moved deploy_mqtt_broker() before deploy_settings() in the full-install
sequence so a fresh install has the admin credentials already generated
by the time settings needs them — previously only worked via a later
`--only settings` redeploy after the broker existed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 17:12:16 +00:00
8b2dce9714 Add MQTT broker smoke-test script
Creates a throwaway dynsec client+role scoped to a private topic,
publishes/subscribes to confirm round-trip delivery, checks a bogus
login is rejected, then cleans up. Syntax verified against a real
eclipse-mosquitto:2 + dynamic-security broker locally before writing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 13:58:25 +00:00
7e9fe62e1c Fix mosquitto data/log dir ownership for non-root container user
eclipse-mosquitto:2 runs as its own uid/gid 1883 and doesn't chown
bind-mounted volumes itself, so the root-owned host dirs from mkdir -p
left it unable to write its log file — broker was stuck restart-looping.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 13:50:21 +00:00
705b3a0561 Fix mosquitto.conf dynamic-security plugin_opts syntax
Mosquitto 2.1.2 config format has no nested-block syntax; plugin
options must be flat plugin_opt_<name> directives, not a
plugin_opts { } block. Broker was crash-looping on every start.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 13:45:08 +00:00
e712352ed6 Fix utilities app portal category to match auth/src/db.js ('Hotel')
Was seeded as 'Operations' here, drifting from the auth service's
seed which uses 'Hotel' — both ON CONFLICT-update the same column, so
whichever ran last silently changed the portal grouping.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 12:59:23 +00:00
e8dea786d5 Add deploy_calendar() — LXC 126, calendar_db, calendar app
calendar was provisioned ad hoc via add-app.sh and never got the
standard installer wiring other apps have (deploy_<slug>() + --only
dispatch), so it couldn't be redeployed/rebuilt the normal way.
2026-07-27 16:56:53 +00:00
20e0c52ea0 Add deploy_hvac() with dual-homed admin-VLAN support
LXC 128, hvac_db, hvac app — plus generic dual-NIC support in create_lxc()
(admin VLAN bridge/tag/IP asked lazily via whiptail, persisted per-hotel in
the credentials file, since these vary per site). Used now for hvac's future
Modbus/Midea/Daikin direct-LAN drivers; the shared MQTT broker LXC will reuse
the same ensure_admin_vlan_config()/ensure_admin_vlan_ip() helpers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 20:19:24 +00:00
2fd6305e4f Add deploy_utilities() — LXC 127, utilities_db, utilities app
Runs before deploy_reports so UTILITIES_API_KEY exists when reports'
.env is written on a fresh install.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 17:33:34 +00:00
053945950a Revert Open category back to Operations (autocorrect typo) 2026-07-26 12:48:07 +00:00
ea5abf7bae Keep fresh-install maintenance seed category in sync with Open group 2026-07-25 21:45:05 +00:00
2a9f66b4bf add-app.sh: disable apparmor_parser in new app LXCs, matching install-stack.sh
add-app.sh had a separate Docker-install block that never got the AppArmor
fix from install-stack.sh's install_docker() (commit 9a45e39) — every app
added individually via add-app.sh since then was exposed to Docker builds
failing with "docker-default profile could not be loaded ... while confined".
Hit this deploying the calendar app to LXC 126 on the dev stack.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 18:04:32 +00:00
52e542d56c Add deploy_wages() — LXC 124, wages_db, wages app
Creates wages_db and deploys the wages app on LXC 124 (10.10.10.124).
Wired into --only wages dispatch, _append_secret, and full deploy sequence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-23 09:04:15 +00:00
a40bf2f8cf Replace forecasting_db grants with FORECASTING_API_KEY in reports deploy
Reports no longer queries forecasting_db directly — it calls the
forecasting public API. Remove GRANT SELECT and FORECAST_DATABASE_URL;
add FORECASTING_URL and FORECASTING_API_KEY (provisioned separately in
the forecasting app's API key manager).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-20 10:36:08 +00:00
fe0e5f5e71 Add forecasting_db grants + FORECAST_DATABASE_URL to reports deploy
Reports LXC now gets SELECT grants on forecasting_db tables (bookings
stats, net revenue, forecasts, budgets) and the FORECAST_DATABASE_URL
env var so the Directors Forecast section can cross-query the forecasting
DB. Also adds 'edit' capability to the auth seeding block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-20 10:24:59 +00:00
c2454b493d Add deploy_directors_report function (LXC 123)
Creates directors_report_db, grants SELECT on forecasting_db tables
(newbook_bookings_stats, newbook_net_revenue_data, forecasts, monthly_budgets,
daily_budgets), wires FORECAST_DATABASE_URL env var.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-20 10:10:17 +00:00
2343c8eff9 Remove BACKUP_DATABASES from management .env — backup script auto-discovers from postgres 2026-07-14 11:49:50 +00:00
f14e06b401 Fix BACKUP_DATABASES: correct hk_planner_db name, add all deployed databases 2026-07-14 11:37:52 +00:00
4d521f8026 Fix management .env: add SETTINGS_SECRET, SETTINGS_URL, PG_SUPERPASS
Previously the management .env was missing SETTINGS_SECRET (needed by
the backup service to fetch Nextcloud creds) and used stale variable
names (BACKUP_PG_PASS, BACKUP_REMOTE) that the docker-compose never read.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 11:04:05 +00:00
0275641243 Add deploy_kitchen (LXC 110) + deploy_kds (LXC 125) functions
- deploy_kitchen: 2 GB RAM, 10 GB disk; creates kitchen_db (shared with kds);
  FastAPI backend with MSSQL ODBC drivers (~5 min build); seeds 10 caps into auth DB
- deploy_kds: 1 GB RAM; connects to kitchen_db (no separate DB — KDS shares schema);
  slim FastAPI build (httpx only, no MSSQL ODBC); seeds 3 caps + Staff role grants
- KITCHEN_DB_PASS added to _append_secret section for --only deploys
- Both added to case dispatch and full deploy sequence

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-11 11:26:13 +00:00
5c345e47a9 Add deploy_reports function (LXC 122 · 10.10.10.122)
Creates reports_db, deploys reports app to LXC 122, seeds auth DB with
app entry and capabilities, registers /reports/ location in NPM.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-06 12:58:42 +00:00
9cf7ed6d70 Raise rates LXC to 4 GB RAM — Chromium scrapes wedged the 2 GB container
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 13:23:06 +00:00
9bae610585 fix: add rates to --only VALID services list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-05 12:12:51 +00:00
e600e1b531 Add deploy_rates for Rate Monitor (LXC 115)
2 GB RAM, 2 CPU, PostgreSQL rates_db. Mirrors deploy_forecasting pattern.
Seeds rates app + 6 caps. Adds npm proxy route /rates/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-05 12:07:03 +00:00
cbdc3bff96 forecasting .env: SETTINGS_URL/SECRET for central NewBook creds; drop NEWBOOK_* vars
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 19:36:42 +00:00
f7ace8f5ef fix: add forecasting to --only VALID services list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 18:52:04 +00:00
4d0fd93b6c Add deploy_forecasting (LXC 113 · 10.10.10.113) + --only dispatch
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 13:37:06 +00:00
b3af8d8849 Add deploy_maintenance (LXC 121 · 10.10.10.121) + --only dispatch
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 21:29:08 +00:00
7e317e9d4e room-planner: seed auth DB via psql on LXC 100, not docker run
The docker run approach failed (image tag/context issues); use the same
direct-psql pattern as add-app.sh which is proven reliable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 16:17:27 +00:00
988b8390f2 fix: seed room-planner app+capabilities into auth DB after deploy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 13:18:49 +00:00
5332f1449d fix: remove NEWBOOK_LOCATION_ID from room-planner deploy — not needed for single-property setup
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 13:11:00 +00:00
7758e67f5b feat: add room-planner to --only deploy (LXC 120)
Adds deploy_room_planner() function for LXC 120 / 10.10.10.120.
Wires it into --only room-planner, postgres init SQL (07-room-planner.sql),
gen_secrets, credentials file template, and the main deploy sequence.
NEWBOOK_LOCATION_ID written to .env with a warning if unset.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-03 13:07:07 +00:00
a35deb40bf Rename twin-optimiser repo: hk-twin-optimiser → twin-optimiser (match slug)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 20:56:52 +00:00
4219a801bf Add twin-optimiser service (LXC 119) to installer and --only mode
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 20:11:10 +00:00
70ffd218a1 NPM: locate proxy host via python3 JSON parse, not brittle grep
The proxy-host lookup grepped for '"id":N,"domain_names":[...]' in a
fixed field order that NPM's JSON doesn't guarantee, so the host was never
found ("proxy host not found"). Replace both the cashup and hk-planner
inline blocks with a shared npm_add_location() helper that parses the
proxy-hosts list with python3 and matches DOMAIN inside domain_names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 14:21:01 +00:00
eadbb383e7 NPM: try all creds-file admin pairs + factory default for token
npm_get_token now attempts the sourced NPM_ADMIN_* pair, then every
email/pass pair present in the credentials file, then admin@example.com/
changeme. Makes proxy-host automation resilient to duplicate, reordered,
or placeholder NPM entries regardless of how they got there.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 14:16:05 +00:00
d4b28dd41b NPM: use stable internal IP 10.10.10.3 for all API calls
The NPM API was reached via NPM_LAN_IP, which breaks when that value is a
placeholder or unset (and the :-10.10.10.103 fallback was wrong — the
internal IP is .3, not .103). NPM listens on all interfaces, so the host
can always reach it at 10.10.10.3:81 over vmbr1. NPM_LAN_IP now only drives
user-facing messages and the NPM LXC's LAN net0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 14:14:42 +00:00
95632f22c9 Fix --only auth/noticeboard: OFFICE_IP unbound under set -u
In --only mode the creds file provides OFFICE_IP_CHECK, not OFFICE_IP,
so the bare ${OFFICE_IP} tripped 'set -u'. Use the same tolerant
${OFFICE_IP_CHECK:-${OFFICE_IP:-disabled}} form as cashup/hk-planner.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 14:11:37 +00:00
95b3741d23 Add hk-planner service (LXC 118) to installer and --only mode
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 13:58:51 +00:00
b124abf8c4 Inject SETTINGS_URL/SECRET into auth env and AUTH_URL into noticeboard env at deploy time
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 00:24:11 +00:00
01abc9c10b Fix NPM_LAN_IP unbound and cashup health check path
- Save NPM_LAN_IP to creds file and reload it in --only mode
- npm_get_token and deploy_cashup NPM patch fall back to 10.10.10.103
  (internal vmbr1 IP) when NPM_LAN_IP is unset
- Fix health check URL: /cashup/api/health → /cashup/health
  (nginx proxies /cashup/health to backend:3001/health; /cashup/api/
  proxies to /api/ which has no /health route)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 21:19:27 +00:00
6bf5f09fe8 Add /cashup/ NPM location and npm_get_token helper
- Add manage@hotel.com NPM credentials to creds file template and
  reload block (NPM_ADMIN_EMAIL was missing from the --only path)
- Extract npm_get_token() so both configure_npm_proxy_hosts and
  deploy_cashup share one auth call
- deploy_cashup now patches the live NPM proxy host to add /cashup/
  → 10.10.10.117:3083 after containers are healthy (idempotent)
- /cashup/ also added to the fresh-install locations array

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 21:09:06 +00:00
5f06519a9a Expose docker compose build errors; remove silent &>/dev/null suppression
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 20:04:55 +00:00
8f5ae3af74 Fix deploy_service set -e exit swallowing errors in command substitution
Using 'if ! pct exec ... > tmp 2>&1' avoids the bash set -e + $()
interaction where the shell exits inside the subshell before || fires.
Errors are now captured to a temp file and printed via msg_error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 20:01:49 +00:00
4fe4e30807 Fix deploy_service to expose git clone errors instead of swallowing them
Removed &>/dev/null suppression; output is now captured and shown in
msg_error with a debug command when clone or pull fails.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 19:52:40 +00:00
86dd283acd Self-generate missing DB passwords in deploy functions for --only installs 2026-07-01 19:46:08 +00:00