# Staging Environment (ORCH-31)

Orchestrator supports a permanent **staging instance** running on port **8501** with a
fully-isolated SQLite database. The staging instance shares the same codebase and
Dockerfile as production but is started under the `staging` Docker Compose profile so it
**never starts accidentally** during a normal `docker compose up -d`.

## Architecture

| | Production | Staging |
|---|---|---|
| Port | 8500 | 8501 |
| Container name | `orchestrator` | `orchestrator-staging` |
| DB (host path) | `./data/orchestrator.db` | `./data/staging/orchestrator.db` |
| DB (container path) | `/app/data/orchestrator.db` | `/app/data/orchestrator.db` |
| env file | `.env` | `.env.staging` |
| Compose profile | *(default)* | `staging` |

DB isolation is achieved via a separate volume mount (`./data/staging:/app/data`), not by
changing `ORCH_DB_PATH` — the container path stays identical while the host path is a
different directory.

## Prerequisites

1. **`.env.staging`** — create from the template (see below). This file is **not committed**
   to the repo (it contains secrets). Copy and fill in values before first start.
2. **`./data/staging/`** directory — created automatically on first container start.

### Create `.env.staging`

```bash
cd /home/slin/repos/orchestrator
cp .env.staging.example .env.staging
# Edit .env.staging — fill in real tokens / secrets.
# At Stage 1 (ORCH-31) you can reuse prod values; sandbox Plane project
# and isolated Gitea webhook will be wired in ORCH-32.
nano .env.staging
```

## Starting Staging

```bash
cd /home/slin/repos/orchestrator
docker compose --profile staging up -d orchestrator-staging
```

Check it is running:

```bash
docker ps | grep orchestrator-staging
curl -s http://localhost:8501/health | python3 -m json.tool
```

## Stopping Staging

```bash
docker compose --profile staging stop orchestrator-staging
# or remove the container entirely:
docker compose --profile staging down orchestrator-staging
```

## Normal `up -d` does NOT start staging

```bash
# This starts ONLY the prod orchestrator (port 8500). Staging is NOT affected.
docker compose up -d
```

The `profiles: [staging]` directive in `docker-compose.yml` ensures staging is
completely invisible to commands that do not pass `--profile staging`.

## Logs

```bash
docker logs -f orchestrator-staging
```

## Staging-образ как источник прод-артефакта (ORCH-058)

Прод-деплой орка — **build-once**: хук ретегает провалидированный staging-образ
(`orchestrator-orchestrator-staging`) на прод-тег **без rebuild** (ORCH-036). Чтобы
в прод не попал устаревший образ (инцидент LESSONS_ORCH-036 п.4), ORCH-058 гарантирует
свежесть staging-образа **двумя слоями** (только self-hosting):

- **A — пересборка staging (liveness):** на ребре `deploy-staging → deploy` (ПОСЛЕ
  merge-gate, ДО Phase A) QG-под-чек `check_staging_image_fresh` через хук
  `--build-staging` пересобирает staging-образ из worktree валидированного коммита
  (`--build-arg GIT_SHA=<sha>`, OCI-лейбл `org.opencontainers.image.revision`) и
  пересоздаёт 8501. Так валидируем РОВНО тот артефакт, что промоутится в прод.
  FAIL → откат на `development`. Сборки/recreate — **только staging (8501)**.
- **B — fail-closed guard (safety):** прод-хук перед `docker tag` сверяет лейбл
  `revision` у `SOURCE_IMAGE` с `EXPECTED_REVISION` (пробрасывает оркестратор);
  несовпадение / пустой лейбл / ошибка inspect → `exit 1`, прод не трогается.

Kill-switch `ORCH_IMAGE_FRESHNESS_ENABLED` включает A+B **как целое**; область —
`ORCH_IMAGE_FRESHNESS_REPOS` (пусто → только `orchestrator`). Детали — `DEPLOY_HOOK.md`,
`docs/work-items/ORCH-058/06-adr/ADR-001-staging-image-provenance.md`.

## Roadmap

| Task | Description |
|---|---|
| **ORCH-31** *(this PR)* | Infra: compose service, .env template, gitignore, docs |
| **ORCH-32** | Sandbox: isolated Plane project + Gitea repo for staging |
| **ORCH-33** | Test suite running against staging endpoint |
| **ORCH-34** | Deploy hook: promote `orchestrator:candidate` image to staging |
