---
id: RAILIANCE-WP-0004
type: workplan
title: "Establish ArgoCD GitOps bootstrap contract"
domain: financials
repo: railiance-platform
status: finished
owner: codex
topic_slug: railiance
planning_priority: high
planning_order: 4
created: "2026-06-19"
updated: "2026-06-25"
state_hub_workstream_id: "e57e487b-8557-439d-8093-0457c73ede93"
---

> **Terminology note:** Historical text in this archived workplan may use the legacy term "workstream". The fleet term is **workplan** (`canon/standards/workplan-terminology-fleet_v0.1.md`).

# 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 for workload credential custody:

```text
platform/workloads/<tenant-or-org>/<workload>/<secret-purpose>
```

Kubernetes namespace and service-account bounds belong in the auth role or
External Secrets binding unless the namespace is itself the approved workload
identity.

ArgoCD repository credentials are operator credentials, not workload secrets,
and should live under:

```text
platform/operators/argocd/repositories/<repo-name>
```

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


## Follow-up Progress (2026-06-25)

- Added a platform-owned `railiance-platform-addons` AppProject for
  cluster-scoped add-ons.
- Added the `external-secrets` ArgoCD Application for External Secrets
  Operator and the `openbao-secretstore` Application for
  `ClusterSecretStore/openbao`.
- Added the least-privilege OpenBao policy and Kubernetes auth role helper for
  the issue-core ESO pilot. The role binds only the
  `external-secrets/external-secrets` service account and reads only
  `platform/workloads/issue-core/issue-core/*`.
- Limited the initial `ClusterSecretStore/openbao` to the `issue-core`
  namespace; broaden only through a later platform review.

## 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
state_hub_task_id: "7cb56ad6-5435-41af-b416-e68fe661b7a0"
```

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
state_hub_task_id: "68f7ef19-686d-4d16-bf75-ffcbba158023"
```

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
state_hub_task_id: "e6dc9176-af33-4216-9871-a61ad7e69943"
```

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
state_hub_task_id: "d859e4ef-d8d1-4403-8225-839925f8bedf"
```

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: done
priority: high
state_hub_task_id: "981f46c0-8dd7-4111-9a4f-2ca58ddb0664"
```

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.

Applied 2026-06-19 on the live ArgoCD cluster (`92.205.130.254`, default
`~/.kube/config`). `make argocd-bootstrap-dry-run` and
`make argocd-bootstrap-deploy` succeeded. Repository registration was skipped
because `railiance-platform` and `issue-core` Gitea repos are currently public.

Post-bootstrap status:

- `railiance-apps-root`: Synced / Healthy
- `issue-core`: OutOfSync / Missing — sync fails because
  `external-secrets.io/ExternalSecret` CRD is not installed on the cluster

Do not paste credentials into the workplan, State Hub, or chat.

### T06 - Notify first tenant

```task
id: RAILIANCE-WP-0004-T06
status: done
priority: medium
state_hub_task_id: "73bdda1d-8e25-48d2-ab92-b203c5050d45"
```

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