The hosting-readiness release. Upgrading from 0.11.0 is a breaking change for an install reached directly from another machine, or behind a proxy that does not connect from 127.0.0.1, ::1 or 172.29.0.1. The upgrade takes the project down before migrating, applies two migrations and runs one backfill:
docker compose -f docker-compose-release.yml pull app
docker compose -f docker-compose-release.yml down # never down -v
docker compose -f docker-compose-release.yml run --rm app npm run migrate
docker compose -f docker-compose-release.yml run --rm app npm run backfill:doc-images
docker compose -f docker-compose-release.yml up -dBefore pulling, check that .env sets APP_URL (now required in production), and back up the database (MySQL is pinned to 8.4.11 and may upgrade its data directory on first start). See Migration in CHANGELOG.md.
Run it
git clone https://github.com/Cloud-City-Computing/c2.git
cd c2
cp .env.example .env # fill in DB and admin credentials and APP_URL; SMTP is optional
docker compose -f docker-compose-release.yml up -d
docker compose -f docker-compose-release.yml run --rm app npm run migrate -- --adopt-fresh-installThat pulls ghcr.io/cloud-city-computing/cloud-codex:0.12.0 and serves it on http://localhost:3000. The last line is a one-time step on a fresh install; until it runs, /readyz reports migrations.
Security
- The rate limiters can no longer be walked around with a forged X-Forwarded-For (GHSA-9fmx-frrf-xxmq, medium).
trust proxywas 1 and the app port was published on every interface, so a client reaching the port directly could claim a new address on every request and get a fresh sign-in, two-factor, reset, account-change and search budget each time.TRUST_PROXYnow names the proxies Codex believes by address. The default is127.0.0.1/32, ::1/128, 172.29.0.1/32, and the compose network is pinned to172.29.0.0/16. A hop count or an over-wide range is refused at boot unlessTRUST_PROXY_ALLOW_HOP_COUNT=true.- The app port is published on
127.0.0.1(APP_BIND, IPv4 only), and the from-source compose file publishes MySQL only on127.0.0.1(DB_BIND). - If you run behind a proxy at another address, list it in
TRUST_PROXY.docs/deployment.md, "Rate limiters", has worked values for a proxy on the host, a proxy container and a cloud load balancer, and a three-step check. With nothing in front, setTRUST_PROXY=false.
- Session tokens are stored only as a SHA-256 digest, and every sign-in is its own session. A copy of the sessions table is no longer a list of working sign-ins, and signing out on one device no longer signs out the others. The migration hashes existing sessions in place, so nobody is signed out.
- Document images are served only to people who can read the document. They used to be a public static mount. Run the one-time backfill above, or existing images are hidden from their readers.
DOC_IMAGES_PUBLIC=1restores the old behaviour. - In production the security headers cover the whole app, not only
/api, includingframe-ancestors 'none'.
Also in this release
/healthzand/readyz, and a DockerHEALTHCHECKon/readyz.- The container stops cleanly. On SIGTERM it saves every open document's live edits, closes the WebSockets and exits within ten seconds.
- One process per database. A second process pointed at the same database refuses to start (
C2_INSTANCE_LOCK=0turns this off). - The configuration contract,
cloudcodex/env-contract.js: every variable the server reads, and whether it is required.APP_URLis required in production, andDB_POOL_SIZEis new. GET /api/documents/state, a reconciliation read for a paired Cloud Command, reached with the existing service token.- The activity log records archive deletes and document renames and moves, and the archive tree refuses a move that would hide documents.
- Fixed:
- a fresh install on an SELinux-enforcing host now gets its schema (the 0.11.0 known gap);
- the migration lock fits any schema name;
- a blank
SMTP_FROMsends from the default address.
Known gaps
linux/amd64only.- On first boot the app can exit and restart a few times while MySQL finishes initialising, then recovers by itself in about ten seconds.
- Documents edited only over the collaborative WebSocket have a stale
html_contentuntil an explicit save.
Full detail in CHANGELOG.md.