Gitea container repo activation

This commit is contained in:
2026-05-15 23:02:21 +02:00
parent d892cbfb13
commit e24568cb40
5 changed files with 516 additions and 5 deletions

65
AGENTS.md Normal file
View File

@@ -0,0 +1,65 @@
# railiance-apps — Codex Instructions
**OAS Stack Level:** S5 Workloads & Experience Endpoints
**Scope:** Application Helm releases and Kubernetes manifests for
user-facing services — Gitea, coulomb services, APIs, web frontends.
**Pre-condition:** The full stack below must be operational:
`railiance-infra``railiance-cluster``railiance-platform``railiance-enablement`
## Custodian State Hub Integration
Domain: **railiance** — topic ID: `ca369340-a64e-442e-98f1-a4fa7dc74a38`
State Hub: http://127.0.0.1:8000
### Session Protocol
**Step 1 — Orient**
```
get_domain_summary("railiance")
```
**Step 2 — Scan workplans**
```
ls workplans/ # read all active workplans; note todo/in_progress tasks
```
**Step 3 — Present brief**
1. Active workstreams for railiance with `[repo:railiance-apps]` tasks
2. Pending tasks from local workplans
3. Goal guidance from summary (needs_workplan / alignment_warnings)
4. Suggested next action
**During work:** use `record_decision()`, `add_progress_event()`, `resolve_decision()`.
**Session close:** `add_progress_event()` with topic_id and workstream_id.
> Design boundary: hub is read model. Bootstrap tools are First Session
> Protocol only. Work originates as files per ADR-001.
### Repo Boundary Rule (ADR-003)
This repo owns **S5 Workloads & Experience Endpoints only**. Do not manage:
- OS-level concerns → `railiance-infra` (S1)
- Kubernetes runtime → `railiance-cluster` (S2)
- Platform services → `railiance-platform` (S3)
- Developer tooling → `railiance-enablement` (S4)
Reference: `railiance-infra/docs/adr/ADR-003-railiance-5repo-stack-architecture.md`
### Workplan Convention (ADR-001)
File location: `workplans/RAIL-AP-WP-NNNN-<slug>.md`
Prefix: `RAIL-AP`
### SBOM
After updating dependencies:
```bash
cd ~/the-custodian/state-hub
make ingest-sbom REPO=railiance-apps SCAN=1 REPO_PATH=/home/worsch/railiance-apps
```
### Quick Reference
`~/the-custodian/state-hub/mcp_server/TOOLS.md`

View File

@@ -1,15 +1,26 @@
SHELL := /usr/bin/env bash SHELL := /usr/bin/env bash
.DEFAULT_GOAL := help .DEFAULT_GOAL := help
GITEA_RELEASE ?= gitea
GITEA_NAMESPACE ?= default
GITEA_CHART ?= gitea-charts/gitea
GITEA_VALUES ?= helm/gitea-values.sops.yaml
GITEA_INGRESS ?= manifests/gitea-ingress.yaml
##@ Gitea ##@ Gitea
gitea-deploy: ## Deploy / upgrade Gitea (S5 workload) gitea-deploy: ## Deploy / upgrade Gitea (S5 workload)
helm upgrade --install gitea gitea-charts/gitea \ helm upgrade --install $(GITEA_RELEASE) $(GITEA_CHART) \
-f <(sops -d helm/gitea-values.sops.yaml) \ -f <(sops -d $(GITEA_VALUES)) \
--namespace gitea --create-namespace --namespace $(GITEA_NAMESPACE) --create-namespace
gitea-ingress-deploy: ## Apply the Gitea OCI registry ingress
kubectl apply -f $(GITEA_INGRESS)
gitea-status: ## Check Gitea health gitea-status: ## Check Gitea health
kubectl get pods -n gitea kubectl get pods -n $(GITEA_NAMESPACE) -l app.kubernetes.io/instance=$(GITEA_RELEASE)
kubectl get svc -n $(GITEA_NAMESPACE) $(GITEA_RELEASE)
kubectl get ingress -n $(GITEA_NAMESPACE) $(GITEA_RELEASE) --ignore-not-found
kubectl cnpg status gitea-db -n databases kubectl cnpg status gitea-db -n databases
##@ Help ##@ Help
@@ -19,4 +30,4 @@ help: ## Show this help
/^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } \ /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } \
/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST) /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST)
.PHONY: gitea-deploy gitea-status help .PHONY: gitea-deploy gitea-ingress-deploy gitea-status help

View File

@@ -0,0 +1,50 @@
# Gitea Container Registry
## Registry Target
Use `gitea.coulomb.social` as the approved registry host. The `/v2` ingress is
live as of 2026-05-15 and returns the OCI registry authentication challenge over
HTTPS.
The encrypted Helm values still need an explicit package-registry stanza once
the SOPS age identity is available in the operator session.
Image names should use the Gitea owner and package path:
```bash
gitea.coulomb.social/coulomb/state-hub:<tag>
```
The State Hub handoff from `CUST-WP-0011` should publish the locally verified
`state-hub:local` image under that name.
## Operator Smoke Test
Use a Gitea personal access token with package read/write permission:
```bash
docker login gitea.coulomb.social
docker tag state-hub:local gitea.coulomb.social/coulomb/state-hub:<tag>
docker push gitea.coulomb.social/coulomb/state-hub:<tag>
docker pull gitea.coulomb.social/coulomb/state-hub:<tag>
```
For private packages, create an image pull secret in each consuming namespace:
```bash
kubectl create secret docker-registry gitea-registry \
--docker-server=gitea.coulomb.social \
--docker-username=<gitea-user> \
--docker-password=<package-token> \
--namespace=<namespace>
```
Reference it from workloads as `imagePullSecrets: [{name: gitea-registry}]`.
## Current Storage Notes
The live Gitea pod mounts `gitea-shared-storage` at `/data`; package blobs are
expected to land on that existing PVC unless a separate package storage backend
is configured. The live cluster did not show Kubernetes `CronJob` backups for
the namespace during the 2026-05-15 inventory, so package backup coverage needs
operator confirmation before publishing many tags.

View File

@@ -0,0 +1,29 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitea
namespace: default
labels:
app.kubernetes.io/name: gitea
app.kubernetes.io/instance: gitea
app.kubernetes.io/part-of: railiance-apps
railiance/component: gitea-registry
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: traefik
rules:
- host: gitea.coulomb.social
http:
paths:
- path: /v2
pathType: Prefix
backend:
service:
name: gitea
port:
number: 3000
tls:
- hosts:
- gitea.coulomb.social
secretName: gitea-tls

View 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.