[Overview](/docs)[Installation](/docs/installation)[Architecture](/docs/architecture)[Integrations](/docs/integrations)[First review](/docs/review)[Operations](/docs/operations)[Acceptance](/docs/acceptance)

# Install in your infrastructure.

A staged engineering guide. Keep your existing GitLab in place.

## Obtain an agreed evaluation package

Request the package and usage terms: source archive, wheel, architecture-specific images, hash manifest, dependencies and acceptance checklist. Verify provenance and matching versions. Self-hosted describes deployment location; it does not automatically mean an open-source license. Pricing and license terms are agreed separately.

[Request evaluation package](mailto:vasiliy.petrov@uwukl.com?subject=Null%20Review%3A%20self-hosted%20evaluation%20package)

## Prepare the prerequisites

- Linux x86_64 / amd64 for the target corporate pilot; Python 3.12; Docker Compose 2.30.0 or newer. The profile also permits arm64 as a separately tested target.

- A trusted operator environment running as UID/GID 10001:10001 with the local Docker socket. Only the operator gets the socket, never a Worker or MR code.

- External PostgreSQL 18.6 with verify-full TLS. Separate service databases and roles, plus a separate ordinary ContextForge owner.

- Already loaded and accepted Null and ContextForge images pinned by immutable IDs. The installer does not pull or build images.

- Corporate OIDC/Keycloak, backoffice and ContextForge certificates, DNS and network permissions. GitLab and runner are prepared separately.

- Operator-owned files 0600 and directories 0700. No symlinks, secrets in Git or automatic reuse of another account login. Direct macOS installation is refused; Mac emulation is not a native Linux capacity result.

## How to read a stage result

An engineer runs the commands below from the agreed package, one stage at a time. These are templates, not an installation receipt. A successful stage does not establish model, ContextForge or pilot readiness.

| Stage | What to inspect before continuing  |
| --- | --- |

| prepare | Private profile and journal in a new target; values match the package.  |

| preflight | Validated prerequisites and bindings before service startup.  |

| allocate | New tenants and databases; then separate official sign-in.  |

| configure / validate | Service projections and an offline check of current images.  |

| start | Services started; useful operation, versions and access boundaries need separate verification.  |

## Prepare the private profile

Run commands from the extracted source/sdist matching the installed wheel. The operator sets PILOT_PROFILE and PILOT_TARGET to absolute private paths; the target must be new. The profile binds the installation, platform, source commit, local image IDs, runtime manifest, PostgreSQL, one or two tenants, HTTPS backoffice, worker profiles, OIDC and ContextForge. Real values accompany the package. Do not invent hashes or tokens.

## The first three stages

These are templates of implemented commands, not evidence of a completed installation. Run one stage at a time and inspect its result.

### prepare

```sh
set -eu
umask 077
: "${PILOT_PROFILE:?Set the private absolute profile path}"
: "${PILOT_TARGET:?Set the new absolute target path}"
python3.12 -B scripts/install_pilot.py --help
python3.12 -B scripts/install_pilot.py prepare \
  --profile "$PILOT_PROFILE" --target "$PILOT_TARGET"
```

### preflight

```sh
set -eu
: "${PILOT_TARGET:?Set the allocated absolute target path}"
python3.12 -B scripts/install_pilot.py apply \
  --target "$PILOT_TARGET" --stage preflight --consent
```

### allocate

```sh
set -eu
: "${PILOT_TARGET:?Set the allocated absolute target path}"
python3.12 -B scripts/install_pilot.py apply \
  --target "$PILOT_TARGET" --stage allocate --consent
```

prepare saves a private profile and journal. preflight verifies prerequisites and bindings. allocate creates new tenants and databases.

## Pause after allocate for official sign-in

Read the newly allocated tenant IDs from private allocation metadata. Initialize each profile’s external Codex volume separately and complete official sign-in using the operator helper in the same package. The expected name is null-review-codex-<allocated-id>-<profile_id>. The installer does not create, discover or repair these logins. Login status alone does not prove a usable model call.

```sh
set -eu
: "${NULL_TENANT_ID:?Use the newly allocated tenant ID}"
: "${NULL_CODEX_PROFILE:?Use the profile ID from the agreed profile}"
: "${NULL_ENGINE_IMAGE:?Use the accepted immutable engine image ID}"
scripts/codex-tenant --tenant "$NULL_TENANT_ID" \
  --profile "$NULL_CODEX_PROFILE" --image "$NULL_ENGINE_IMAGE" init
scripts/codex-tenant --tenant "$NULL_TENANT_ID" \
  --profile "$NULL_CODEX_PROFILE" --image "$NULL_ENGINE_IMAGE" login-browser
```

After official sign-in, inspect its status. Use only the agreed immutable image ID, never an old default tag. This does not verify a useful model call.

```sh
set -eu
: "${NULL_TENANT_ID:?Use the newly allocated tenant ID}"
: "${NULL_CODEX_PROFILE:?Use the profile ID from the agreed profile}"
: "${NULL_ENGINE_IMAGE:?Use the accepted immutable engine image ID}"
scripts/codex-tenant --tenant "$NULL_TENANT_ID" \
  --profile "$NULL_CODEX_PROFILE" --image "$NULL_ENGINE_IMAGE" status
```

## Configure, validate, start

### configure

```sh
set -eu
: "${PILOT_TARGET:?Set the allocated absolute target path}"
python3.12 -B scripts/install_pilot.py apply \
  --target "$PILOT_TARGET" --stage configure --consent
```

### validate

```sh
set -eu
: "${PILOT_TARGET:?Set the allocated absolute target path}"
python3.12 -B scripts/install_pilot.py apply \
  --target "$PILOT_TARGET" --stage validate --consent
```

### start

```sh
set -eu
: "${PILOT_TARGET:?Set the allocated absolute target path}"
python3.12 -B scripts/install_pilot.py apply \
  --target "$PILOT_TARGET" --stage start --consent
```

configure creates isolated service projections. validate runs the offline deployment check with network disabled. start performs detached Compose up without build or pull. This does not verify a useful review, successful ContextForge migrations or pilot readiness. Installer results retain runtime_verified=false and pilot_ready=false.

## If a stage is interrupted

Do not replay a stage marked started/unknown or delete its journal, databases or target. Preserve them for reconciliation of actual effects. The installer is create-only: there is no automatic resume, upgrade, rollback or adoption of an existing installation. Replaying start is also refused.

[Open operations playbooks](/docs/operations)

## Deployment options

| Option | Current delivery boundary  |
| --- | --- |

| One Linux Docker host + external PG | Current staged bootstrap. Separate acceptance after startup.  |

| Corporate VMs, reverse proxy and existing GitLab/SSO | Agreed topology and network plan, scoped implementation work.  |

| Kubernetes / Helm | No ready-made chart is supplied. It does not replace service-contract acceptance.  |

| Air-gapped environment | Requires a complete offline bundle and an internal model. The current Codex CLI profile is not an air-gapped solution.  |
