---
module: processes.main
title: Main Process
tab: Overview
order: 10
audience: bcos
---

How code reaches `main`. **BCOS admin only.** `main` is branch-protected — **no direct commits, ever.**

## Two — and only two — ways onto `main`

### 1. Routine release: `develop → main`
- Open a PR `develop → main`. CI must pass; **1 approval required**.
- **Johannes approves in the Gitea UI** (SOC2 gate) — Claude never approves.
- After approval, **Claude merges via API**.

### 2. Production hotfix: `hotfix/* → main`
- **Never `develop → main` for a production fix.** A prod fix goes on a `hotfix/*` branch → PR
  targeting `main` → Johannes approval → merge.
- This keeps unrelated in-flight `develop` work out of a targeted production fix.

## The SOC2 gate (non-negotiable)
1. Johannes **approves the PR in the Gitea UI** — never Claude, never programmatically.
2. Claude **merges via API** only after approval.
3. **PAUSE.** Merging is *not* deploying. After merge, **wait for an explicit "deploy" go-ahead.**
   Never chain merge → build → deploy automatically — deploy is its own approval.

## Deploy (once explicitly authorized) — production is blue/green
- Blue/green services: `auth · incident · devices · fleet · transactions · agent-proxy`.
  Everything else is single-instance.
- **Deploy to the STANDBY slot only** (check the active colour first). Verify, then switch.
- **The switch is not just the nginx line** — internal service-to-service callers go by container
  name directly (`*_SERVICE_URL=http://hiveiq-<svc>-<colour>:port` in `compose/*.yml`), bypassing
  nginx. Move **every** caller too, or the real code path stays on the old slot.
- **Keep the old slot running** until the new one is verified — it's the rollback. Stop it only
  after verification passes.

## Do NOT
- Do NOT merge or deploy before Johannes approves the PR.
- Do NOT bundle multiple actions under one "go" — one decision per approval gate.
- Do NOT deploy to the active blue/green slot.
