# Naming shift, Phase D — databases and roles (checklist, 2026-09-03)

*Requested by the owner on 2026-09-03 ("Slice 2 Phase D now"), overriding ruling N1 of
`apollo-naming-shift-prompt-260902.md`, which placed this step last, after `gott_sibyla` had been
eliminated. `gott_sibyla` has not been eliminated: `Sibyla.Legacy.Api` still serves the Hermes
plugin until intake cutover step 8. This checklist therefore moves the legacy database aside instead
of dropping it. Nothing below has run; the agent stops before any rename until the owner says go
with the two inputs of section 4.*

## 1. Inventory (read-only, 2026-09-03 08:20 UTC)

PostgreSQL 18 on this box (`postgresql-x64-18`), `password_encryption = scram-sha-256`, so a role
rename keeps its password (an MD5 hash would be cleared; none is in use).

| Database | Owner | Used by |
|---|---|---|
| `gott_apollo` | `apollo_migrator` | the platform: `Sibyla.Web` (`apollo_app`), `Sibyla.Api` (`apollo_api`), `Sibyla.Worker.Documents` (`apollo_worker`, `apollo_ai_reader` for the agent runs), the test suites, `local\*.ps1` |
| `gott_apollo_preview` | `apollo_preview_migrator` | the preview target (`apollo_preview_app`) |
| `gott_sibyla` | `gott` | Sibyla Legacy: `Sibyla.Legacy.Api` (Started), `Sibyla.Legacy.Web` (Stopped), `GottSibylaWorker` (Disabled) |
| `gott_identity` | `gott` | the IdP (`login.gottsolutions.net`); untouched |
| `postgres` | `postgres` | — |

Roles: `apollo_migrator` (BYPASSRLS, owns the 95 tables of `gott_apollo`), `apollo_app`,
`apollo_worker`, `apollo_api`, `apollo_ai_reader`, `apollo_preview_migrator`, `apollo_preview_app`;
`gott` (legacy + IdP), `postgres`, and a login-less `sibyla_piece_c_executor` of unknown origin
(left alone). Row-level-security policies name roles by OID (`worker_access` → `apollo_worker`,
`channel_binding_*` → `apollo_api`), and every grant and default privilege is keyed by OID: a
rename carries them.

Where the names are written (the only places a rename must touch):

- **Platform secrets** (values never printed, never copied): `C:\ProgramData\Sibyla\secrets\web.json`,
  `api.json`, `worker.json` — connection strings with `Database=gott_apollo` and `Username=apollo_*`.
  The hosts reach them only through `SIBYLA_SECRETS_FILE` (IIS `<location>` for the two sites, the
  service's environment), so nothing else on the host names the database.
- **Repository secrets** (gitignored): `local\secrets\apollo-db.json` (`migrator_user`, `app_user`,
  `worker_user`, `api_user`, `ai_reader_user` + passwords) and `apollo-preview-db.json`.
- **Sibyla Legacy**: IIS pools `Sibyla.Legacy.Web` and `Sibyla.Legacy.Api` carry
  `ConnectionStrings__SibylaDb` as a pool environment variable (with the password), and the
  service `GottSibylaWorker` carries the same in its SCM `Environment`. No file under
  `C:\SibylaApps` names a database. The IdP, dashboard, buzz, postiz and the MCP server name none of
  these databases.
- **Hermes**: nothing in `.env` or `config.yaml` names a database or a role; the read-only
  `apollo_ai_reader` credentials reach the agent through the worker's secrets file.
- **Repository code and scripts**: `local\tools\DatabaseTarget.ps1` (the name table),
  `local\setup-db.ps1`, `local\migrate.ps1` (via the target table), `local\run-preview.ps1`,
  `local\test-preview-principal.ps1`, `local\run-api.ps1`, `local\run-worker.ps1`,
  `db\channel-intake-api-role.sql`, `db\rls-policies.sql`, `src\Sibyla.Platform.Infrastructure\Ai\PreviewConnectionGuard.cs`,
  `src\Sibyla.Modules.Argus.Infrastructure\Persistence\SyncConnectionGuard.cs`,
  `src\Sibyla.Sync\Program.cs`, `src\Sibyla.Api\Program.cs`, `src\Sibyla.Worker.Documents\Program.cs`,
  the design-time factories, the tests that read `apollo-db.json` (`TestSecrets`, `TestDb`,
  `ApiFactory`, the Argus/Platform/Browser suites) and `PreviewConnectionGuardTests`,
  `PreviewParityTests`, `AiReaderIsolationTests`, `ChannelIntakeFailClosedTests`; `README.md`.
  Applied migrations that name `apollo_api` / `apollo_ai_reader` in guarded `DO` blocks stay as they
  are (history is never rewritten; on a fresh database after Phase D those blocks find no role,
  say so, and `setup-db.ps1` applies the renamed SQL afterwards, as designed).

Kept on purpose (not Phase D, or ruled separately): `apollo_ai_reader` (N1: it is the role the
agent Apollo reads with), `gott` and `gott_identity`, the IdP clients `apollo-web`/`apollo-worker`
and the audience/scope names (N4), `D:\ApolloData\*` (moving data is a separate owner call),
the data-protection application name "Apollo" and purpose `Apollo.CredentialStore.v1` (changing
them makes every stored credential unreadable), the code-ledger issuer `"apollo"`, the extraction
contract `apollo.extract.v0`, the Nextcloud prefix `Apollo/`, the repo folder and GitLab project (N3).

## 2. Target names

| Today | After Phase D |
|---|---|
| `gott_sibyla` (legacy) | `gott_sibyla_legacy` (kept until intake cutover step 8 drops it) |
| `gott_apollo` | `gott_sibyla` |
| `gott_apollo_preview` | `gott_sibyla_preview` |
| `apollo_migrator`, `apollo_app`, `apollo_worker`, `apollo_api` | `sibyla_migrator`, `sibyla_app`, `sibyla_worker`, `sibyla_api` |
| `apollo_preview_migrator`, `apollo_preview_app` | `sibyla_preview_migrator`, `sibyla_preview_app` |
| `apollo_ai_reader` | unchanged |
| `local\secrets\apollo-db.json`, `apollo-preview-db.json` | `sibyla-db.json`, `sibyla-preview-db.json` (same content, user names substituted, passwords untouched) |

## 3. Order (from the prompt document, adjusted for the live legacy database)

Every write is a blind substitution done by `local\phase-d\Invoke-PhaseD.ps1`: the script reads
each file, replaces `Database=gott_apollo` / `Username=apollo_*` (never `apollo_ai_reader`) and,
for the legacy carriers, `Database=gott_sibyla` → `gott_sibyla_legacy`, writes the file back with
its ACL intact and leaves a `.pre-phase-d.bak` beside it. No value is printed or logged. The script
runs in `-WhatIf` mode until the owner's go; `-Execute` needs the PostgreSQL superuser password in
`PGPASSWORD` for the duration of the run, exactly as `local\setup-db.ps1` does.

1. **Window.** Announce a few minutes of downtime: the platform (`sibyla.*`) and the legacy API
   (`api.legacy.gottsolutions.net`) are down while the databases have no connections. Not inside the
   win-acme window (09:00–15:00 UTC) and not while a hand-path deploy runs.
2. **Stop the connections.** Stop IIS pools `Sibyla.Web`, `Sibyla.Api`, `Sibyla.Legacy.Api`
   (`Sibyla.Legacy.Web` is Stopped, `GottSibylaWorker` is Disabled); stop service
   `Sibyla.Worker.Documents`; pause the Hermes cron `apollo-email-gateway` (triage only, but it
   should not run mid-window). Confirm `pg_stat_activity` shows no session on the three databases;
   terminate stragglers with `pg_terminate_backend`.
3. **Rename in PostgreSQL** (superuser), in one psql session, each statement checked:
   `ALTER DATABASE gott_sibyla RENAME TO gott_sibyla_legacy;`
   `ALTER DATABASE gott_apollo RENAME TO gott_sibyla;`
   `ALTER DATABASE gott_apollo_preview RENAME TO gott_sibyla_preview;`
   `ALTER ROLE apollo_migrator RENAME TO sibyla_migrator;` and likewise `app`, `worker`, `api`,
   `preview_migrator`, `preview_app`. Then verify: `pg_policies` show the new role names on
   `docint`/`jobque`/`jobsch`/`chnbnd`, `has_database_privilege('sibyla_app','gott_sibyla','CONNECT')`
   is true and `has_database_privilege('sibyla_preview_app','gott_sibyla','CONNECT')` is false (the
   preview isolation), the table owner of `gott_sibyla` is `sibyla_migrator`.
4. **Substitute the carriers.** The three platform secrets files; the two repository secrets files
   (and rename them); the two legacy pools' `ConnectionStrings__SibylaDb`; the legacy worker's
   SCM `Environment`.
5. **Repository code** on branch `ops/naming-phase-d`: the files of section 1, the name table in
   `DatabaseTarget.ps1`, the SQL files, the connection guards and their tests, the secrets file names
   in the test helpers and run scripts; a dated record in the naming-shift document (N1 overridden
   on the owner's word), the glossary, the plan document, memory.
6. **Verify locally.** `local\test.ps1` (the connection guards, preview parity, RLS meta-tests and
   the API suite all read the renamed names), `local\migrate.ps1` (nothing pending on both targets).
7. **Start and verify production.** Start the service and the pools; `https://sibyla.gottsolutions.net/`
   302, `https://api.sibyla.gottsolutions.net/health/ready` 200 and `/health/schema` answering
   `"role": "sibyla_api"`, docs 200, worker Running with `sibyla_worker` sessions in
   `pg_stat_activity`; `https://api.legacy.gottsolutions.net/health/live` 200 on `gott_sibyla_legacy`;
   the IdP sign-in still works (untouched database); unpause the cron.
8. **Later, by the owner:** `Sibyla.Deployment` Slice 2's `Precondition.Ownership`/secrets rules are
   unaffected (they carry paths, not database names); intake cutover step 8 drops
   `gott_sibyla_legacy` with the legacy API.

## 4. What the owner provides before the go

1. The PostgreSQL superuser password, set as `PGPASSWORD` in the session that runs step 3 (the
   agent never reads or stores it; `setup-db.ps1` already works this way).
2. The window (step 1) and the explicit go. The dry run (`-WhatIf`) can be reviewed first: it lists
   every file, pool, service and statement it would touch, by name only.

## 5. Rollback

Reverse the renames in the same order (`ALTER DATABASE ... RENAME TO` back, `ALTER ROLE ... RENAME TO`
back), restore every `.pre-phase-d.bak` over its file, restore the two pools' variable and the
service's environment from the same backups, start the pools and the service. The preview database
and the legacy database follow the same reversal. Nothing in Phase D changes data.
