Gitea container repo activation
This commit is contained in:
356
workplans/RAIL-AP-WP-0001-gitea-container-registry.md
Normal file
356
workplans/RAIL-AP-WP-0001-gitea-container-registry.md
Normal file
@@ -0,0 +1,356 @@
|
||||
---
|
||||
id: RAIL-AP-WP-0001
|
||||
type: workplan
|
||||
title: "Enable Gitea Container Registry for Cluster Image Publishing"
|
||||
domain: railiance
|
||||
repo: railiance-apps
|
||||
status: active
|
||||
owner: railiance
|
||||
topic_slug: railiance
|
||||
created: "2026-05-15"
|
||||
updated: "2026-05-15"
|
||||
planning_priority: high
|
||||
planning_order: 1
|
||||
state_hub_workstream_id: "abd268e6-5af9-45ec-93e0-5ffca0211dd0"
|
||||
---
|
||||
|
||||
# Enable Gitea Container Registry for Cluster Image Publishing
|
||||
|
||||
## Goal
|
||||
|
||||
Enable the existing Railiance-managed Gitea deployment to serve as an OCI
|
||||
container registry so cluster workloads can publish and pull images through the
|
||||
same source-forge boundary already used for Git hosting.
|
||||
|
||||
The immediate forcing function is `CUST-WP-0011`: the State Hub image
|
||||
`state-hub:local` builds and smoke-tests locally, but cannot be published until
|
||||
Docker can authenticate against the Gitea registry `/v2/` endpoint.
|
||||
|
||||
## Placement in the Railiance Tooling Set
|
||||
|
||||
This workplan lives in `railiance-apps` because Gitea is an S5 application
|
||||
workload. The active deployment surface is:
|
||||
|
||||
- `helm/gitea-values.sops.yaml` — SOPS-encrypted Gitea Helm values.
|
||||
- `Makefile` target `gitea-deploy` — applies the Gitea Helm release.
|
||||
- `Makefile` target `gitea-status` — checks Gitea pod and database health.
|
||||
|
||||
Lower-layer ownership remains unchanged:
|
||||
|
||||
| Concern | Owner repo | Notes |
|
||||
|---------|------------|-------|
|
||||
| Gitea application config and Helm release | `railiance-apps` | This workplan |
|
||||
| Gitea database and CNPG health | `railiance-platform` | Existing `gitea-db` |
|
||||
| Ingress controller / cluster routing primitives | `railiance-cluster` | Only if `/v2/` requires lower-layer route changes |
|
||||
| Forgejo replacement roadmap | `railiance-infra` | `RAIL-HO-WP-0005` remains the umbrella plan |
|
||||
|
||||
This is not a replacement for the Forgejo production migration. It is a
|
||||
pragmatic enablement step for current Gitea so images needed before Forgejo
|
||||
cutover have a governed registry target.
|
||||
|
||||
## Current Evidence
|
||||
|
||||
- `railiance-apps/SCOPE.md` states that Gitea Helm values are owned here and
|
||||
that Gitea is the active git hosting platform for Railiance and Custodian
|
||||
repos.
|
||||
- `railiance-apps/Makefile` deploys Gitea via `helm/gitea-values.sops.yaml` into
|
||||
the `gitea` namespace.
|
||||
- `helm/gitea-values.sops.yaml` currently contains Gitea app config sections
|
||||
such as `server`, `database`, `cache`, `session`, and `queue`, but no visible
|
||||
unencrypted package-registry section.
|
||||
- `CUST-WP-0011` recorded Docker login/push receiving HTTP 404 from `/v2/`.
|
||||
Runtime inspection found no `[packages]` section in the live Gitea `app.ini`.
|
||||
|
||||
## Safety Contract
|
||||
|
||||
- Do not commit decrypted Helm values or secrets.
|
||||
- Take a Gitea backup or verify the most recent backup before changing the live
|
||||
Helm release.
|
||||
- Preserve Git hosting behavior: clone, push, login, and repository browsing
|
||||
must remain healthy after any registry change.
|
||||
- Do not broaden public exposure beyond the current Gitea exposure model.
|
||||
- If package storage/backups require platform changes, pause and create or link
|
||||
a `railiance-platform` task before changing S3 resources.
|
||||
- If `/v2/` routing requires ingress-controller or NodePort changes, pause and
|
||||
create or link a `railiance-cluster` task before changing S2 resources.
|
||||
|
||||
## Target State
|
||||
|
||||
- Gitea package registry is globally enabled.
|
||||
- Gitea container package uploads are allowed with an explicit size policy.
|
||||
- `/v2/` reaches Gitea and returns an OCI registry authentication challenge
|
||||
rather than a generic 404.
|
||||
- Docker can log in with a package-capable personal access token.
|
||||
- The State Hub image can be tagged, pushed, pulled, and referenced by a future
|
||||
cluster deployment.
|
||||
- The chosen registry URL and image naming convention are documented for
|
||||
downstream workplans.
|
||||
|
||||
## Tasks
|
||||
|
||||
### T01 — Inventory current registry and routing state
|
||||
|
||||
```task
|
||||
id: RAIL-AP-WP-0001-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "30075930-6585-465d-9b8f-1c5f2304632d"
|
||||
```
|
||||
|
||||
Confirm the live Gitea registry state before changing Helm values.
|
||||
|
||||
Checks:
|
||||
|
||||
- Confirm the active Helm release, chart version, namespace, and values source.
|
||||
- Inspect live Gitea `app.ini` for `[packages]`, `[repository]`, `ROOT_URL`, and
|
||||
any package or repository-unit settings.
|
||||
- Check `GET /v2/` through each expected access path: service, NodePort or
|
||||
tunnel, and any ingress hostname.
|
||||
- Record the current response codes and headers for `/v2/`.
|
||||
- Identify the registry hostname that Docker and Kubernetes should use.
|
||||
|
||||
**Done when:** the workplan records whether the blocker is app config, route
|
||||
config, TLS/trust, authentication, or a combination.
|
||||
|
||||
---
|
||||
|
||||
### T02 — Enable Gitea package and container registry config
|
||||
|
||||
```task
|
||||
id: RAIL-AP-WP-0001-T02
|
||||
status: blocked
|
||||
priority: high
|
||||
state_hub_task_id: "e4136a4a-7730-47fe-bf64-315a513a3d8b"
|
||||
```
|
||||
|
||||
Update `helm/gitea-values.sops.yaml` through `sops` so the generated Gitea
|
||||
`app.ini` enables packages and permits container uploads.
|
||||
|
||||
Expected app configuration:
|
||||
|
||||
```ini
|
||||
[packages]
|
||||
ENABLED = true
|
||||
LIMIT_SIZE_CONTAINER = -1
|
||||
```
|
||||
|
||||
Also verify repository package units are not globally disabled:
|
||||
|
||||
```ini
|
||||
[repository]
|
||||
DISABLED_REPO_UNITS =
|
||||
```
|
||||
|
||||
If the chart values require YAML nesting, express those settings under the
|
||||
existing `gitea.config` tree without exposing decrypted secrets in Git.
|
||||
|
||||
**Done when:** a dry-rendered or live-inspected `app.ini` includes the package
|
||||
registry settings and no decrypted secret material was committed.
|
||||
|
||||
---
|
||||
|
||||
### T03 — Ensure `/v2/` reaches the Gitea registry handler
|
||||
|
||||
```task
|
||||
id: RAIL-AP-WP-0001-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "21c503be-12c7-411c-a82c-f738536cc114"
|
||||
```
|
||||
|
||||
Make the OCI registry endpoint reachable at the root `/v2/` path for the chosen
|
||||
registry host.
|
||||
|
||||
Validation:
|
||||
|
||||
- `curl -i https://<gitea-host>/v2/` should return the expected registry auth
|
||||
challenge or an auth-related response, not a generic 404.
|
||||
- Docker must not be pointed at a sub-path registry URL; the registry name is
|
||||
the host, and `/v2/` is fixed by the OCI distribution API.
|
||||
- If Gitea is served through a sub-path, route root-level `/v2/` to the Gitea
|
||||
service as required by Docker-compatible registries.
|
||||
|
||||
Boundary note: Gitea Helm ingress/service settings belong here. Ingress
|
||||
controller or cluster network changes belong in `railiance-cluster`.
|
||||
|
||||
**Done when:** `/v2/` is routed correctly through the intended operator and
|
||||
cluster access paths.
|
||||
|
||||
---
|
||||
|
||||
### T04 — Prove Docker login, push, and pull
|
||||
|
||||
```task
|
||||
id: RAIL-AP-WP-0001-T04
|
||||
status: blocked
|
||||
priority: high
|
||||
state_hub_task_id: "5ffd7515-384b-4a11-9b5e-141197d1b985"
|
||||
```
|
||||
|
||||
Use a Gitea user or bot personal access token with package read/write
|
||||
permissions to prove the registry workflow.
|
||||
|
||||
Smoke sequence:
|
||||
|
||||
```bash
|
||||
docker login <gitea-host>
|
||||
docker tag state-hub:local <gitea-host>/coulomb/state-hub:<tag>
|
||||
docker push <gitea-host>/coulomb/state-hub:<tag>
|
||||
docker pull <gitea-host>/coulomb/state-hub:<tag>
|
||||
```
|
||||
|
||||
Then verify pull behavior from the cluster node runtime or a disposable
|
||||
Kubernetes pod, including TLS trust and private-registry credentials if the
|
||||
package is private.
|
||||
|
||||
**Done when:** the State Hub image can be pushed and pulled by both the
|
||||
operator workstation and the Railiance cluster runtime.
|
||||
|
||||
---
|
||||
|
||||
### T05 — Document registry handoff for State Hub deployment
|
||||
|
||||
```task
|
||||
id: RAIL-AP-WP-0001-T05
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "55c2fd0c-ee6b-4524-8022-f21d6e9e046f"
|
||||
```
|
||||
|
||||
Record the approved registry target and downstream handoff details.
|
||||
|
||||
Expected output:
|
||||
|
||||
- Registry host and owner/image naming convention.
|
||||
- State Hub image tag used for the successful smoke test.
|
||||
- Whether images are public or private.
|
||||
- Required Kubernetes `imagePullSecret` name or creation command if private.
|
||||
- Link back to `CUST-WP-0011` and its container image provenance.
|
||||
|
||||
**Done when:** State Hub cluster deployment work can consume the image without
|
||||
rediscovering registry naming, auth, or TLS requirements.
|
||||
|
||||
---
|
||||
|
||||
### T06 — Capture backup and retention implications
|
||||
|
||||
```task
|
||||
id: RAIL-AP-WP-0001-T06
|
||||
status: blocked
|
||||
priority: medium
|
||||
state_hub_task_id: "d5734ef1-d710-458c-b569-034f03a50bd8"
|
||||
```
|
||||
|
||||
Confirm how Gitea package data is stored and backed up once container images
|
||||
are published.
|
||||
|
||||
Checks:
|
||||
|
||||
- Identify whether package blobs live on the existing Gitea persistent volume or
|
||||
another configured storage backend.
|
||||
- Confirm the current backup process includes package data.
|
||||
- Decide whether image retention or cleanup policy is needed before publishing
|
||||
many tags.
|
||||
- If storage or backups need S3/platform changes, create a follow-up
|
||||
`railiance-platform` workplan or task.
|
||||
|
||||
**Done when:** package data durability is understood and no hidden storage gap
|
||||
is introduced by enabling the registry.
|
||||
|
||||
## Implementation Log
|
||||
|
||||
### 2026-05-15 — Inventory and S5 routing update
|
||||
|
||||
T01 findings:
|
||||
|
||||
- Active Kubernetes context: `default`.
|
||||
- Live Helm release metadata is stored in namespace `default` as revisions
|
||||
`sh.helm.release.v1.gitea.v1` through `v6`.
|
||||
- Live deployment labels report chart `gitea-12.5.0`, app version `1.25.4`,
|
||||
image `docker.gitea.com/gitea:1.25.4-rootless`.
|
||||
- Live Gitea service is `default/gitea`, type `NodePort`, port
|
||||
`3000:32166/TCP`.
|
||||
- `default/gitea` pod app.ini has server `ROOT_URL =
|
||||
http://gitea.coulomb.social`, `SSH_DOMAIN = gitea.coulomb.social`, and
|
||||
`DOMAIN = gitea.coulomb.social`.
|
||||
- No `[packages]` section was found in the inspected live app.ini output, but
|
||||
the application handler is active: pod-local `/v2/` and
|
||||
`http://92.205.130.254:32166/v2/` both returned OCI registry
|
||||
`401 Unauthorized` with `Docker-Distribution-Api-Version: registry/2.0`.
|
||||
- `http://gitea.coulomb.social/v2/` and
|
||||
`https://gitea.coulomb.social/v2/` returned generic `404`, so the immediate
|
||||
blocker is public hostname routing. A secondary cleanup is updating
|
||||
`ROOT_URL` to `https://gitea.coulomb.social/` so future auth challenges use
|
||||
the TLS endpoint.
|
||||
|
||||
T02 status:
|
||||
|
||||
- SOPS editing was attempted with `sops 3.9.0`, matching the file metadata.
|
||||
- The local age identity required by `helm/gitea-values.sops.yaml` was not
|
||||
available at the default path, so encrypted Helm values were not changed.
|
||||
- Once the age identity is available, apply these non-secret app config values:
|
||||
|
||||
```bash
|
||||
sops set helm/gitea-values.sops.yaml '["gitea"]["config"]["packages"]["ENABLED"]' 'true'
|
||||
sops set helm/gitea-values.sops.yaml '["gitea"]["config"]["packages"]["LIMIT_SIZE_CONTAINER"]' '-1'
|
||||
sops set helm/gitea-values.sops.yaml '["gitea"]["config"]["repository"]["DISABLED_REPO_UNITS"]' '""'
|
||||
sops set helm/gitea-values.sops.yaml '["gitea"]["config"]["server"]["ROOT_URL"]' '"https://gitea.coulomb.social/"'
|
||||
```
|
||||
|
||||
T03 implementation:
|
||||
|
||||
- Added `manifests/gitea-ingress.yaml`, a Traefik/cert-manager ingress for
|
||||
only `gitea.coulomb.social/v2*` to the existing `default/gitea` service.
|
||||
- Added `make gitea-ingress-deploy`.
|
||||
- Updated Makefile variables so the default Gitea namespace matches the live
|
||||
release namespace `default`; this avoids accidentally deploying a parallel
|
||||
`gitea` namespace release while the live release remains in `default`.
|
||||
- Applied the ingress to the live cluster.
|
||||
- Cert-manager issued `default/gitea-tls`.
|
||||
- `http://gitea.coulomb.social/v2/` now returns `401 Unauthorized` with
|
||||
`Docker-Distribution-Api-Version: registry/2.0`.
|
||||
- `https://gitea.coulomb.social/v2/` now returns `401 Unauthorized` with
|
||||
`Docker-Distribution-Api-Version: registry/2.0` and a TLS token realm.
|
||||
|
||||
T04 blocker:
|
||||
|
||||
- Docker is available locally, but no Gitea personal access token was present
|
||||
in this session. Login, push, pull, and cluster runtime pull remain blocked
|
||||
on a package-capable token and the T02/T03 deployment.
|
||||
|
||||
T05 handoff:
|
||||
|
||||
- Registry host: `gitea.coulomb.social`.
|
||||
- Image naming convention: `gitea.coulomb.social/coulomb/state-hub:<tag>`.
|
||||
- Handoff notes and `imagePullSecret` command are documented in
|
||||
`docs/gitea-container-registry.md`.
|
||||
- This links back to `CUST-WP-0011`, whose local image provenance is
|
||||
`state-hub:local`.
|
||||
|
||||
T06 findings:
|
||||
|
||||
- Live Gitea package data is expected to use the existing `/data` mount backed
|
||||
by PVC `default/gitea-shared-storage` (`10Gi`, `local-path`) unless package
|
||||
storage is separately configured later.
|
||||
- No Kubernetes `CronJob` backup resources were present in the live cluster
|
||||
inventory. Backup coverage for `gitea-shared-storage` needs operator
|
||||
confirmation or a `railiance-platform` follow-up before publishing many tags.
|
||||
|
||||
## Completion Criteria
|
||||
|
||||
This workplan is complete when:
|
||||
|
||||
1. Gitea's container registry is enabled through governed Helm values.
|
||||
2. `/v2/` is reachable at the chosen registry host.
|
||||
3. `state-hub:local` has been pushed as
|
||||
`<gitea-host>/coulomb/state-hub:<tag>`.
|
||||
4. The pushed image can be pulled from the Railiance cluster runtime.
|
||||
5. Registry auth, TLS, naming, and backup/retention notes are documented.
|
||||
|
||||
## Notes
|
||||
|
||||
The future Forgejo migration should inherit the lessons from this workplan:
|
||||
registry package scope, `/v2/` routing, package data backups, and cluster image
|
||||
pull credentials. If Forgejo lands before this plan starts, close this workplan
|
||||
as superseded and move the tasks under `RAIL-HO-WP-0005` / the appropriate S5
|
||||
Forgejo workplan.
|
||||
Reference in New Issue
Block a user