# ArgoCD GitOps deployment - railiance01 This runbook captures the issue-core side of the railiance01 GitOps pilot. It keeps secrets out of Git and leaves platform-owned bootstrap steps in railiance-platform. ## Source layout - Workload bundle: `issue-core/k8s/railiance/` - Image: `gitea.coulomb.social/coulomb/issue-core:0.2.0` - Container port and Service port: `8765` - Cluster Service URL: `http://issue-core.issue-core.svc.cluster.local:8765` - Tenant Application: `railiance-platform/argocd/applications/issue-core.application.yaml` The `Application` should point at this repo's `k8s/railiance` path and use `CreateNamespace=true` for the `issue-core` namespace. The namespace itself is therefore intentionally not duplicated in this bundle. ## Platform gates The following pieces are owned by railiance-platform before the workload can be fully reconciled: - ArgoCD repository credentials and the project/app-of-apps convention. - The `issue-core` ArgoCD `Application`. - External Secrets Operator and a `ClusterSecretStore` named `openbao`. - OpenBao entries for the issue-core runtime Secret. Until those gates exist, `kubectl kustomize k8s/railiance` can render locally, but the live `ExternalSecret` and `Deployment` are expected to wait. ## Secret contract Kubernetes Secret name: `issue-core-runtime` Current issue-core manifest path, pending railiance-platform confirmation: ```text platform/workloads/issue-core/issue-core/issue-core-runtime ``` Credential route catalog id `issue-core-ingestion-api-key` is owned by railiance-platform/OpenBao and is still marked draft/path TBD in the local ops-warden catalog reviewed 2026-06-18. Confirm the canonical path before provisioning the live Secret. Required properties: - `ISSUE_CORE_API_KEY` - shared ingestion key used by issue-core and activity-core. - `GITEA_BACKEND_TOKEN` - token for creating issues in cluster Gitea. Never write either value to Git, State Hub, workplans, logs, or chat. Record only non-secret evidence such as Secret key count, ExternalSecret readiness, HTTP status codes, and created issue URLs. ## Build and publish Use the published package as the image input. For a reproducible release image, pin the package version to the image tag: ```bash docker build --build-arg ISSUE_CORE_VERSION="==0.2.0" -t gitea.coulomb.social/coulomb/issue-core:0.2.0 . docker push gitea.coulomb.social/coulomb/issue-core:0.2.0 ``` The Coulomb Gitea package is public-pullable for this image, so the workload does not use an `imagePullSecret`. ## Pre-sync validation From the issue-core repo: ```bash kubectl kustomize k8s/railiance ``` The rendered resources should be: - `ExternalSecret/issue-core-runtime` - `ConfigMap/issue-core-backends` - `Deployment/issue-core` - `Service/issue-core` ## Sync verification After railiance-platform syncs the tenant `Application`: ```bash kubectl get application issue-core -n argocd kubectl -n issue-core get externalsecret issue-core-runtime kubectl -n issue-core get secret issue-core-runtime kubectl -n issue-core get deploy,pod,svc ``` Expected non-secret evidence: - ArgoCD Application reports `Synced` and `Healthy`. - `ExternalSecret/issue-core-runtime` reports Ready. - `Secret/issue-core-runtime` exists with two data keys. - `Deployment/issue-core` has one available replica. - `Service/issue-core` exposes port `8765`. Health check from inside the cluster: ```bash kubectl -n issue-core run issue-core-health --rm -i --restart=Never --image=curlimages/curl:8.8.0 -- http://issue-core:8765/healthz ``` ## Ingestion smoke Run the authenticated smoke from a short-lived Job so the API key is mounted from the Kubernetes Secret without printing it: ```bash kubectl -n issue-core delete job issue-core-smoke --ignore-not-found kubectl -n issue-core apply -f - <<'YAML' apiVersion: batch/v1 kind: Job metadata: name: issue-core-smoke spec: ttlSecondsAfterFinished: 600 backoffLimit: 0 template: spec: restartPolicy: Never containers: - name: smoke image: curlimages/curl:8.8.0 env: - name: ISSUE_CORE_API_KEY valueFrom: secretKeyRef: name: issue-core-runtime key: ISSUE_CORE_API_KEY command: ["/bin/sh", "-ceu"] args: - | curl -fsS -X POST "http://issue-core:8765/issues/" -H "Authorization: Bearer ${ISSUE_CORE_API_KEY}" -H "Content-Type: application/json" --data '{"title":"issue-core railiance01 smoke","description":"GitOps smoke created by the issue-core deployment runbook.","target_repo":"coulomb/markitect_project","priority":"low","labels":["smoke","issue-core"],"source_type":"rule","source_id":"issue-core-gitops-smoke","triggering_event_id":"scheduled","activity_definition_id":"issue-core-gitops-smoke"}' YAML kubectl -n issue-core wait --for=condition=complete job/issue-core-smoke --timeout=90s kubectl -n issue-core logs job/issue-core-smoke ``` Acceptance evidence is HTTP 201 plus a response body containing `issue_id`, `backend: "gitea"`, and an `issue_url` for cluster Gitea. Cleanup: ```bash kubectl -n issue-core delete job issue-core-smoke ``` ## Activity-core handoff After issue-core is Ready and the shared `ISSUE_CORE_API_KEY` is available to activity-core from the same approved OpenBao source: - Set `ISSUE_CORE_URL=http://issue-core.issue-core.svc.cluster.local:8765`. - Set `ISSUE_SINK_TYPE=rest`. - Inject the same `ISSUE_CORE_API_KEY` into the activity-core worker. - Keep cron-triggered emissions explicit: `triggering_event_id` may be a stable non-empty scheduler key such as `scheduled`; event-driven emissions should continue to send the event UUID. Verify by running an activity-core emission and confirming that issue-core returns HTTP 201 and creates a Gitea issue.