Files
issue-core/workplans/ISSUE-WP-0003-railiance01-deployment.md
tegwick 3e29bc964d Add railiance01 deployment artifacts and fix container image build
Introduce Dockerfile, entrypoint, and k8s/railiance manifests for the
ArgoCD GitOps pilot (ISSUE-WP-0003). Rename the Gitea PyPI build arg to
GITEA_PYPI_INDEX_URL so pip still resolves dependencies from PyPI.
2026-06-19 21:05:18 +02:00

9.9 KiB

id, type, title, domain, repo, status, owner, topic_slug, created, updated, state_hub_workstream_id
id type title domain repo status owner topic_slug created updated state_hub_workstream_id
ISSUE-WP-0003 workplan Deploy issue-core as a service on railiance01 (ArgoCD GitOps pilot) custodian issue-core active claude custodian 2026-06-19 2026-06-19

Deploy issue-core as a service on railiance01 (ArgoCD GitOps pilot)

issue-core is the authoritative task-lifecycle manager and the REST ingestion target for activity-core's IssueSink. Deployment artifacts (Dockerfile, docker-entrypoint.sh, k8s/railiance/) are now in-repo; the image builds locally and /healthz returns 200. The railiance01 cluster still has no issue-core namespace or workload — nothing is deployed until T01 push and T02 ArgoCD bootstrap complete.

This workplan stands up issue-core as a first-class in-cluster service on railiance01 via ArgoCD GitOps — making issue-core the cluster's first declarative Application and turning on the idle GitOps capability.

Current state (verified 2026-06-19)

  • Deployment artifacts in-repo: Dockerfile, docker-entrypoint.sh, and k8s/railiance/ (Kustomize: ExternalSecret, ConfigMap, Deployment, Service). Image builds locally; docker run + GET /healthz returns 200. Image not yet pushed to gitea.coulomb.social/coulomb/issue-core:0.2.0.
  • Dockerfile fix (2026-06-19): build arg renamed GITEA_PYPI_INDEX_URLARG PIP_INDEX_URL leaked into the build env and pip used Gitea as the sole index, so dependencies like click were not found.
  • railiance01 cluster: no issue-core namespace; no issue-core Deployment/Service/Pod in any namespace.
  • Dangling reference: activity-core/k8s/railiance/20-runtime.yaml sets ISSUE_CORE_URL: http://issue-core.issue-core.svc.cluster.local:8010 — a service that does not exist, on the wrong port (issue-core serves 8765) — with ISSUE_SINK_TYPE: "null" so emission is disabled. It is a placeholder.
  • Packaging precursor is done: ISSUE-WP-0002 published issue-core==0.2.0 to the Coulomb Gitea PyPI index.
  • ArgoCD is installed but unused: all 7 components healthy (~290d), but 0 Applications, 0 ApplicationSets, 0 registered git repos, only the stock default AppProject. No kind: Application manifests exist in any infra repo.
  • Existing deploy pattern is imperative (the path we are replacing for this service): local docker buildk3s ctr images import (side-load, no registry) → rsync manifests → kubectl apply (see activity-core/k8s/railiance/README.md).

Decisions

  • Deployment method = ArgoCD GitOps (operator decision 2026-06-19). issue-core is the pilot Application; the imperative side-load pattern is not used for this service.
  • ArgoCD bootstrap owned by railiance-platform (operator decision 2026-06-19). Platform owns repo registration, AppProject/app-of-apps conventions, and the External-Secrets/OpenBao plumbing. issue-core only contributes its Application manifest + workload manifests into the agreed GitOps source. T02 is therefore a cross-repo dependency, not issue-core work — see handoff to railiance-platform.
  • Backend = cluster Gitea (markitect) (operator decision 2026-06-19). Ingested tasks route to the existing Gitea backend; no new Postgres/PVC.
  • Secret management = OpenBao. ISSUE_CORE_API_KEY is a shared ingestion key injected from OpenBao on both issue-core and the activity-core worker. ops-warden does not vend it (see ~/ops-warden/wiki/playbooks/activity-core-issue-sink.md). Coordinate the canonical path with railiance-platform (issue-core-ingestion-api-key).
  • Image delivery = container registry, not side-load. GitOps requires a pullable image tag in a registry the cluster can reach (the Coulomb Gitea container registry); side-loading defeats declarative reproducibility.

Open questions

  • GitOps source repo. Resolved by railiance-platform as part of the bootstrap (T02 dependency): where issue-core's Application + manifests are expected to live (its own issue-core/k8s/ vs. a platform GitOps repo) and the AppProject/app-of-apps convention to follow.
  • Registry path & pull secret. Confirm the Coulomb Gitea container registry path and the cluster pull-secret posture (tracked in railiance-forge container-registry docs and railiance-apps-WP-0004 I03).

Container image published to a pullable registry

id: ISSUE-WP-0003-T01
status: in_progress
priority: high

Goal. A reproducible, registry-hosted image ArgoCD-managed pods can pull.

  • Add Dockerfile installing issue-core[api]>=0.2,<0.3 from the Gitea PyPI index (with explicit PyPI primary index). Entrypoint renders backends.json then issue serve --host 0.0.0.0 --port 8765.
  • Local build succeeds; docker run + GET /healthz returns 200.
  • Build and push to the Coulomb Gitea container registry (confirm path per Open questions); tag 0.2.0.
  • Configure the cluster pull secret so issue-core namespace pods can pull.
  • Verify: POST /issues/ smoke; pushed tag pullable from the cluster.

ArgoCD bootstrap (railiance-platform dependency) + issue-core Application

id: ISSUE-WP-0003-T02
status: wait
priority: high

Owner split. ArgoCD bootstrap is railiance-platform's (operator decision 2026-06-19): repo registration in ArgoCD, AppProject/app-of-apps convention, and the agreed GitOps source layout. This task is wait on that handoff. issue-core's part is to contribute the Application manifest + workload manifests into the layout platform defines.

  • (railiance-platform) Register the GitOps source repo (repository Secret + creds); define AppProject for cluster services; publish the source-repo/path convention and sync policy.
  • (issue-core) Once the convention is known: author the issue-core ArgoCD Application manifest (source repo/path/revision → destination issue-core namespace) per the platform layout.
  • Verify: kubectl get applications -n argocd shows issue-core Synced/Healthy; ArgoCD reconciles a trivial manifest change.

Kubernetes manifests (namespace, Deployment, Service) in GitOps source

id: ISSUE-WP-0003-T03
status: in_progress
priority: high

Goal. Declarative manifests in the GitOps source repo, synced by T02.

  • k8s/railiance/ Kustomize bundle (namespace via ArgoCD CreateNamespace=true).
  • Deployment: registry image tag 0.2.0; port 8765; /healthz probes; resource requests/limits; env from ExternalSecret (T04) and ConfigMap (T05).
  • Service: ClusterIP on 8765 as issue-core.issue-core.svc.cluster.local.
  • Verify: ArgoCD syncs the manifests; Pod Ready; /healthz 200 from a debug pod (blocked on T01 push + T02 bootstrap + T04 secrets).

OpenBao secret: ISSUE_CORE_API_KEY

id: ISSUE-WP-0003-T04
status: todo
priority: high

Goal. The shared ingestion key delivered to both sides from OpenBao.

  • Provision ISSUE_CORE_API_KEY in OpenBao at the canonical path (coordinate with railiance-platform; catalog id issue-core-ingestion-api-key).
  • Deliver into the issue-core Deployment (T03) and the activity-core worker (T06) with the same value (External Secrets / Bao injector — match the cluster's established mechanism).
  • Never write the value to Git, manifests, State Hub, or logs.
  • Verify: both pods resolve a non-empty key; auth round-trip (401 without, 201 with).

In-cluster backend config (cluster Gitea / markitect)

id: ISSUE-WP-0003-T05
status: in_progress
priority: medium

Goal. issue-core's backends.json inside the cluster points default at the cluster Gitea (markitect) backend.

  • ConfigMap issue-core-backends with in-cluster Gitea URL (gitea-http.default.svc.cluster.local:3000); token sentinel __FROM_ENV__.
  • docker-entrypoint.sh renders ~/.config/issue-tracker/backends.json from BACKENDS_TEMPLATE + GITEA_BACKEND_TOKEN at startup.
  • Verify: a POST /issues/ creates a real Gitea issue and returns issue_url (blocked on T04 secrets + in-cluster deployment).

Wire activity-core to the live service

id: ISSUE-WP-0003-T06
status: todo
priority: high

Goal. activity-core emits to the live issue-core Service.

  • Fix activity-core/k8s/railiance/20-runtime.yaml: ISSUE_CORE_URL port 8010 -> 8765; flip ISSUE_SINK_TYPE null -> rest once issue-core is Ready.
  • Inject ISSUE_CORE_API_KEY into the activity-core worker from the same OpenBao secret (T04).
  • Contract gap: issue-core requires triggering_event_id as a UUID; activity-core cron paths may send non-UUID keys (e.g. "scheduled"). Event-driven emission with real event UUIDs works (the str() guard in issue_sink.py, commit f05c56e, handles UUID objects). Align schemas before enabling rest for cron-triggered rules.
  • Verify: an activity-core run emits a task that lands in cluster Gitea via issue-core.

End-to-end verification + GitOps runbook

id: ISSUE-WP-0003-T07
status: todo
priority: medium

Goal. Confirm the deployed service is healthy and document the new path.

  • ArgoCD Application Synced/Healthy; issue-core Pod Ready; Service reachable cluster-internal.
  • activity-core → issue-core emission returns 201 and creates a Gitea issue.
  • Document the GitOps runbook (image build/push, ArgoCD sync, secret rotation, rollback) in docs/.
  • Emit an add_progress_event milestone to the hub on completion.

See also

  • ISSUE-WP-0002 — Gitea PyPI publication (packaging precursor, finished).
  • railiance-apps-WP-0004 I03 — issue-core packaging/image enablement notes.
  • railiance-forge — Gitea container registry docs.
  • activity-core/docs/issue-core-emission-boundary.md — emission contract.
  • activity-core/k8s/railiance/README.md — the imperative pattern being superseded for this service.
  • ~/ops-warden/wiki/playbooks/activity-core-issue-sink.md — key routing.