Files
ModelForge/docs/INSTALLATION.md
T

172 lines
7.5 KiB
Markdown

# Installing ITWorx ModelForge v1.2.1
This is the whole installation. If you have to do something that is not written here, that is a
defect — please report it rather than working around it.
The steps below are release-gated against a fresh project with isolated volumes, newly generated
test secrets and an empty PostgreSQL database.
## 1. What you need
| Requirement | Minimum | Notes |
| --- | --- | --- |
| Docker Engine | 24 | `docker version` |
| Docker Compose | 2.x | `docker compose version` |
| PostgreSQL | 17 (16 accepted) | Provided by the deployment; you do not install it separately |
| Redis | 7 | Provided by the deployment |
| Disk | 50 GiB free, more for model artifacts | Model weights dominate; plan for the models you intend to run |
| Python | 3.12 | Only to run the preflight, bootstrap and upgrade tools from the host |
A GPU is **not** required on the control-plane host. It is required on any compute node that will
serve a capability, along with the NVIDIA driver and the NVIDIA container runtime. See
[NODE_AGENT.md](NODE_AGENT.md).
Networking: the control plane never dials a compute node. Nodes report outbound to the control
plane, so a node needs to reach the API but the API needs no route back.
PostgreSQL 16+ is the production database contract. SQLite is an isolated unit-test backend, not a
supported installation or migration target.
## 2. Check the host first
```bash
python scripts/preflight.py
```
It reports the Docker and Compose versions, the NVIDIA driver if present, whether the ports the
deployment publishes are free, and free disk. Exit status is 0 when the host can run ModelForge.
Nothing is changed by this command.
## 3. Generate your secrets
ModelForge never mints its own credentials. A platform that generates its own admin secret has no
way to tell you it did.
```bash
python -c "import secrets; print(secrets.token_urlsafe(48))" # operator API key
python -c "import base64, os; print(base64.b64encode(os.urandom(32)).decode())" # backup key
python -c "import secrets; print(secrets.token_urlsafe(32))" # PostgreSQL bootstrap admin
python -c "import secrets; print(secrets.token_urlsafe(32))" # migration/schema owner
python -c "import secrets; print(secrets.token_urlsafe(32))" # API runtime role
```
**Store the backup encryption key outside this deployment.** Losing it makes every existing backup
unrecoverable. It is the one value that must survive the machine it protects.
## 4. Configure
```bash
cp .env.example .env
```
Fill in at least these. Production refuses to start without them:
```ini
MODELFORGE_POSTGRES_DB=modelforge
MODELFORGE_POSTGRES_ADMIN_USER=postgres
MODELFORGE_POSTGRES_ADMIN_PASSWORD=<generated-admin-secret>
MODELFORGE_MIGRATION_DB_PASSWORD=<generated-owner-secret>
MODELFORGE_RUNTIME_DB_PASSWORD=<generated-runtime-secret>
MODELFORGE_MIGRATION_DATABASE_URL=postgresql+psycopg://modelforge:<URL-encoded-owner-secret>@postgres:5432/modelforge
MODELFORGE_RUNTIME_DATABASE_URL=postgresql+psycopg://modelforge_runtime:<URL-encoded-runtime-secret>@postgres:5432/modelforge
MODELFORGE_OPERATOR_API_KEY=<generated, at least 32 characters>
MODELFORGE_BACKUP_ENCRYPTION_KEY=<generated base64 AES-256 key>
MODELFORGE_CORS_ORIGINS=https://modelforge.example.internal
VITE_API_BASE_URL=https://modelforge.example.internal:8000
MODELFORGE_VERSION=1.2.1
MODELFORGE_API_IMAGE=modelforge-api:1.2.1
MODELFORGE_WEB_IMAGE=modelforge-web:1.2.1
```
Every setting is documented in [CONFIGURATION.md](CONFIGURATION.md), which is generated from the
typed settings — it cannot drift from what the code actually reads.
To move a port, set `MODELFORGE_API_PUBLISHED_PORT`, `MODELFORGE_WEB_PORT`,
`MODELFORGE_POSTGRES_PORT` or `MODELFORGE_REDIS_PORT`. You never have to edit a Compose file.
Then re-run the preflight with the production rules applied:
```bash
python scripts/preflight.py --production
```
## 5. Deploy
With the verified release images already loaded or pulled, deploy them without rebuilding:
```bash
docker compose -f docker-compose.yml -f docker-compose.production.yml up -d --no-build
```
`docker-compose.yml` on its own is a **development** deployment, but it still requires distinct
database secrets; none is embedded in the repository. It leaves the environment at `development`,
which switches off production-only startup rules. The production overlay sets
`MODELFORGE_ENV=production` and takes each secret through
`${VAR:?message}`, so Compose refuses to render the project at all if one is missing. If you see a
message like
```text
required variable MODELFORGE_OPERATOR_API_KEY is missing a value
```
that is the deployment refusing before it starts a single container.
For an explicitly local source build, keep `MODELFORGE_VERSION`, `MODELFORGE_COMMIT` and
`MODELFORGE_BUILT_AT` pinned and use `up -d --build`. The production projection retains this build
path, but never derives a deployable image from a floating `latest` tag.
To stamp build identity into the images so a running container can say where it came from:
```bash
MODELFORGE_VERSION=1.2.1 \
MODELFORGE_COMMIT=$(git rev-parse HEAD) \
MODELFORGE_BUILT_AT=$(date -u +%FT%TZ) \
docker compose -f docker-compose.yml -f docker-compose.production.yml up -d --build
```
## 6. Verify
```bash
curl -s http://127.0.0.1:8000/api/v1/health/live # 200
curl -s http://127.0.0.1:8000/api/v1/health/ready # 200
curl -s http://127.0.0.1:8000/api/v1/version # version, commit, compatibility
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8000/api/v1/admin/recovery/dashboard # 401
curl -s -o /dev/null -w '%{http_code}\n' -H "X-ModelForge-Admin-Token: $KEY" \
http://127.0.0.1:8000/api/v1/admin/recovery/dashboard # 200
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3000/ # 200
```
The 401 matters as much as the 200: it is the operator boundary refusing an unauthenticated caller.
The `migrate` container runs Alembic once with the non-superuser `modelforge` schema-owner URL and
must complete before the API starts. The API receives only the `modelforge_runtime` URL; it cannot
read the owner or bootstrap secret. Migration 0024 grants that runtime role SELECT on the audit
tables and EXECUTE on the canonical append function while revoking direct audit mutation and DDL.
Production startup re-reads the PostgreSQL catalog and refuses any drift in that boundary.
To run bootstrap tooling yourself, or to check what a clean database would do:
```bash
python scripts/bootstrap.py --database-url postgresql+psycopg://user:pass@127.0.0.1:5432/modelforge
```
`bootstrap.py` is safe to run twice. Every step either creates what is missing or confirms what is
already there and says which. Running it repeatedly on an installed system changes nothing — the
release rehearsal ran it three times and all ten authoritative counts were identical.
## 7. Next
- [FIRST_RUN.md](FIRST_RUN.md) — what to do with a running control plane
- [NODE_AGENT.md](NODE_AGENT.md) — enrol a GPU compute node
- [OPERATIONS.md](OPERATIONS.md) — runbooks, health model, backups
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) — the states you will actually meet
## What a fresh install does not include
A newly installed control plane has **no capability contracts and no deployments**. That is
deliberate: contracts are lifecycle-owned and a deployment requires a model artifact that has been
acquired, verified and promoted. The projects and policies from the shipped manifests are seeded,
and the bindings that wait for a contract are reported rather than fabricated. See
[FIRST_RUN.md](FIRST_RUN.md).