diff --git a/concept.md b/concept.md
index 43e3167..827f6b7 100644
--- a/concept.md
+++ b/concept.md
@@ -10,9 +10,9 @@ At the same time, deployment should no longer give application CI jobs direct ac
| Component | Responsibility |
| --------------------------------- | --------------------------------------------------------------------------------- |
-| **Gitea** | Git hosting and, if suitable, OCI registry |
+| **Gitea** | Git hosting and webhook trigger source |
| **Woodpecker CI** | CI pipelines, image builds and triggering deployments |
-| **OCI registry** | Stores container images and immutable deployment artifacts |
+| **Zot** | OCI registry storing container images and immutable deployment artifacts, with retention policies |
| **ORAS** | Publishes and retrieves arbitrary deployment bundles as OCI artifacts |
| **SOPS + age** | Encrypts application secrets committed to service repositories |
| **Central deployment repository** | Contains generic deployment logic and the production credentials |
@@ -73,7 +73,7 @@ flowchart TD
WP --> Test["Tests / quality checks"]
Test --> Build["Build container image"]
- Build -->|push image| Registry["Gitea OCI Registry
or dedicated OCI registry"]
+ Build -->|push image| Registry["Zot
OCI registry"]
WP --> Bundle["Create deployment bundle
compose.yaml
encrypted secrets.prod.env
release metadata"]
@@ -144,15 +144,17 @@ This avoids having the deployment pipeline clone or check out the original Git r
### 3. Trigger the central deployment pipeline
-After publishing the artifacts, the application pipeline triggers the Woodpecker pipeline of `deployments.git`.
+After publishing the artifacts, the application pipeline triggers the Woodpecker pipeline of `deployments.git` using the official `woodpeckerci/plugin-trigger` plugin, which calls the Woodpecker server API to start a pipeline in another repository.
-Only a small amount of non-secret information needs to be passed:
+Only a small amount of non-secret information needs to be passed as trigger params:
```text
ARTIFACT=registry.example.org/deploy/passbolt@sha256:...
TARGET=cont2
```
+The trigger step authenticates with a Woodpecker API token stored as a Woodpecker secret. This token should belong to a dedicated bot/service account rather than a personal account, since it grants the ability to start pipelines on any repository the token owner can access, and should be scoped as narrowly as Woodpecker allows.
+
The application pipeline has no production SSH key and no SOPS decryption key.
Woodpecker therefore shows the build and deployment as separate pipeline runs, making failures easy to locate and deployments independently inspectable.
@@ -171,7 +173,9 @@ cont2
cont3
```
-The central deployment machinery resolves these to actual SSH destinations. Arbitrary SSH hosts, usernames or commands should not be accepted from application repositories.
+The central deployment machinery resolves these to actual SSH destinations using the corresponding file under `hosts/` in `deployments.git`. Arbitrary SSH hosts, usernames or commands should not be accepted from application repositories; only pre-defined aliases resolve to a target, and an unknown alias fails closed.
+
+Initially, resolution is kept simple: any service repository may request deployment to any existing alias, with no per-repository allow-list. Restricting which repositories may deploy to which aliases is a possible later refinement, not required for the initial model.
### 5. Decrypt runtime secrets
@@ -276,7 +280,7 @@ If an age private key is actually compromised, application credentials contained
## Registry
-The first choice is the existing **Gitea OCI/container registry**, avoiding another persistent infrastructure service.
+**Zot** is the chosen OCI registry, run as a small dedicated service rather than reusing Gitea's built-in registry. Zot supports retention policies (e.g. pruning old image and artifact digests), which the Gitea OCI registry does not offer and which is needed to keep registry storage bounded over time.
It needs to support two types of content:
@@ -288,14 +292,13 @@ Deployment artifact
registry.example.org/deploy/passbolt@sha256:...
```
-Before relying on it, interoperability with ORAS and arbitrary OCI artifacts should be tested, including:
+Interoperability with ORAS and arbitrary OCI artifacts should still be verified as part of initial setup, including:
* pushing an artifact;
* retrieving it;
* resolving its digest;
-* pulling it by digest.
-
-If Gitea proves unsuitable for arbitrary OCI artifacts, **Zot** is the preferred lightweight dedicated OCI registry.
+* pulling it by digest;
+* retention/garbage-collection behaviour for untagged or aged-out digests.
## Trust boundaries
@@ -419,4 +422,10 @@ It was not selected because the services rely heavily on host bind mounts, for w
Harbor provides extensive OCI registry functionality, including richer access control, robot accounts, scanning, retention and replication.
-It was considered heavier than necessary. The existing Gitea registry should be tried first; Zot is the preferred lightweight fallback if Gitea's OCI artifact support proves insufficient.
+It was considered heavier than necessary. Zot provides the required retention behaviour and ORAS/OCI-artifact support with substantially less operational overhead.
+
+## Gitea OCI/container registry
+
+Using Gitea's built-in OCI registry was the initial preference, since it would avoid running another persistent infrastructure service.
+
+It was dropped in favour of Zot because Gitea's registry does not support retention policies, which are needed to keep registry storage bounded as images and deployment artifacts accumulate. ORAS/OCI-artifact interoperability with Gitea's registry was also not yet verified at the time this decision was made.