diff --git a/.gitignore b/.gitignore index b98831e..35a016d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,9 @@ helm/*.yaml !helm/*-networkpolicies.yaml !helm/*-databases.yaml +# ArgoCD repository credentials — encrypt locally, never commit +argocd/repositories/*.repository.sops.yaml +!argocd/repositories/*.repository.sops.yaml.template + # Kubeconfig *.kubeconfig diff --git a/argocd/applications/README.md b/argocd/applications/README.md new file mode 100644 index 0000000..87d57e4 --- /dev/null +++ b/argocd/applications/README.md @@ -0,0 +1,18 @@ +# Railiance ArgoCD Tenant Applications + +This directory is synced by the `railiance-apps-root` ArgoCD Application. + +Tenant teams author a thin ArgoCD `Application` manifest against the contract +in `docs/argocd-gitops.md`. Platform review merges that manifest here after +checking namespace, repository, sync policy, and secret-delivery shape. + +Workload manifests stay in the owning tenant repo. The default source path for +tenant workloads is: + +```text +k8s/railiance/ +``` + +Do not commit Kubernetes Secret values, ArgoCD repository credentials, OpenBao +tokens, deploy keys, or API keys here. + diff --git a/argocd/applications/issue-core.application.yaml b/argocd/applications/issue-core.application.yaml new file mode 100644 index 0000000..41f5f5c --- /dev/null +++ b/argocd/applications/issue-core.application.yaml @@ -0,0 +1,27 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: issue-core + namespace: argocd + labels: + app.kubernetes.io/part-of: railiance-gitops + railiance.io/domain: issue-core + annotations: + argocd.argoproj.io/sync-wave: "10" +spec: + project: railiance-tenants + source: + repoURL: https://gitea.coulomb.social/coulomb/issue-core.git + targetRevision: main + path: k8s/railiance + destination: + server: https://kubernetes.default.svc + namespace: issue-core + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ApplyOutOfSyncOnly=true + - PruneLast=true diff --git a/argocd/bootstrap/00-railiance-bootstrap-project.yaml b/argocd/bootstrap/00-railiance-bootstrap-project.yaml new file mode 100644 index 0000000..4e04465 --- /dev/null +++ b/argocd/bootstrap/00-railiance-bootstrap-project.yaml @@ -0,0 +1,22 @@ +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: railiance-bootstrap + namespace: argocd + labels: + app.kubernetes.io/part-of: railiance-gitops + railiance-platform/component: gitops +spec: + description: Platform-owned ArgoCD bootstrap project for Railiance app-of-apps. + sourceRepos: + - https://gitea.coulomb.social/coulomb/railiance-platform.git + destinations: + - server: https://kubernetes.default.svc + namespace: argocd + clusterResourceWhitelist: [] + namespaceResourceWhitelist: + - group: argoproj.io + kind: Application + orphanedResources: + warn: true + diff --git a/argocd/bootstrap/01-railiance-tenants-project.yaml b/argocd/bootstrap/01-railiance-tenants-project.yaml new file mode 100644 index 0000000..02c1a86 --- /dev/null +++ b/argocd/bootstrap/01-railiance-tenants-project.yaml @@ -0,0 +1,52 @@ +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: railiance-tenants + namespace: argocd + labels: + app.kubernetes.io/part-of: railiance-gitops + railiance-platform/component: gitops +spec: + description: Guardrails for Railiance tenant applications deployed by ArgoCD. + sourceRepos: + - https://gitea.coulomb.social/coulomb/*.git + destinations: + - server: https://kubernetes.default.svc + namespace: "*" + clusterResourceWhitelist: + - group: "" + kind: Namespace + namespaceResourceWhitelist: + - group: "" + kind: ConfigMap + - group: "" + kind: PersistentVolumeClaim + - group: "" + kind: Secret + - group: "" + kind: Service + - group: "" + kind: ServiceAccount + - group: apps + kind: Deployment + - group: apps + kind: StatefulSet + - group: autoscaling + kind: HorizontalPodAutoscaler + - group: batch + kind: CronJob + - group: batch + kind: Job + - group: external-secrets.io + kind: ExternalSecret + - group: networking.k8s.io + kind: Ingress + - group: networking.k8s.io + kind: NetworkPolicy + - group: traefik.io + kind: IngressRoute + - group: traefik.io + kind: Middleware + orphanedResources: + warn: true + diff --git a/argocd/bootstrap/10-railiance-apps-root.application.yaml b/argocd/bootstrap/10-railiance-apps-root.application.yaml new file mode 100644 index 0000000..98f24a2 --- /dev/null +++ b/argocd/bootstrap/10-railiance-apps-root.application.yaml @@ -0,0 +1,26 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: railiance-apps-root + namespace: argocd + labels: + app.kubernetes.io/part-of: railiance-gitops + railiance-platform/component: gitops +spec: + project: railiance-bootstrap + source: + repoURL: https://gitea.coulomb.social/coulomb/railiance-platform.git + targetRevision: main + path: argocd/applications + destination: + server: https://kubernetes.default.svc + namespace: argocd + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=false + - ApplyOutOfSyncOnly=true + - PruneLast=true + diff --git a/argocd/bootstrap/kustomization.yaml b/argocd/bootstrap/kustomization.yaml new file mode 100644 index 0000000..03db897 --- /dev/null +++ b/argocd/bootstrap/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - 00-railiance-bootstrap-project.yaml + - 01-railiance-tenants-project.yaml + - 10-railiance-apps-root.application.yaml + diff --git a/argocd/repositories/README.md b/argocd/repositories/README.md new file mode 100644 index 0000000..4ed5513 --- /dev/null +++ b/argocd/repositories/README.md @@ -0,0 +1,23 @@ +# ArgoCD Repository Registration + +ArgoCD discovers Git repositories from Kubernetes Secrets in the `argocd` +namespace with `argocd.argoproj.io/secret-type: repository`. + +Use the templates in this directory to create SOPS-encrypted, non-plaintext +repository Secret files. Credentials must be sourced from the approved +operator/OpenBao path and must never be committed in plaintext. + +Recommended OpenBao path: + +```text +platform/operators/argocd/repositories/ +``` + +After creating an encrypted file such as +`argocd/repositories/railiance-platform.repository.sops.yaml`, apply it with: + +```bash +ARGOCD_REPOSITORY_SECRET=argocd/repositories/railiance-platform.repository.sops.yaml \ + make argocd-repo-apply +``` + diff --git a/argocd/repositories/issue-core.repository.sops.yaml.template b/argocd/repositories/issue-core.repository.sops.yaml.template new file mode 100644 index 0000000..239eeaa --- /dev/null +++ b/argocd/repositories/issue-core.repository.sops.yaml.template @@ -0,0 +1,21 @@ +# Copy to issue-core.repository.sops.yaml, fill from the approved +# operator/OpenBao path, then encrypt with: +# sops -e -i argocd/repositories/issue-core.repository.sops.yaml +# +# Do not commit plaintext credentials. +apiVersion: v1 +kind: Secret +metadata: + name: issue-core-repository + namespace: argocd + labels: + argocd.argoproj.io/secret-type: repository + app.kubernetes.io/part-of: railiance-gitops + railiance-platform/component: gitops +stringData: + type: git + project: railiance-tenants + url: https://gitea.coulomb.social/coulomb/issue-core.git + username: CHANGE_ME + password: CHANGE_ME + diff --git a/argocd/repositories/railiance-platform.repository.sops.yaml.template b/argocd/repositories/railiance-platform.repository.sops.yaml.template new file mode 100644 index 0000000..a7876af --- /dev/null +++ b/argocd/repositories/railiance-platform.repository.sops.yaml.template @@ -0,0 +1,21 @@ +# Copy to railiance-platform.repository.sops.yaml, fill from the approved +# operator/OpenBao path, then encrypt with: +# sops -e -i argocd/repositories/railiance-platform.repository.sops.yaml +# +# Do not commit plaintext credentials. +apiVersion: v1 +kind: Secret +metadata: + name: railiance-platform-repository + namespace: argocd + labels: + argocd.argoproj.io/secret-type: repository + app.kubernetes.io/part-of: railiance-gitops + railiance-platform/component: gitops +stringData: + type: git + project: railiance-bootstrap + url: https://gitea.coulomb.social/coulomb/railiance-platform.git + username: CHANGE_ME + password: CHANGE_ME + diff --git a/docs/argocd-gitops.md b/docs/argocd-gitops.md new file mode 100644 index 0000000..5131f89 --- /dev/null +++ b/docs/argocd-gitops.md @@ -0,0 +1,176 @@ +# ArgoCD GitOps Contract + +Railiance ArgoCD is the cluster sync engine. This repo owns only the shared +platform contract around GitOps trust, guardrails, and OpenBao-backed secret +delivery. Application workload manifests remain in the owning application +repos. + +## Ownership Boundary + +`railiance-platform` owns: + +- ArgoCD AppProject guardrails for Railiance tenant workloads. +- The root app-of-apps entrypoint. +- Repository credential registration templates. +- Secret delivery conventions through OpenBao and External Secrets Operator. + +Tenant repos own: + +- Container images. +- Workload manifests under `k8s/railiance/`. +- The proposed ArgoCD `Application` manifest for platform review. +- Application config that contains no secret values. + +Cluster/runtime ownership remains outside this repo: installing or upgrading +ArgoCD itself belongs with the cluster layer. + +## Bootstrap Layout + +```text +argocd/bootstrap/ + 00-railiance-bootstrap-project.yaml + 01-railiance-tenants-project.yaml + 10-railiance-apps-root.application.yaml + +argocd/applications/ + *.application.yaml + +argocd/repositories/ + *.repository.sops.yaml +``` + +The bootstrap is applied once by an operator. If the Git source is private, +apply the encrypted `railiance-platform` repository Secret first so the root +Application can sync this repo: + +```bash +ARGOCD_REPOSITORY_SECRET=argocd/repositories/railiance-platform.repository.sops.yaml \ + make argocd-repo-apply +make argocd-bootstrap-dry-run +make argocd-bootstrap-deploy +make argocd-status +``` + +After that, `railiance-apps-root` syncs tenant Application manifests from +`argocd/applications/`. + +## Repository Registration + +Every Git source repo used by ArgoCD must be registered in the `argocd` +namespace with an ArgoCD repository Secret. Use one read-only deploy token or +deploy key per repo unless an operator approves a narrower shared credential +model. + +Repository credentials are operator credentials, not workload secrets. Store +their source material in OpenBao under: + +```text +platform/operators/argocd/repositories/ +``` + +Create an encrypted repository Secret from the matching template: + +```bash +cp argocd/repositories/issue-core.repository.sops.yaml.template \ + argocd/repositories/issue-core.repository.sops.yaml +sops -e -i argocd/repositories/issue-core.repository.sops.yaml +``` + +Apply only encrypted files: + +```bash +ARGOCD_REPOSITORY_SECRET=argocd/repositories/issue-core.repository.sops.yaml \ + make argocd-repo-apply +``` + +Do not commit plaintext deploy tokens, passwords, SSH private keys, OpenBao +tokens, or ArgoCD API tokens. + +## Tenant Application Contract + +Tenant Applications are thin routing manifests reviewed into +`argocd/applications/`. The workload source remains in the tenant repo, +normally: + +```text +k8s/railiance/ +``` + +Default Application shape: + +```yaml +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: example-service + namespace: argocd +spec: + project: railiance-tenants + source: + repoURL: https://gitea.coulomb.social/coulomb/example-service.git + targetRevision: main + path: k8s/railiance + destination: + server: https://kubernetes.default.svc + namespace: example-service + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ApplyOutOfSyncOnly=true + - PruneLast=true +``` + +Use sync waves only for real dependency ordering. Secret delivery resources +should sync before Deployments that consume them. + +## Secret Delivery + +OpenBao is the canonical runtime secret custody authority. + +Default pattern: + +1. Store workload secret material in OpenBao. +2. Use External Secrets Operator to materialize a Kubernetes Secret in the + workload namespace. +3. Reference that Kubernetes Secret from the Deployment, Job, or CronJob. + +Path convention: + +```text +platform/workloads/// +``` + +Use CSI-mounted files only for workloads that need file references, sharper +mount boundaries, or refresh behavior that should not rewrite application +manifests. Do not use the OpenBao injector in the current deployment. + +For `issue-core`, the expected custody shape is: + +```text +platform/workloads/issue-core/issue-core/issue-core-runtime +``` + +with properties such as: + +```text +ISSUE_CORE_API_KEY +GITEA_BACKEND_TOKEN +``` + +The exact ExternalSecret manifest belongs with `issue-core` workload +manifests, because it is part of that service's runtime deployment. + +## AppProject Guardrails + +`railiance-bootstrap` allows the root app to manage ArgoCD `Application` +objects in the `argocd` namespace. + +`railiance-tenants` allows ordinary namespaced workload resources and namespace +creation. It does not allow tenant Applications to create CRDs, ClusterRoles, +ClusterRoleBindings, or other cluster-admin resources. + +If a tenant needs a cluster-scoped platform resource, create a new +platform-owned workplan instead of broadening the tenant project by default. diff --git a/docs/openbao.md b/docs/openbao.md index d9b2c58..162b976 100644 --- a/docs/openbao.md +++ b/docs/openbao.md @@ -374,7 +374,8 @@ platform/operators/ platform/operators/inter-hub/ ``` -Workload delivery choice: +Workload delivery choice (see also `docs/argocd-gitops.md` for the GitOps +tenant contract): - Prefer External Secrets Operator for values that should become Kubernetes Secrets consumed by ordinary Helm charts. diff --git a/workplans/RAILIANCE-WP-0004-argocd-gitops-bootstrap.md b/workplans/RAILIANCE-WP-0004-argocd-gitops-bootstrap.md new file mode 100644 index 0000000..0090bea --- /dev/null +++ b/workplans/RAILIANCE-WP-0004-argocd-gitops-bootstrap.md @@ -0,0 +1,256 @@ +--- +id: RAILIANCE-WP-0004 +type: workplan +title: "Establish ArgoCD GitOps bootstrap contract" +domain: railiance +repo: railiance-platform +status: blocked +owner: codex +topic_slug: railiance +planning_priority: high +planning_order: 4 +created: "2026-06-19" +updated: "2026-06-19" +--- + +# RAILIANCE-WP-0004 - Establish ArgoCD GitOps Bootstrap Contract + +## Goal + +Establish the minimal platform-owned ArgoCD GitOps contract needed for +Railiance application teams to deploy through the already-installed ArgoCD +instance on `railiance01`. + +This work responds to the `issue-core` dependency message from 2026-06-18: +ArgoCD is installed and healthy on `railiance01`, but unused. `issue-core` +will be the first tenant Application and needs platform decisions before it +can author its workload deployment. + +## Intent Alignment + +`INTENT.md` defines this repo as the shared platform-services layer: +stateful services, secret custody, stable interfaces, and recoverable +operational contracts. + +ArgoCD itself is not an application, database, or secret store. The work in +this repo is therefore intentionally limited to the platform contract around +GitOps: + +- repository trust and credential registration for ArgoCD; +- AppProject guardrails that keep tenant syncs inside expected boundaries; +- a root app-of-apps entrypoint that provides a stable onboarding surface; +- the OpenBao-backed runtime secret delivery convention tenants must use. + +Application workloads, container images, per-service manifests, and business +logic remain owned by the tenant repos. + +## Scope + +In scope: + +- Define the bootstrap manifests for ArgoCD AppProjects and the root + app-of-apps Application. +- Define how Git source repositories are registered without committing + credentials. +- Define where tenant Application manifests are placed and how they point + back to tenant-owned workload manifests. +- Confirm the runtime secret delivery pattern: OpenBao custody delivered to + Kubernetes via External Secrets Operator by default; CSI-mounted files only + when a workload requires file references; OpenBao injector remains disabled. +- Provide an `issue-core` pilot Application example so that repo can author + its final manifest against a concrete contract. + +Out of scope: + +- Installing or upgrading ArgoCD itself; that is cluster/runtime ownership. +- Moving S5 application workload manifests into this repo. +- Storing ArgoCD repository credentials, API tokens, or application secrets in + Git, workplans, State Hub, or chat. +- Applying live manifests that require operator-owned credentials. + +## Decisions + +### D-01 - Bootstrap Layout + +Use this repo only for the platform-owned GitOps bootstrap: + +```text +argocd/bootstrap/ AppProjects and root app-of-apps Application +argocd/applications/ thin tenant Application manifests reviewed by platform +argocd/repositories/ SOPS templates for ArgoCD repository Secret objects +docs/argocd-gitops.md GitOps contract and onboarding guidance +``` + +The root Application syncs `argocd/applications/` from this repo. Tenant +Application manifests in that directory point to workload manifests in each +tenant repo, normally `k8s/railiance/`. + +### D-02 - AppProject Model + +Create two AppProjects: + +- `railiance-bootstrap` only allows the root app to manage ArgoCD + `Application` objects in the `argocd` namespace. +- `railiance-tenants` allows tenant Applications to sync ordinary namespaced + workload resources into their own namespaces, plus namespace creation. It + does not grant CRD, ClusterRole, ClusterRoleBinding, or arbitrary + cluster-admin authority. + +### D-03 - Sync Policy + +Default tenant Applications use automated sync with prune and self-heal +enabled after platform review. Recommended sync options are: + +```yaml +syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true + - ApplyOutOfSyncOnly=true + - PruneLast=true +``` + +Sync waves are reserved for dependency ordering. Platform services and secret +delivery resources should sync before workloads that consume them. + +### D-04 - Secret Delivery + +OpenBao remains the canonical runtime secret custody service. For ordinary +Kubernetes workloads, use External Secrets Operator to materialize OpenBao +values as Kubernetes Secrets. Do not use the OpenBao injector in the current +deployment. + +Runtime path convention: + +```text +platform/workloads/// +``` + +ArgoCD repository credentials are operator credentials, not workload secrets, +and should live under: + +```text +platform/operators/argocd/repositories/ +``` + +## Current Evidence + +- State Hub inbox message `d7a18ff9-e6c6-4e44-a39e-78369e530dfc` reports + ArgoCD is installed and healthy on `railiance01`, with zero Applications, + zero ApplicationSets, zero registered repositories, and only the stock + `default` AppProject. +- `INTENT.md` and `SCOPE.md` keep this repo focused on shared platform + services and secret custody. This work therefore creates a bootstrap + contract and secret-delivery convention, not app workload ownership. +- `docs/openbao.md` already states the preferred delivery pattern: + External Secrets Operator for values that become Kubernetes Secrets, CSI for + file-reference workloads, and no OpenBao injector in the current deployment. + +## Target State + +- `argocd/bootstrap/` contains the two AppProjects and root app-of-apps + Application. +- `argocd/applications/` documents the tenant Application contract and includes + an `issue-core` example manifest. +- `argocd/repositories/` contains non-secret SOPS templates for ArgoCD + repository registration. +- `docs/argocd-gitops.md` answers the four questions raised by `issue-core`: + repository registration, source layout, sync policy, and secret delivery. +- Make targets exist for dry-run, deploy, status, and SOPS-backed repository + secret application. +- `issue-core` can author its final Application and workload manifests against + this contract without waiting for more platform design. + +## Tasks + +### T01 - Review intent and scope boundary + +```task +id: RAILIANCE-WP-0004-T01 +status: done +priority: high +``` + +Review `INTENT.md`, `SCOPE.md`, existing OpenBao delivery docs, and the +`issue-core` inbox request. Capture the boundary that ArgoCD bootstrap belongs +here only as a platform trust and secret-delivery contract. + +### T02 - Add ArgoCD bootstrap manifests + +```task +id: RAILIANCE-WP-0004-T02 +status: done +priority: high +``` + +Add AppProject manifests and the root app-of-apps Application under +`argocd/bootstrap/`. + +Done when the manifests can be rendered by `kubectl apply -k` and avoid secret +material. + +### T03 - Define tenant onboarding and repository registration + +```task +id: RAILIANCE-WP-0004-T03 +status: done +priority: high +``` + +Add documentation and templates for tenant Applications, per-repo ArgoCD +repository Secret registration, and the `issue-core` pilot example. + +### T04 - Confirm OpenBao-backed secret delivery + +```task +id: RAILIANCE-WP-0004-T04 +status: done +priority: high +``` + +Document that OpenBao remains the runtime custody authority, External Secrets +Operator is the default Kubernetes delivery mechanism, CSI is reserved for +file-reference workloads, and the OpenBao injector remains disabled. + +### T05 - Operator live bootstrap + +```task +id: RAILIANCE-WP-0004-T05 +status: blocked +priority: high +``` + +Apply the bootstrap and repository credentials to live ArgoCD after these repo +changes are merged to the Git source ArgoCD reads and, if the source repo is +private, after an operator provides or materializes read-only repository +credentials through the approved OpenBao/operator path. + +Blocked until the bootstrap files are available on the remote branch ArgoCD +syncs and operator-owned repository credentials exist outside Git when needed. +Do not paste credentials into the workplan, State Hub, or chat. + +Expected command sequence after credentials are available: + +```bash +ARGOCD_REPOSITORY_SECRET=argocd/repositories/railiance-platform.repository.sops.yaml \ + make argocd-repo-apply +make argocd-bootstrap-dry-run +make argocd-bootstrap-deploy +make argocd-status +``` + +### T06 - Notify first tenant + +```task +id: RAILIANCE-WP-0004-T06 +status: done +priority: medium +``` + +Reply to `issue-core` with the GitOps contract pointer and confirm that it owns +the final `issue-core` Application proposal and workload manifests. Include the +OpenBao path convention for `ISSUE_CORE_API_KEY` and the Gitea backend token. + +State Hub reply: `56df276d-77d0-427f-92a5-a99cacc1290f`.