From 32f76abc9f2eae6db3f96eb84fd2da810ec739a5 Mon Sep 17 00:00:00 2001 From: varutasu <104105839+varutasu@users.noreply.github.com> Date: Fri, 30 Oct 2026 19:38:03 +0000 Subject: [PATCH] fix: give Workspace Chat its own public hostname --- .env.example | 2 +- docker/docker-compose.prod.yml | 2 +- docs/Current Progress.md | 12 +++++++++--- docs/deployment-production.md | 13 +++++++++++-- proxmox/ct116/traefik-ct100.example.yml | 2 +- scripts/chat-deployment.test.mjs | 14 ++++++++++++-- 6 files changed, 35 insertions(+), 10 deletions(-) diff --git a/.env.example b/.env.example index f008d26..7260cbc 100644 --- a/.env.example +++ b/.env.example @@ -178,6 +178,6 @@ CHAT_TICKET_SECRET="CHANGE_ME_USE_A_RANDOM_32_BYTE_SECRET" # Internal collab chat listener port (distinct from Hocuspocus/Yjs on 1234) CHAT_PORT=1235 # Public WSS endpoint, baked into the web bundle at build time. Rebuild web after changing. -NEXT_PUBLIC_CHAT_SERVER_URL="wss://chat.stillwell.cloud" +NEXT_PUBLIC_CHAT_SERVER_URL="wss://echodo-chat.stillwell.cloud" COLLAB_PORT=1234 diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 753f2c0..60c2740 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -12,7 +12,7 @@ # Domains (Dokploy UI or CT 100 Traefik): # web → echodo.stillwell.cloud (port 3000) # collab → collab.echodo.stillwell.cloud (port 1234) -# chat → chat.stillwell.cloud (collab service, CHAT_PORT default 1235) +# chat → echodo-chat.stillwell.cloud (collab service, CHAT_PORT default 1235) # opencode (optional) → opencode.stillwell.cloud (port 4096) # # Build (required on ≤8 GB hosts — parallel export OOMs with exit 255): diff --git a/docs/Current Progress.md b/docs/Current Progress.md index 3278489..fba0c94 100644 --- a/docs/Current Progress.md +++ b/docs/Current Progress.md @@ -1,10 +1,16 @@ # Workspace Chat — Current Progress -Updated: 2026-10-29 (homelab go-live + acceptance). +Updated: 2026-10-30 (dedicated Chat hostname migration pending rebuild). -## Status +## Hostname correction — pending deployment -Chat runtime is live on CT 116; operator reports **14/14 acceptance checks passed**. Operator-confirmed CT 102 `echodo` ledger: **24 entries through `0023`**, with required columns/table verified and all `chat_*` tables owned by `echodo`. An earlier copy of this document said `0022–0024`; no `0024` migration exists in this checkout, so that statement is not treated as verified. +`chat.stillwell.cloud` belongs to OpenMausBot on CT 117, not Workspace Chat. The operator reports routing to that app has been restored and `echodo-chat.stillwell.cloud` is already an alias on the Workspace Chat Traefik rule. + +The Workspace Chat endpoint is now **`wss://echodo-chat.stillwell.cloud`** (socket path `/chat`). Set `NEXT_PUBLIC_CHAT_SERVER_URL` to that value in CT 116's `/opt/submatter/.env`, rebuild the web image, and recreate web as documented in `docs/deployment-production.md`. Repo examples now use only the dedicated hostname. Deployment and browser verification of the new endpoint are not yet confirmed; a push alone will not update the host's `.env`. + +## Previous go-live evidence (before hostname correction) + +The following records the previous deployment, not verification of the new hostname. Operator reported **14/14 acceptance checks passed**. Operator-confirmed CT 102 `echodo` ledger: **24 entries through `0023`**, with required columns/table verified and all `chat_*` tables owned by `echodo`. An earlier copy of this document said `0022–0024`; no `0024` migration exists in this checkout, so that statement is not treated as verified. The deployment evidence below was supplied by the operator and is present in this workspace copy. It was not independently rerun in this reconciliation session; read-only access through PVE stopped at unknown SSH host-key verification. diff --git a/docs/deployment-production.md b/docs/deployment-production.md index 0a31edc..69fe39b 100644 --- a/docs/deployment-production.md +++ b/docs/deployment-production.md @@ -65,7 +65,7 @@ docker compose -f docker/docker-compose.prod.yml \ | `AUTH_AUTHENTIK_*` | | Authentik OIDC app `echodo` | | `COLLAB_SERVER_URL` | `wss://collab.echodo.stillwell.cloud` | Server-side | | `NEXT_PUBLIC_COLLAB_SERVER_URL` | `wss://collab.echodo.stillwell.cloud` | **Build arg** — requires image rebuild | -| `NEXT_PUBLIC_CHAT_SERVER_URL` | `wss://chat.stillwell.cloud` | **Build arg** — requires web image rebuild; provision DNS/TLS first | +| `NEXT_PUBLIC_CHAT_SERVER_URL` | `wss://echodo-chat.stillwell.cloud` | **Build arg** — requires web image rebuild; provision DNS/TLS first | | `CHAT_TICKET_SECRET` | Generate with `openssl rand -base64 32` | Same runtime secret for web and collab; at least 32 characters; never a build arg | | `CHAT_PORT` | `1235` | Second listener inside collab; cannot equal Hocuspocus port `1234` | | `OMNIROUTE_BASE_URL` | `https://omniroute.stillwell.cloud/v1` | Web + worker | @@ -82,7 +82,16 @@ Changing any `NEXT_PUBLIC_*` value requires `bash scripts/deploy-prod.sh build`, Chat runs inside the existing `collab` service on a **separate listener** from Hocuspocus. The worker publishes metadata-only Redis hints; Postgres history and mutation catch-up remain authoritative. Team Chat does not invoke AI agents, and object contexts are not enabled by this deployment. 1. Set the Chat variables above in `/opt/submatter/.env`. Keep the signing secret server-side; Compose requires a nonempty value, and the application requires at least 32 characters. A missing/short secret disables the listener and makes the dual-listener health check fail. -2. Provision `chat.stillwell.cloud` DNS and TLS on CT 100 (canonical browser endpoint). The proxy example also retains `chat.echodo.stillwell.cloud`; a single-level wildcard certificate does not cover that nested hostname. Apply the `echodo-chat` router/service from [`traefik-ct100.example.yml`](../proxmox/ct116/traefik-ct100.example.yml). Traefik supports WebSocket upgrades without a separate service process. If `CHAT_PORT` changes, update the proxy backend port too. Restrict host backend ports to the trusted proxy network. +2. Provision `echodo-chat.stillwell.cloud` DNS and TLS on CT 100 (canonical browser endpoint). `chat.stillwell.cloud` belongs to another app: never include it in the Workspace Chat router. Apply the `echodo-chat` router/service from [`traefik-ct100.example.yml`](../proxmox/ct116/traefik-ct100.example.yml). Traefik supports WebSocket upgrades without a separate service process. If `CHAT_PORT` changes, update the proxy backend port too. Restrict host backend ports to the trusted proxy network. + + For the hostname migration, set `NEXT_PUBLIC_CHAT_SERVER_URL=wss://echodo-chat.stillwell.cloud` in CT 116's `/opt/submatter/.env`, then run from `/opt/submatter`: + + ```bash + docker compose --project-directory /opt/submatter -f docker/docker-compose.prod.yml -f docker/docker-compose.prod.standalone.yml build web + docker compose --project-directory /opt/submatter -f docker/docker-compose.prod.yml -f docker/docker-compose.prod.standalone.yml up -d --no-deps web + ``` + + A git push or container restart alone does not change the baked browser URL. Test Workspace Chat inside `https://echodo.stillwell.cloud`; its WebSocket should connect to `wss://echodo-chat.stillwell.cloud/chat`. The WSS endpoint is not a separate chat UI. 3. Before rolling out, verify the database target is CT 102 **`echodo`** and inspect `drizzle.__drizzle_migrations`. The operator reports 24 ledger entries through `0023`, verified Chat schema, and ownership of all `chat_*` tables transferred to `echodo`. Do not reapply these migrations; inspect the ledger for any future pending entries. Compare the ledger with `packages/database/migrations/meta/_journal.json`; apply only pending journaled migrations using `pnpm --filter @tasks/database db:migrate` in a checkout with the real `DATABASE_URL` exported securely. Do not print the URL, use `db:push`, or replay historical migrations manually. Take a backup first. 4. Verify `0022` added `chat_messages.send_intent_fingerprint` and positive `revision`; verify `0023` added `chat_message_mutations`, its tenant/channel/message foreign keys and indexes, and `chat_channels.next_mutation_sequence`. Do not infer applied schema from TypeScript files. The historical `0017` correction is already local; do not replay it on an initialized database. 5. Validate Compose on the deployment host without printing resolved secrets: diff --git a/proxmox/ct116/traefik-ct100.example.yml b/proxmox/ct116/traefik-ct100.example.yml index 0c1d4e5..55717f8 100644 --- a/proxmox/ct116/traefik-ct100.example.yml +++ b/proxmox/ct116/traefik-ct100.example.yml @@ -26,7 +26,7 @@ http: certResolver: letsencrypt echodo-chat: - rule: Host(`chat.stillwell.cloud`) || Host(`chat.echodo.stillwell.cloud`) + rule: Host(`echodo-chat.stillwell.cloud`) entryPoints: [websecure] service: echodo-chat tls: diff --git a/scripts/chat-deployment.test.mjs b/scripts/chat-deployment.test.mjs index d5c1eed..50b4f42 100644 --- a/scripts/chat-deployment.test.mjs +++ b/scripts/chat-deployment.test.mjs @@ -27,6 +27,16 @@ test("web receives the Chat URL at build time, without baking the signing secret assert.ok(turbo.tasks.build.env.includes("NEXT_PUBLIC_*")); }); +test("Workspace Chat uses its dedicated hostname, not the other app's hostname", () => { + assert.match( + read(".env.example"), + /^NEXT_PUBLIC_CHAT_SERVER_URL="wss:\/\/echodo-chat\.stillwell\.cloud"$/m, + ); + const proxy = read("proxmox/ct116/traefik-ct100.example.yml"); + assert.ok(proxy.includes("Host(`echodo-chat.stillwell.cloud`)")); + assert.ok(!proxy.includes("Host(`chat.stillwell.cloud`)")); +}); + test("web and collab receive the same required runtime signing secret", () => { for (const service of [web, collab]) { assert.match( @@ -57,8 +67,8 @@ test("standalone publication matches the configurable Chat listener", () => { test("Traefik example routes public Chat TLS to the second collab listener", () => { const proxy = read("proxmox/ct116/traefik-ct100.example.yml"); - assert.match(proxy, /Host\(`chat\.echodo\.stillwell\.cloud`\)/); - assert.match(proxy, /Host\(`chat\.stillwell\.cloud`\)/); + assert.match(proxy, /Host\(`echodo-chat\.stillwell\.cloud`\)/); + assert.doesNotMatch(proxy, /Host\(`chat\.stillwell\.cloud`\)/); assert.match(proxy, /service: echodo-chat/); assert.match(proxy, /url: "http:\/\/192\.168\.68\.116:1235"/); });