---
title: Environments and versioning
description: The two-contour environment model (dev and prod), the promotion flow, and the two independent versioning axes — platform modules and pipeline versions.
locale: en
version: 0.2.0
canonical: https://enterium-developer-docs.pages.dev/developers/platform/environments/
---

> Documentation index: https://enterium-developer-docs.pages.dev/developers/llms.txt — every page of this portal has a Markdown twin at <route>.md; the frozen API contract is at https://enterium-developer-docs.pages.dev/developers/api/openapi.json.

## Two contours

| Contour | Branch | Runs where | Purpose |
| --- | --- | --- | --- |
| **prod** | `main` | The production host, on the nightly schedule | Stable; every change arrives by deliberate promotion |
| **dev** | `dev` | A local development stack (Docker: Postgres, Redis, API, web) | Integration; all day-to-day work lands here first |

The flow is one-directional: feature branches merge into `dev` via small
pull requests; promotion to `main` is a separate, explicitly-approved step;
deployment to the production host is a separate script run after that. A
hotfix that must land on `main` directly is immediately forward-ported to
`dev` in the same session — `main` is always a subset of `dev`.

## Where it runs

Production is a single self-hosted Linux box behind a Caddy reverse proxy,
reachable through a dynamic-DNS name on a non-standard port
(`entherium.duckdns.org:8443`). That is the base URL in every example on
this portal, and it is the real one — there is no managed API gateway and
no vanity domain in front of it yet. The fleet of destination sites lives
on separate hosts. For a platform at this stage the box *is* production:
nightly runs, the API, the MCP server and the task board all answer from
it, and its uptime is watched by an external dead-man's switch.

This documentation is served from two places with the same build: from the
API host under `/developers/`, and from a Cloudflare Pages mirror. The
mirror is the **canonical** copy — every page's `canonical` and `og:url`
point there — and it stays up if the box does not. Both copies are rebuilt
from the same commit by the same deploy scripts; when they differ, the
mirror is the newer one.

## Versioning — two independent axes

**Axis A: platform modules.** Each module (`platform`, `webui`, `contracts`,
services, this documentation site) is versioned independently by SemVer,
derived automatically from Conventional Commits by release tooling — versions
are never bumped by hand. An umbrella platform version tracks the whole.

**Axis B: pipeline versions.** Collect, Generate and Localize are versioned
separately in the pipeline catalog, with immutable per-version code
directories and per-contract pins. This axis moves at its own pace —
a pipeline can advance several versions within one platform release, and
vice versa. See [Version lifecycle](../../pipelines/versioning/).

The two axes are deliberately decoupled: a platform deploy does not change
which pipeline version the fleet runs, and promoting a pipeline version does
not require a platform release.

## What a "release" means here

- **Merged to `dev`** — integrated, gate-checked (lint, types, tests run
  locally as the merge gate), not yet user-visible.
- **Promoted to `main` + deployed** — running on the production host.
- **Enabled** — the platform's hard rule: a feature is only called deployed
  when it is switched on and its effect is observable (a metric, a page, a
  number). Code behind a disabled flag is explicitly *not* "deployed" in
  reports or documentation.
