github Cloud-City-Computing/c2 v0.12.0
Cloud Codex 0.12.0

3 hours ago

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 -d

Before 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-install

That 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 proxy was 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_PROXY now names the proxies Codex believes by address. The default is 127.0.0.1/32, ::1/128, 172.29.0.1/32, and the compose network is pinned to 172.29.0.0/16. A hop count or an over-wide range is refused at boot unless TRUST_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 on 127.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, set TRUST_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=1 restores the old behaviour.
  • In production the security headers cover the whole app, not only /api, including frame-ancestors 'none'.

Also in this release

  • /healthz and /readyz, and a Docker HEALTHCHECK on /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=0 turns this off).
  • The configuration contract, cloudcodex/env-contract.js: every variable the server reads, and whether it is required. APP_URL is required in production, and DB_POOL_SIZE is 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_FROM sends from the default address.

Known gaps

  • linux/amd64 only.
  • 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_content until an explicit save.

Full detail in CHANGELOG.md.

Don't miss a new c2 release

NewReleases is sending notifications on new releases.