oCMS 0.29 and 0.30: UTC, Two-Factor Authentication, and Safer Operations

An opossum beside a glowing shield, clock, and connected servers on a dark background

Two releases, one practical goal: make oCMS easier to trust in production. Version 0.29.0, released on October 18, brings consistent UTC timestamps, account dashboards, and substantial work on integrations and the server lifecycle. Version 0.30.0, released on October 24, adds authenticator-based two-factor authentication and tighter protection for sensitive administration, alongside deployment and monitoring fixes.

These changes matter both to people using the admin panel and to developers operating a site. There are also upgrade requirements to prepare for. Here is what changed, what it means in practice, and what to check before moving an existing installation to 0.30.0.

0.29.0: consistent time across the system

Dates previously depended too much on how they were written and which time zone the server used. Some stored values used Go's debug representation, including a named zone and monotonic-clock suffix. That is awkward input for SQLite date functions and downstream integrations.

The 0.29.0 migration converts parseable, time-zone-bearing stored timestamps to UTC across core and module tables. Existing zone-free values are left alone, including SQLite's already-UTC timestamps and Internal Analytics' local wall-clock values. Values that look zoned but cannot be parsed are retained and logged.

The outward-facing behavior changes too: admin and public views display UTC, and REST responses, webhooks, and content exports return UTC timestamps. For example, 2026-10-18T05:41:31+02:00 becomes 2026-10-18T03:41:31Z. The instant is identical; the string is different. Consumers that compare raw timestamp strings or assume the old offset need checking.

Back up the database before this upgrade. The data rewrite is irreversible: ocms migrate down removes the migration record but does not reconstruct the original timestamp strings. The Docker image also defaults to UTC unless TZ is configured.

Better account navigation and migration tooling

Editors now arrive at /admin after login, and public users arrive at a new read-only /account dashboard. It shows their account details and provides interface-language selection and logout. Already signed-in visitors opening /login are sent to the appropriate dashboard too.

Database migrations can run independently of the web server:

ocms migrate status
ocms migrate up

The command reads the same environment configuration as the server. The Makefile and site-instance migration targets now use it instead of a standalone goose invocation, ensuring Go migrations such as the UTC rewrite are included in the correct sequence.

Integrations and shutdowns get more predictable

REST-driven page creation, updates, deletion, and media changes now emit the corresponding webhook events. Scheduled publishing emits page.published, and shutdown flushes events still waiting in the dispatcher's debounce window. A simultaneous manual and scheduled publication also produces the publication event once.

Server startup and shutdown received considerable attention. A failed listen now exits instead of leaving a process alive without a working listener. Startup failures reach the configured logs, and event logging runs in the background rather than holding a request while waiting for SQLite. Shutdown drains work within defined budgets; the supplied deployment configurations allow 60 seconds for the process to stop.

For everyday editing, checkbox fields now retain all selected options and restore the correct selections after validation errors. Category ordering saves correctly. Media deletion reports leftover files honestly when disk cleanup fails after the database records have already been removed.

Security fixes around the edges

Version 0.29.0 also fixes panic recovery inside the request-timeout goroutine, so a handler panic no longer terminates the whole server. Content Security Policy sources follow active modules, and unreadable privacy or captcha settings use protective fallbacks while recovery is retried.

Reserved route names receive stricter slug and alias validation. Static directory URLs stop exposing file listings, and the Docker Compose defaults bind the published port to loopback and reduce container privileges. These are practical changes to the existing deployment model, with configuration details documented in the release notes.

0.30.0: two-factor authentication built in

The headline feature is TOTP authentication using an authenticator app, with ten single-use recovery codes. It covers administrator, editor, and public accounts. After the password check, sign-in remains incomplete until the second factor succeeds. Accepted authenticator codes cannot be replayed, and wrong codes count toward account lockout.

Users manage enrollment, replacement, and recovery codes at /account/security. Administrators can reset another user's second factor, while ocms mfa-reset <email> provides a command-line recovery route for an administrator who cannot ask another administrator for help. Changing or resetting the second factor invalidates the account's other sessions.

Production requires MFA by default. Existing installations must configure OCMS_MFA_KEY, at least 32 bytes and distinct from OCMS_SESSION_SECRET. Generate a key with:

openssl rand -base64 32

Store the generated value as OCMS_MFA_KEY in the site's environment configuration and back it up with the database. TOTP secrets are encrypted with a key derived from it. ocms init and the site-setup script now generate this setting for new installations.

When MFA is required, accounts without an authenticator enroll at their next login, and sessions established without a second factor are signed out. An installation can explicitly set OCMS_REQUIRE_MFA=false to opt out of the production requirement. Demo mode keeps MFA off. API keys continue to use their own expiry and source-network policies; MFA applies to account sign-in.

Sensitive administration asks for the password again

Changing a user's password or email, changing administrator privileges, and resetting another user's second factor now require the acting administrator's current password. Content imports also preserve existing administrator-role assignments when overwriting users.

Activating DB Manager requires password confirmation as well. This matters because its raw SQL access can reach sensitive database contents. Once enabled, queries do not ask for the password again, so deactivate the module after use.

Deployment success now includes a working site

The deployment scripts wait for restarted instances to become healthy and fail when an instance does not come up. Binary deployment continues with the remaining instances after an individual failure, instead of abandoning sites it has already stopped.

Production-to-development synchronization restarts a production instance on exit if the sync stopped it, including after failure or interruption, and checks its health. Monitoring now detects crash-looping services, limits automatic restart attempts per outage, and reports recovery.

Source builds also move to Go 1.27.3, including its certificate-validation security fix, with refreshed database and frontend dependencies.

Before upgrading

  1. Back up the database and environment configuration before applying the UTC migration. Preserve the MFA encryption key in your backup process.
  2. Configure OCMS_MFA_KEY before starting 0.30.0 in production, and tell users to prepare an authenticator app and save their recovery codes.
  3. Check timestamp consumers, webhook integrations, and the environment file. An unreadable or malformed configured environment file now stops startup.
  4. Review API-key policies. 0.29.1 corrected 0.29.0's startup-blocking behavior: out-of-policy active keys are logged and rejected on requests, but no longer stop the production server from starting. That correction is included in 0.30.0.
  5. Verify health after deployment, then exercise sign-in, account enrollment, editing, and the integrations your site uses.

Read the complete 0.29.0 release notes, 0.29.1 correction, and 0.30.0 release notes. The login and MFA documentation covers configuration and recovery in detail.

Written by OIV