Federated GitLab — Repo Architecture
How the platform splits operational state across GitLab groups: comptech-platform/* for platform-owned repos, divisions/<division>/* for tenant repos, and the strict no-write-across-the-boundary rule between them.
This page is the GitLab-side map of where everything lives. It is for anyone deciding which repo to commit to, where to open an MR, or whether a given access grant is allowed at all. The shape changes rarely; the names of <division> and individual tenant repos change as teams onboard.
What and why
The platform runs on GitLab CE 18.11.1 at the internal LAN endpoint (http://<gitlab-vm>), with the human browser route at gitlab.apps.sub.comptech-lab.com. GitOps automation (Argo CD, Jenkins, runners) always uses the LAN endpoint; the public route is for browser/dashboard access only.
The federated layout (ADR 0015 + ADR 0023) splits state by ownership boundary rather than packaging it all into a single monorepo:
comptech-platform/is the platform team’s tree. OpenShift cluster state, VM platform tools, shared service infrastructure, tenant registry.divisions/is the application teams’ tree. One sub-group per division (sandbox,payments,risk, etc.), and inside each division: an app source repo (<division>-apps-monorepo) and an app GitOps repo (<division>-gitops).
The reason this is two trees, not one super-group:
- A single group makes accidental over-grant easy. Adding a role group at the top level reaches every project under it; a two-tree split puts a visual and policy boundary between platform and tenant state.
openshift-platform-gitops(project ID 12 in the live instance) must be writable by the platform team only. ADR 0015 calls this out explicitly: application teams must not have write access. The two-tree split lets the platform team grant maintainer tocomptech-platform/without bleed-over intodivisions/.- Audit reviews scan eleven
ct-*role groups, not per-project user lists. A flat repo model would force per-repo shares and remove that affordance.
The alternatives considered and rejected in ADR 0023:
| Rejected | Why rejected |
|---|---|
| Single super-group + role groups | Too easy to grant broadly; no visual boundary between platform and tenant. |
Flat repo model under comptech-platform/ | Forces per-repo shares; removes sub-group share affordance. |
| Per-team Maintainer on platform repos | Violates ADR 0015 ownership boundary; breaks the ADR 0018 pull-model enforcement contract. |
| Defer ADR until second division onboards | Issue #68 acceptance requires the model is documented before more teams onboard. |
The group tree, whiteboarded
Yellow boxes are platform-owned repos; blue boxes are tenant-owned. The red dashed edges call out write directions that are explicitly forbidden by the access model.
The full project inventory
| GitLab path | Write owner | Default readers | Status (2026-05-11) |
|---|---|---|---|
comptech-platform/openshift-ops/openshift-platform-gitops | OpenShift/platform team only | platform, security reviewers, auditors | Live (project ID 12) |
comptech-platform/openshift-ops/openshift-cluster-build | OpenShift/platform team only | platform, auditors | Planned |
comptech-platform/infra-ops/vm-platform-ops | Infra/platform tools team | OpenShift/platform read, security reviewers, auditors | Planned |
comptech-platform/platform-services/platform-tools-iac | Infra/platform tools team | platform read | Deferred until split proves useful |
comptech-platform/platform-services/platform-tools-config | Infra/platform tools team | platform read | Deferred until split proves useful |
comptech-platform/tenant-registry/openshift-tenants | OpenShift/platform team | tenant owners by approval | Optional; folder inside openshift-platform-gitops for phase 1 |
divisions/<division>/<division>-apps-monorepo | Division app team | platform/CI/security as needed | Per division onboarding |
divisions/<division>/<division>-gitops | Division app team inside guardrails | OpenShift/platform retains maintainer for emergency rollback | Per division onboarding |
Application teams do not have write access to:
openshift-platform-gitopsopenshift-cluster-buildvm-platform-opsplatform-tools-iac,platform-tools-config,openshift-tenants- any platform runner or admin configuration repo
Drift indicator: an app-team ct-* group appears on openshift-platform-gitops. Treat as a configuration incident, remove the share, record in the access log.
Creation order
Not every repository in the inventory exists immediately. The accepted creation order (ADR 0023) is:
openshift-platform-gitops— live, project ID 12.vm-platform-ops— once ownership and first service adoption are ready.openshift-cluster-build— once cluster-build artifacts are ready to split out of planning docs.platform-tools-iac/platform-tools-config/openshift-tenants— only when their split proves useful; defer otherwise.- Division repositories — one pair (
<division>-apps-monorepo+<division>-gitops) per onboarding event.
This is why the live instance today shows only openshift-platform-gitops, the comptech-platform/{openshift-ops,infra-ops,platform-services,tenant-registry} sub-groups, and divisions/sandbox/ — everything else exists in the access matrix but has not been instantiated yet.
What lives in openshift-platform-gitops
The boundary of this repo matters: it is the single platform team’s defensible namespace and it must not contain application source, VM tool configuration, or routine app release manifests.
openshift-platform-gitops/
clusters/
hub-dc-v6/
hub-dr-v6/
spoke-dc-v6/
spoke-dr-v6/
dev-ocp-01/
uat-ocp-01/
staging-ocp-01/
components/
gitops/
acm/
odf/
ingress/
cert-manager/
external-secrets/
compliance/
logging/
monitoring/
image-registry/
operator-catalogs/
policies/
rbac/
quotas/
networkpolicies/
pod-security/
appprojects/
tenants/
<tenant>/
bootstrap/
root-apps/
appsets/
tenants/<tenant>/ is the platform’s view of a tenant — Namespace, AppProject, ResourceQuota, LimitRange, baseline NetworkPolicy, RoleBinding for the team’s OIDC group, allowed source repos, allowed destination namespaces. It is not the tenant’s release manifests.
What lives in <division>-apps-monorepo
The application source. Example for a hypothetical payments division:
divisions/payments/
payments-apps-monorepo/
openliberty/customer-api/
jboss/payment-service/
nodejs/payment-ui/
springboot/ledger-api/
Build paths (Jenkins or Tekton) clone from this repo, build images, scan, push, and produce CI evidence.
What lives in <division>-gitops
The overlay tree that Argo CD reconciles. Same example:
divisions/payments/
payments-gitops/
apps/
customer-api/
base/
kustomization.yaml
deployment.yaml
service.yaml
route.yaml
overlays/
dev/
kustomization.yaml
stg/
kustomization.yaml
prd/
kustomization.yaml
payment-service/
base/...
overlays/...
The detailed layout rules are in section 04 (gitops-consume) and in app-repo-contract.md (#182). The relevant fact here is the separation: the source lives in <division>-apps-monorepo, the deployment intent lives in <division>-gitops, and the CI step that bridges the two is a digest patch MR.
Some teams may choose to keep both inside <division>-apps-monorepo if they prefer a single repo; the contract supports both shapes, as long as the directory structure under apps/<team>/<app>/ is honoured. Defaulting to two repos is the ADR-recommended shape because it gives a cleaner audit trail (image build vs deploy intent are two MRs in two repos, not one MR in one repo).
Endpoints
| Endpoint | Used by | Notes |
|---|---|---|
http://<gitlab-vm> (LAN) | Argo CD, Jenkins, GitLab Runner, platform tooling, API clients | Internal-only; all automation. |
https://gitlab.apps.sub.comptech-lab.com | Human browser | Public route, dashboards and MR review. |
The boundary is enforced by access control on the public route’s automation use: any Argo CD source URL, Jenkins SCM URL, or runner config that points at the public route is drift and must be migrated to the LAN endpoint.
Internal-only specifics (LAN IP, runner tokens, PAT custody locations) are kept in
opp-full-plat/connection-details/gitlab-operator-guide.md.
Failure modes and gotchas
| Symptom | Root cause | Fix | Prevention |
|---|---|---|---|
App team can push to openshift-platform-gitops | A ct-<division>-* group was added to the project | Remove the share from project Settings -> Members; record in access log. | Apply initial shares from a single template; review group shares monthly. |
| Argo CD source URL is the public route | Automation was configured at console-tour time | Edit the Argo Application source URL to the LAN endpoint; restart openshift-gitops-repo-server. | Always copy clone URLs from the LAN endpoint in the operator guide. |
git ls-remote from a runner returns 401 against the LAN endpoint | Runner’s GitLab token is project-scoped to a different project | Re-register the runner with a project-scoped or group-scoped token aligned with the matrix. | Document each runner’s allowed scope in gitlab-operator-guide.md. |
| Public-route automation breaks behind a captive portal | Egress routing assumed direct LAN | Move the automation to the LAN endpoint; do not punch firewall holes for the public route. | Never permit automation on the public route. |
| Migrating a project from old path leaves stale Argo URLs | Project ID stays the same but path-with-namespace changes | Update repoURL in every Argo Application (live example: ID 12 was moved from gitops/platform-gitops to comptech-platform/openshift-ops/openshift-platform-gitops). | Prefer project-ID-based lookups in automation where possible. |
Daily checks
# GitLab readiness (any LAN client)
GITLAB_URL=http://<gitlab-vm>
curl -fsS "$GITLAB_URL/-/readiness"
curl -fsS "$GITLAB_URL/-/liveness"
# Platform repo reachable from the LAN endpoint?
git ls-remote \
http://<gitlab-vm>/comptech-platform/openshift-ops/openshift-platform-gitops.git \
main
# Hub Argo CD sees the platform repo?
oc --kubeconfig "$K_HUB" -n openshift-gitops get app hub-dc-v6-bootstrap -o wide
# Expected: Synced Healthy
The first command answers “is GitLab healthy”, the second answers “does the federated tree still resolve”, and the third answers “does the platform actually see it”.
References
connection-details/gitlab-operator-guide.md— operator handoff (#128)adr/0015-federated-gitops-repo-architecture.md— parent architectureadr/0023-federated-gitlab-group-repo-ownership.md— formalised ownership modelplans/federated-gitops-gitlab-access-matrix.md— access matrix this ADR locks inplans/federated-gitops-gitlab-implementation-checklist.md— execution checklist- GitHub issue #68 —
architecture: define federated GitLab group and repo ownership model(closed by ADR 0023) - GitHub issue #83 — FG-1 implementation gate (open)
- GitHub issue #128 —
GITLAB-HANDOFF1(closed)