# Publishing the public manual This is an internal operator runbook. Publishing documentation must not recreate the ThothII application, Gitea, proxy, DWH or other documentation containers. ## Three separate states 1. `main` contains reviewed source and the public allowlist in `mkdocs.yml`. 2. Gitea Actions builds and validates the generated `pages` branch. A successful Actions run alone does **not** update the live site. 3. The live [manual](https://git.tylconsulting.it/thothii-docs/) is served by the dedicated `thothii-docs` nginx container on the host reached through the existing trusted SSH alias `contabo`. Its Compose project/service are `thothii-docs`/`docs`. This procedure is manual. No timer, webhook, credential or automatic deployment has been added. Use the operator's existing SSH authorization and known host key; never disable host verification to make a publication work. ## Build and stage From a clean ThothII `main` checkout already pushed to Gitea: ```sh git status --short git rev-parse HEAD ./scripts/build-docs.sh bash scripts/auth-docs-smoke.sh bash scripts/verify-dwh-auth-docs.sh bash scripts/test-auth-docs-smoke.sh bash scripts/test-verify-dwh-auth-docs.sh ``` The strict build also verifies the public boundary: exactly 20 navigation pages, five approved source assets, no internal pages/assets, and matching search entries. Theme-generated assets are separate from those five source assets. Choose a unique release identifier consisting of UTC timestamp and source SHA. Record the current symlink and container identity before proceeding: ```sh ssh -oBatchMode=yes -oStrictHostKeyChecking=yes contabo \ 'readlink /srv/thothii-docs/current; docker inspect --format "{{.Id}} {{.State.Health.Status}}" thothii-docs' ``` On the server, create `/srv/thothii-docs/releases/RELEASE/site` only if RELEASE does not exist. Copy the current `nginx.conf` unchanged into the new release. Transfer the local built `site/` into that new empty directory with rsync over the same verified SSH connection. Do not use `--delete` against `current`, and do not edit routing, TLS, network or authentication configuration. ## Activate only this site Replace RELEASE below with the validated identifier, not a user-supplied path. Keep the previously recorded release for rollback. ```sh cd /srv/thothii-docs test -f releases/RELEASE/site/index.html test -f releases/RELEASE/nginx.conf docker exec thothii-docs nginx -t ln -s releases/RELEASE current.next mv -Tf current.next current docker compose --project-name thothii-docs -f docker-compose.yml \ up -d --no-deps --force-recreate docs ``` Check that `current.next` does not already exist before creating it; stop if it does, because another publication or recovery may be in progress. Changing the symlink alone is insufficient: an existing Docker bind mount still resolves to the old release. The service recreation above remounts the new directory. It may briefly interrupt this manual only. ## Verify, record, or roll back - Wait for `docker inspect` to report this container healthy; verify mounted paths and nginx configuration. Stop after a bounded timeout (for example 60 s). - Without login/cookies, require HTTP 200 for home, search and both `install/standalone-manual-it/` and `install/standalone-manual-en/`. - Compare live home and `search/search_index.json` checksums to the build. Search must contain only the 20 approved pages, never plans, reports or architecture. - Require HTTP 404 for retired/internal paths, including `architecture/overview/`, `plans/2026-09-08-memory-management/`, and `operations/compose-reference/`. - Record source SHA, release ID, previous release and checks in the cleanup/release record. Do not call the publication complete solely because `pages` was pushed. If health or public verification fails, point `current` back to the recorded previous release using a fresh temporary symlink and the same atomic rename, then recreate **only** service `docs` with the same Compose command. Verify health and old site availability. Do not delete either release during recovery. Historical releases are retained; pruning them requires a separate retention decision.