# Hostname cutover to sibyla.pt — plan and run record (2026-09-10)

Owner instruction (2026-09-10): replace the public `*.gottsolutions.net` endpoints of the platform
and its companions with the already-created `*.sibyla.pt` names. DNS for every new name resolves to
the POC server's public address (verified before any change). Postiz was added to the list by the
owner mid-session.

| New host | Replaces | IIS site (id) | What answers |
|---|---|---|---|
| `login.sibyla.pt` | `login.gottsolutions.net` | `login.gottsolutions.net` (1) | GOTT.IdentityServer (OpenIddict), `C:\SibylaApps\IdentityServer` |
| `app.sibyla.pt` | `sibyla.gottsolutions.net` | `Sibyla.Web` (10) | Sibyla web host, release under `C:\Apps\Sibyla\web` |
| `api.sibyla.pt` | `api.sibyla.gottsolutions.net` | `Sibyla.Api` (11) | channel-intake API |
| `docs.sibyla.pt` | `docs.sibyla.gottsolutions.net` | `Sibyla.Docs` (12) | static docs over this repo's `docs\` |
| `nextcloud.sibyla.pt` | `nextcloud.gottsolutions.net` | `nextcloud.gottsolutions.net` (3) | reverse proxy → docker `nextcloud-app` :8081 |
| `dashboard.sibyla.pt` | `dashboard.gottsolutions.net` | `dashboard.gottsolutions.net` (4) | reverse proxy → Hermes dashboard :9119 |
| `buzz.sibyla.pt` | `buzz.gottsolutions.net` | `buzz.gottsolutions.net` (9) | reverse proxy → docker `buzz-prod-relay-1` :3001 |
| `postiz.sibyla.pt` | `postiz.gottsolutions.net` | `postiz.gottsolutions.net` (8) | reverse proxy → docker `postiz` :4007 |

Out of scope (unchanged): `mcpserver.gottsolutions.net`, the retired `legacy.*` names (no IIS
sites remain; stale SNI entries only), `mattermost.gottsolutions.net` (IdP client exists, nothing
runs), mailboxes and e-mail addresses under `gottsolutions.net`, the GitLab group.

## Inventory (what references the old names)

- **IdP** — `C:\SibylaApps\IdentityServer\appsettings.Production.json`: `IdentityServer:Issuer`,
  `AllowedHosts`, `Cors:AllowedOrigins`, the disabled `Seed` client. Clients in
  `gott_identity.OpenIddictApplications`: `apollo-web` (Sibyla web), `hermes-dashboard`, `postiz`,
  `Identityserver-web` (the IdP's own UI; also `Properties` holds CORS origin + front-channel logout).
- **Relying parties on the issuer** — `C:\ProgramData\Sibyla\secrets\web.json` (`Oidc:Authority`);
  the API release's `appsettings.json` (`Identity:Authority`, no secrets override today); Hermes
  `.env` (`APOLLO_CHANNEL_INTAKE_AUTHORITY`, `SIBYLA_CHANNEL_INTAKE_AUTHORITY`), Hermes
  `config.yaml` (`dashboard.oauth.self_hosted.issuer`, `dashboard.public_url`), the installed plugin
  `plugins\apollo-channel-intake\client.py` defaults, `C:\SibylaApps\postiz\secrets\postiz.env`
  (`POSTIZ_OAUTH_*`), `C:\SibylaApps\Apolo-agent\ps1\Set-ChannelIntakeSecret.ps1`.
- **Own-host settings** — Nextcloud `D:\nextcloud\config\config.php` (`trusted_domains`,
  `overwritehost`, `overwrite.cli.url`) and `D:\nextcloud\.env`; Buzz
  `C:\SibylaApps\buzz\deploy\compose\.env` (`BUZZ_DOMAIN`, `RELAY_URL`, media + CORS); Hermes `.env`
  `BUZZ_RELAY_URL`, `APOLLO_CHANNEL_INTAKE_API_BASE_URL`; Postiz `MAIN_URL`/`FRONTEND_URL`/
  `NEXT_PUBLIC_BACKEND_URL`; Hermes `buzz\channel-cursors.json` records the relay URL.
- **TLS** — win-acme renewals: `GOTT.login` (site 1), `[IIS] nextcloud` (site 3), `[IIS] dashboard`
  (site 4), `[IIS] postiz` (site 8), `[IIS] buzz` (site 9), `[Platform] sibyla|api.sibyla|docs.sibyla`
  (manual source, IIS install to sites 10/11/12). Store `WebHosting`, self-hosted http-01. The
  scheduled renewal already ran today (11:37 UTC, result 0; next 2026-09-11 12:57 UTC).
- **Repository** — defaults in `src/Sibyla.Web/Program.cs`, `src/Sibyla.Api/Program.cs` +
  `appsettings.json`, `StorageAdmin.razor` placeholder; deployment module defaults
  (`TestSibylaProductionCommand.cs`, `Invoke-SibylaDeployment.ps1`, `Test-SibylaProduction.ps1`,
  oracles + probe `e2-execute-v2.ps1` with the old certificate thumbprints), `provision-production.ps1`,
  `deploy-release.ps1` (retired), `Deploy-IdentityServer.ps1` health URL, `RegisterIdpClients`,
  `local/hermes` plugin sources + tests, `local/artifacts/*.mjs` smoke scripts,
  `local/secrets/idp-clients.json` (gitignored). Dated docs keep their history; living docs get a note.
- **Database** — `gott_sibyla` holds no old hostname in any text/json column (scanned every table);
  `stgcfg` (storage targets) is empty, so no tenant Nextcloud URL to rewrite.
- **Not affected** — `hosts` file has no overrides; the worker never calls the IdP; web/worker
  `AllowedHosts` is `*`; Nextcloud has no OIDC app installed.

## Decisions taken in this run

1. **Hard cutover of the issuer** with every relying party repointed in the same window. OpenIddict
   has one issuer, so `login.gottsolutions.net` cannot keep answering as an IdP. Existing user
   sessions end (cookies are per host); machine clients mint new tokens on their next call.
2. **Old names keep answering as 301 redirects** from one dedicated IIS site (`Redirect.gottsolutions.net`,
   `C:\Apps\Redirects\gottsolutions.net`) that owns the old bindings and reuses the old certificates,
   with one win-acme renewal covering the eight old names so the redirects outlive the current
   certificates. Reversible by moving the bindings back.
3. **One certificate per new host**, issued by win-acme with the platform pattern (manual source,
   self-hosted validation, `WebHosting` store, IIS installation into the site), friendly name
   `[sibyla.pt] <host>`. The eight old renewals are cancelled once their bindings leave the sites
   (an IIS-source renewal would otherwise re-create the old binding on the app site).
4. **IIS site and pool names stay** (`login.gottsolutions.net`, `nextcloud.gottsolutions.net`, …).
   Pool names are identities (`IIS AppPool\<name>`) that folder ACLs reference; renaming is a
   separate, optional owner task. Scripts keying on the site name keep working.
5. The API gets `Identity:Authority` in its secrets file (`api.json`) so the running release follows
   the cutover now; the repository default changes for the next release.
6. `GOTT.IdentityServer` (separate repository) is not edited: its working tree carries someone's
   uncommitted local change, and `Deploy-IdentityServer.ps1` preserves the deployed
   `appsettings.Production.json` across releases, so the deployed file is the source of truth.

## Sequence

0. Backups: IIS configuration backup, `.pre-sibyla-pt.bak` beside every edited file, SQL dump of the
   four IdP client rows, under `D:\Backups\SibylaDeployment\<stamp>-sibyla-pt`.
1. IdP clients: add the new redirect / post-logout URIs and CORS origins (additive).
2. Certificates: eight win-acme renewals → HTTPS+SNI bindings on the eight sites; add the HTTP
   bindings; verify each new host over TLS.
3. Issuer cutover: IdP settings → recycle pool → discovery shows `https://login.sibyla.pt`.
4. Relying parties: web/api secrets → recycle pools; Hermes `.env` + `config.yaml` + plugin default;
   Postiz env → recreate container; Set-ChannelIntakeSecret defaults.
5. Own-host settings: Nextcloud config + `.env` → restart containers; Buzz `.env` → recreate relay;
   Hermes cursor file; restart Hermes gateway + dashboard.
6. Redirect site: create, move the old bindings, verify 301s; cancel old renewals; create the
   redirect renewal.
7. Verification: TLS + status on all eight new hosts, discovery issuer, authenticated smoke sign-in
   on `app.sibyla.pt`, client-credentials token + API probe, Nextcloud status, Postiz/Buzz/dashboard
   answer, 301s from the old hosts.
8. Repository: defaults, scripts, module oracles/probe (new thumbprints), smoke scripts, tests,
   docs; commit.
9. Remove the old redirect URIs from the IdP clients (after step 7 passes).

## Run record (2026-09-10, 13:35–14:25 UTC, agent session at the owner's instruction)

**Result: all eight names live on `sibyla.pt`; the old names redirect; sign-in, the machine
client, Nextcloud, Postiz, Buzz and the dashboard verified; nothing rolled back.**

| Step | Done (UTC) | Evidence |
|---|---|---|
| 0 Backups | 13:45 | IIS backup `SibylaPt-20260910-134533`; `D:\Backups\SibylaDeployment\20260910-134533-sibyla-pt` (12 config files + the four IdP client rows as `openiddict-applications-before.txt`); `.pre-sibyla-pt.bak` beside every edited file. |
| 1 IdP clients (additive) | 13:46 | `apollo-web`, `hermes-dashboard`, `postiz`, `Identityserver-web` carried both old and new URIs until step 9. |
| 2 Certificates | 13:47–13:50 | Eight win-acme renewals `[sibyla.pt] <host>` (manual source, self-hosted http-01, `WebHosting`, IIS install), Let's Encrypt, expiring 2026-12-09, next renewal after 2026-11-04. HTTPS+SNI and HTTP bindings on sites 1, 3, 4, 8, 9, 10, 11, 12. Thumbprints: login `6EA7CF87…`, app `B5F278E4…`, api `C31F7CEB…`, docs `F78977E8…`, nextcloud `BACF199C…`, dashboard `BEA042A2…`, buzz `C83C60D5…`, postiz `80741AC7…`. |
| 3 Issuer cutover | 13:51 | `appsettings.Production.json` (7 replacements), pool recycled; discovery on `login.sibyla.pt` answers `issuer https://login.sibyla.pt/`, one signing key. |
| 4 Relying parties | 13:51–13:56 | `web.json` `Oidc:Authority`; `api.json` gained `Identity:Authority`; Hermes `.env` (two authorities, API base, Buzz relay), `config.yaml` (dashboard issuer + public URL), plugin `client.py` defaults, `Set-ChannelIntakeSecret.ps1`; Postiz env → container recreated; Hermes gateway restarted (PID 1164) and dashboard task restarted (it had been down before this work: nothing listened on 9119; it listens now). |
| 5 Own-host settings | 13:52–13:54 | Nextcloud `config.php` + `.env` → `app` container recreated (`status.php` installed/no maintenance, login page 200); Buzz `.env` → relay recreated (relay info answers on the new name); `buzz\channel-cursors.json` relay updated. |
| 6 Redirect site | 14:05–14:12 | IIS site `Redirect.gottsolutions.net` (id 5, pool of the same name, `C:\Apps\Redirects\gottsolutions.net`, rewrite map old→new, 301 with query string, ACME path passthrough). The eight old bindings moved with their certificates; then win-acme renewal `[Redirect] gottsolutions.net retired hosts` (IIS source, site 5) issued one certificate for the eight old names (expires 2026-12-09) and rebound them. The eight old renewals cancelled by id; `GOTT.MCPServer` and `Apollo GOTT HTTPS` untouched. |
| 7 Verification | 14:00–14:15 | Every new host serves a valid Let's Encrypt chain. `app.sibyla.pt/` 200; `/account/login` 302 to `login.sibyla.pt/connect/authorize` with `redirect_uri=https://app.sibyla.pt/signin-oidc`. Headless browser sign-in with the UI test account: landed on `login.sibyla.pt/en/Account/Login`, returned to `app.sibyla.pt/argus/documents/uploads` with the `sibyla.auth` cookie, sign-out reached `login.sibyla.pt/connect/logout`. Client-credentials token from `login.sibyla.pt` (`iss https://login.sibyla.pt/`, `aud apollo-channel-intake`) accepted by `api.sibyla.pt` (anonymous 401, token 404 on an unknown registration). `api.sibyla.pt/health/ready` 200, `docs.sibyla.pt` 200, `nextcloud.sibyla.pt` 302→login 200, `postiz.sibyla.pt` 307→`/auth` 200, `dashboard.sibyla.pt` 302→`/auth/login?provider=self-hosted`, `buzz.sibyla.pt` 200. All eight old hosts: HTTPS 301 to the new host with path and query kept; HTTP 301 to HTTPS first (the pre-existing server-level "Redirect HTTP to HTTPS" rule), then 301 to the new host. |
| 8 Repository | 14:20 | Defaults, scripts, module oracles and the E2 probe (new hostnames and thumbprints), plugin sources and tests, smoke scripts, glossary, this record. `Sibyla.Web`/`Sibyla.Api` build clean; deployment module tests 1266/1266. |
| 9 IdP cleanup | 14:16 | Old URIs removed from the four clients; `mattermost` and the legacy `sibyla-web` client keep their old names (out of scope). |

Two mistakes caught during the run, both fixed before anything depended on them:

- `sed -i` replaces a file by rename, so `web.json` lost the explicit `IIS AppPool\Sibyla.Web:(R)` grant
  (Sibyla.Web answered 500.30 for about a minute) and the Hermes `.env` lost its restricted ACL
  (inherited entries, including `CodexSandboxUsers`, appeared). Both restored (`icacls`); every other
  edited file has only inherited entries, identical to its siblings. Three files also came back with LF
  instead of CRLF; all edited files were regenerated byte-for-byte from their backups with only the
  hostnames replaced (`api.json` was rewritten by PowerShell and kept its grant).
- The first API probe used the nil GUID, which the route rejects (400) before authentication; the
  probe with a random GUID and the correlation header is the evidence above.

### Follow-up 2026-09-10, ~15:00 UTC — docs site serves files by name only

Owner instruction: `docs.sibyla.pt` must answer known file names and never list a folder.

- Live: `directoryBrowse enabled="false"` in the `Sibyla.Docs` location of applicationHost
  (`appcmd … /commit:apphost`). Verified: `/` and `/discovery/` → 403; `/naming-glossary.md`,
  `/sibyla-pt-hostname-cutover-260910.md`, `/discovery/09-storage-architecture.md` → 200
  `text/markdown`. No default document exists in `docs\`, so the root stays 403.
- The deployment module re-applies the docs location on every Configure, so the same change is in
  `IisWindowsAdapterV2.ApplyAttributes` (now `false`), `provision-production.ps1`, the E2 adapter
  probe (`docs-location-settings` expects `False`), and health intent Http 5 now expects **403** at
  `<DocsBaseUri>/` (spec row 10 amended; `ExpectedStatus` accepts `403` — value type, validator
  oracle `validators.v2.json`, `health.v2.json`, spec §4). Module suite 1267/1267 after the change.
  Without the module change the next deployment would have re-enabled listing and then failed its
  own health check (root 403 ≠ 200) and rolled back.

### Left for the owner

- Buzz desktop/mobile clients must be repointed to `wss://buzz.sibyla.pt` by their users (the relay
  answers a WebSocket upgrade on the old name with a 301, which clients do not follow).
- IIS site/pool names still carry the old hostnames (decision 4). Renaming pools changes their
  identity and every ACL that names it, so it is a separate task if wanted.
- `GOTT.IdentityServer`'s repository copy of `appsettings.Production.json` still says
  `login.gottsolutions.net`; the deployed file is authoritative and the deploy script preserves it.
- Certificate renewals change thumbprints; the E2 probe (`probes/e2-execute-v2.ps1`) pins the web,
  API and docs thumbprints and must be updated after each renewal (as before).
- Stale SNI entries for `legacy.*` and `mattermost.gottsolutions.net` remain in HTTP.sys (no site
  binds them); harmless.
- OneDrive/Google Drive OAuth redirect URIs (still to be registered by the owner) must use
  `https://app.sibyla.pt/...`.
- The old `[Platform]` certificates and the per-host old certificates stay in the `WebHosting` store
  until they expire (they are no longer bound).
