# Deployment

> Container images built per commit by CI, verified as a complete set before the commit counts as a release, pulled by the target which refuses to start a partial one - with each service reporting the commit it was built from

Source: https://aipril.dev/docs/platform/environments/deployment

## The topology

A container per service, composed: the control plane, the query engine, the extract executor, the
ledger service, the process service, the collaboration server, PostgreSQL, Redis, S3-compatible
object storage, the identity services, the orchestration engine and its workers, and a reverse proxy
in front.

The same composition runs in the cloud and inside a customer perimeter. What differs is who operates
it and which model providers are configured.

## A release is a commit

The rule that everything else follows from: **a release is a commit, not a set of images that happen
to be current.**

| Stage | What happens |
|---|---|
| Push to main | CI builds one image per service |
| Tagging | Each image is tagged with the commit. That tag is immutable. |
| Verification | A final job confirms every image in the set exists. A partial build is not a release. |
| Deploy | The target pulls that commit's set and **refuses to start** if any image for it is missing |
| Confirmation | Services report the commit they were built from, so the fleet can be checked for agreement |

There is also a moving convenience tag per service. It is informational and production never uses it:
a per-service moving tag cannot say that the fleet agrees on one commit, which is the only question
worth asking during an incident.

## Build and run are separate descriptions

The base composition describes how each service is **built**; a deployment overlay describes what to
**run** - published images, not local builds. Neither leaks into the other, so a developer's
local rebuild and a production deploy cannot silently change each other's meaning.

## Migrations

Database migrations run on their own path at service start, not as a deploy step. A deploy that
spans several commits carries whatever migrations accumulated between them, which is worth checking
before a large jump.

## Verifying what is running

Because every service reports its build commit, "what is production running" is a query, not a
comparison of image timestamps. A periodic check compares the reporting services against an expected
commit.

## On-prem

The same path: a customer pulls a verified image set on their own schedule. Air-gapped installations
take the set as an archive. Model providers are configured locally, so an on-prem deployment can run
entirely without outbound network access - see [Privacy and on-prem](https://aipril.dev/docs/harness/privacy.md) for the
hardware.

## Related

- [Platform](https://aipril.dev/docs/platform.md) - the service topology and where the scaling levers are.
- [Versions and the built-in git](https://aipril.dev/docs/platform/versions.md) - the same rule one level up: an
  application's release is a commit too.
- [Licensing](https://aipril.dev/docs/platform/environments/licensing.md) - what an installation is entitled to run.
- [Audit](https://aipril.dev/docs/platform/audit.md) - what a deployed system records.
