Problem Statement
Platform admins have no supported way to deploy arbitrary Kubernetes addons — cross-namespace operators, CRDs, Helm charts spanning multiple namespaces — without going through the team-scoped AppProject model. Every existing AppProject is locked to a single team namespace and forbids cluster-scoped resources. There is no escape hatch for platform-level addon management.
Solution
Introduce an apl-addons ArgoCD AppProject and namespace. Platform admins drop ArgoCD Application CRs into env/manifests/namespaces/apl-addons/ in the values repo. The existing addGitOpsApps mechanism automatically creates a gitops-ns-apl-addons ArgoCD Application (App-of-Apps) that syncs those Application CRs into the apl-addons namespace. ArgoCD's "app in any namespace" feature then reconciles them under the fully-unrestricted apl-addons project.
User Stories
- As a platform admin, I want to drop an ArgoCD
Application CR into env/manifests/namespaces/apl-addons/ and have it automatically reconciled by ArgoCD, so that I can deploy addons without manual cluster access.
- As a platform admin, I want the
env/manifests/namespaces/apl-addons/ directory to exist immediately after APL installs or upgrades, so that I do not need to create it manually before I can use the feature.
- As a platform admin, I want Applications in the
apl-addons project to be able to deploy to any namespace on any registered cluster, so that I can install cross-namespace and cluster-scoped addons without restriction.
- As a platform admin, I want Applications in the
apl-addons project to be able to pull from any git repository or Helm registry, so that I am not limited to pre-approved sources when choosing addons.
- As a platform admin, I want Applications in the
apl-addons project to be able to deploy any Kubernetes resource type including cluster-scoped resources, so that I can manage CRDs, ClusterRoles, and StorageClasses as addons.
- As a platform admin, I want the
apl-addons AppProject to be protected from accidental deletion at the API-server level, so that the project cannot be removed even by someone stripping ArgoCD finalizers.
- As a platform admin, I want the
apl-addons namespace to be created automatically when the first Application CR is synced there, so that I do not need to pre-create it.
- As a platform admin, I want the
apl-addons AppProject to be visible and manageable in the ArgoCD UI using my existing platform-admin credentials, so that I do not need separate RBAC configuration.
- As a platform admin, I want Applications I place in
apl-addons to be pruned from the cluster when I remove their manifests from git, so that deleting a file is sufficient to remove the addon.
- As a platform admin, I want to understand that Application CRs in
env/manifests/namespaces/apl-addons/ must set spec.project: apl-addons, so that I know why ArgoCD will reject them if this field is wrong.
- As a platform admin, I want to understand that the
argocd namespace must not be used as a destination, so that I do not accidentally overwrite ArgoCD's own configuration.
- As a security-conscious operator, I want images deployed into the
apl-addons namespace to still be subject to the ORCS registry Kyverno policy, so that the addon escape hatch does not bypass image provenance enforcement.
Implementation Decisions
Bootstrap: env/manifests/namespaces/apl-addons/
ensureManifestDirectories in src/common/utils.ts must be extended to also call ensureDirectoryWithGitkeepAsync for env/manifests/namespaces/apl-addons/. This ensures the directory exists after every APL install or upgrade, which in turn causes addGitOpsApps to automatically create the gitops-ns-apl-addons Application on the next reconciliation cycle.
No changes are needed to addGitOpsApps, calculateGitOpsAppsSyncState, or getArgocdGitopsManifest — the existing namespace-directory scanning logic handles apl-addons as just another namespace directory.
ArgoCD "app in any namespace" wiring
In values/argocd/argocd.gotmpl, add apl-addons to two config sections:
configs.cm: application.namespaces: apl-addons — enables ArgoCD to watch Application CRs in the apl-addons namespace (writes to argocd-cm)
configs.params: application.namespaces: apl-addons — enables the conditional extra verbs on the ArgoCD server ClusterRole in charts/argocd/templates/argocd-server/clusterrole.yaml (lines 52–62 are already gated on this value)
AppProject apl-addons
Add an AppProject resource to values/argocd/argocd-raw.gotmpl. Spec:
sourceRepos: ['*']
sourceNamespaces: ['apl-addons']
destinations: [{namespace: '*', server: '*'}]
clusterResourceWhitelist: [{group: '*', kind: '*'}]
namespaceResourceBlacklist: []
- No ArgoCD finalizer (protected by VAP instead — see below)
The project carries no roles. The platform-admin OIDC group already maps to role:admin globally in argocd.gotmpl policy.csv and therefore has full access.
ValidatingAdmissionPolicy
Add a ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding to values/argocd/argocd-raw.gotmpl that denies DELETE operations on the AppProject named apl-addons in the argocd namespace. This protects the project at the API-server level, which cannot be bypassed by stripping ArgoCD finalizers.
Naming convention deviation
The existing ADR 2026-06-25-manifests-directory.md states that apl--prefixed namespace directories are operator-owned. apl-addons is a deliberate exception: the operator bootstraps it but platform admins own its contents. This deviation and its rationale are recorded in adr/2026-07-07-apl-addons-argocd-project.md.
Unenforced constraints (document only)
- Application CRs must set
spec.project: apl-addons. ArgoCD enforces this with an RBAC error; no operator-level pre-validation is added.
- The
argocd namespace must not be used as a destination. ArgoCD AppProject has no native destination blacklist; this is documented rather than enforced.
Testing Decisions
Good tests assert observable outcomes (directory exists, manifest shape, required app set) without coupling to internal call order or mock counts.
Seam 1: ensureManifestDirectories — src/common/utils.test.ts
Add a test that calls ensureManifestDirectories and asserts that ensureDirectoryWithGitkeepAsync is invoked for env/manifests/namespaces/apl-addons/. Prior art: the ensureTeamGitOpsDirectories tests in the same file use the same dependency-injection pattern.
Seam 2: calculateGitOpsAppsSyncState — src/cmd/apply-as-apps.test.ts
Add a test case where the mocked glob returns apl-addons as one of the namespace directories and asserts that gitops-ns-apl-addons appears in requiredGitOpsApps. No code changes required; this is a documentation test that makes the intent explicit. Prior art: the existing calculateGitOpsAppsSyncState describe block in the same file.
Helm template rendering (argocd-raw.gotmpl, argocd.gotmpl) is not unit-tested in this repo and is validated by deployment.
Out of Scope
- Restricting which destination namespaces the
apl-addons project can target (ArgoCD has no native destination blacklist; this would require a Kyverno admission policy)
- Operator-level validation that Application CRs reference
project: apl-addons
- Excluding the
apl-addons namespace from the ORCS registry Kyverno policy
- Multi-namespace or wildcard
application.namespaces configuration
- Any UI or API surface for platform admins to manage addons outside of git
Further Notes
- The
gitops-ns-apl-addons Application is created automatically by the existing addGitOpsApps reconciliation loop — no new operator code path is introduced for it.
- The parent
gitops-ns-apl-addons Application runs under project: default (the existing behaviour for all gitops-ns-* applications) and syncs Application CRs into the apl-addons namespace. The default project is fully permissive and allows this.
- See
adr/2026-07-07-apl-addons-argocd-project.md for the full record of design decisions and trade-offs.
- See
CONTEXT.md for the canonical glossary of terms used above.
Problem Statement
Platform admins have no supported way to deploy arbitrary Kubernetes addons — cross-namespace operators, CRDs, Helm charts spanning multiple namespaces — without going through the team-scoped AppProject model. Every existing AppProject is locked to a single team namespace and forbids cluster-scoped resources. There is no escape hatch for platform-level addon management.
Solution
Introduce an
apl-addonsArgoCD AppProject and namespace. Platform admins drop ArgoCDApplicationCRs intoenv/manifests/namespaces/apl-addons/in the values repo. The existingaddGitOpsAppsmechanism automatically creates agitops-ns-apl-addonsArgoCD Application (App-of-Apps) that syncs those Application CRs into theapl-addonsnamespace. ArgoCD's "app in any namespace" feature then reconciles them under the fully-unrestrictedapl-addonsproject.User Stories
ApplicationCR intoenv/manifests/namespaces/apl-addons/and have it automatically reconciled by ArgoCD, so that I can deploy addons without manual cluster access.env/manifests/namespaces/apl-addons/directory to exist immediately after APL installs or upgrades, so that I do not need to create it manually before I can use the feature.apl-addonsproject to be able to deploy to any namespace on any registered cluster, so that I can install cross-namespace and cluster-scoped addons without restriction.apl-addonsproject to be able to pull from any git repository or Helm registry, so that I am not limited to pre-approved sources when choosing addons.apl-addonsproject to be able to deploy any Kubernetes resource type including cluster-scoped resources, so that I can manage CRDs, ClusterRoles, and StorageClasses as addons.apl-addonsAppProject to be protected from accidental deletion at the API-server level, so that the project cannot be removed even by someone stripping ArgoCD finalizers.apl-addonsnamespace to be created automatically when the first Application CR is synced there, so that I do not need to pre-create it.apl-addonsAppProject to be visible and manageable in the ArgoCD UI using my existingplatform-admincredentials, so that I do not need separate RBAC configuration.apl-addonsto be pruned from the cluster when I remove their manifests from git, so that deleting a file is sufficient to remove the addon.env/manifests/namespaces/apl-addons/must setspec.project: apl-addons, so that I know why ArgoCD will reject them if this field is wrong.argocdnamespace must not be used as a destination, so that I do not accidentally overwrite ArgoCD's own configuration.apl-addonsnamespace to still be subject to the ORCS registry Kyverno policy, so that the addon escape hatch does not bypass image provenance enforcement.Implementation Decisions
Bootstrap:
env/manifests/namespaces/apl-addons/ensureManifestDirectoriesinsrc/common/utils.tsmust be extended to also callensureDirectoryWithGitkeepAsyncforenv/manifests/namespaces/apl-addons/. This ensures the directory exists after every APL install or upgrade, which in turn causesaddGitOpsAppsto automatically create thegitops-ns-apl-addonsApplication on the next reconciliation cycle.No changes are needed to
addGitOpsApps,calculateGitOpsAppsSyncState, orgetArgocdGitopsManifest— the existing namespace-directory scanning logic handlesapl-addonsas just another namespace directory.ArgoCD "app in any namespace" wiring
In
values/argocd/argocd.gotmpl, addapl-addonsto two config sections:configs.cm:application.namespaces: apl-addons— enables ArgoCD to watch Application CRs in theapl-addonsnamespace (writes toargocd-cm)configs.params:application.namespaces: apl-addons— enables the conditional extra verbs on the ArgoCD server ClusterRole incharts/argocd/templates/argocd-server/clusterrole.yaml(lines 52–62 are already gated on this value)AppProject
apl-addonsAdd an
AppProjectresource tovalues/argocd/argocd-raw.gotmpl. Spec:sourceRepos: ['*']sourceNamespaces: ['apl-addons']destinations: [{namespace: '*', server: '*'}]clusterResourceWhitelist: [{group: '*', kind: '*'}]namespaceResourceBlacklist: []The project carries no roles. The
platform-adminOIDC group already maps torole:adminglobally inargocd.gotmplpolicy.csv and therefore has full access.ValidatingAdmissionPolicy
Add a
ValidatingAdmissionPolicyandValidatingAdmissionPolicyBindingtovalues/argocd/argocd-raw.gotmplthat denies DELETE operations on theAppProjectnamedapl-addonsin theargocdnamespace. This protects the project at the API-server level, which cannot be bypassed by stripping ArgoCD finalizers.Naming convention deviation
The existing ADR
2026-06-25-manifests-directory.mdstates thatapl--prefixed namespace directories are operator-owned.apl-addonsis a deliberate exception: the operator bootstraps it but platform admins own its contents. This deviation and its rationale are recorded inadr/2026-07-07-apl-addons-argocd-project.md.Unenforced constraints (document only)
spec.project: apl-addons. ArgoCD enforces this with an RBAC error; no operator-level pre-validation is added.argocdnamespace must not be used as a destination. ArgoCD AppProject has no native destination blacklist; this is documented rather than enforced.Testing Decisions
Good tests assert observable outcomes (directory exists, manifest shape, required app set) without coupling to internal call order or mock counts.
Seam 1:
ensureManifestDirectories—src/common/utils.test.tsAdd a test that calls
ensureManifestDirectoriesand asserts thatensureDirectoryWithGitkeepAsyncis invoked forenv/manifests/namespaces/apl-addons/. Prior art: theensureTeamGitOpsDirectoriestests in the same file use the same dependency-injection pattern.Seam 2:
calculateGitOpsAppsSyncState—src/cmd/apply-as-apps.test.tsAdd a test case where the mocked glob returns
apl-addonsas one of the namespace directories and asserts thatgitops-ns-apl-addonsappears inrequiredGitOpsApps. No code changes required; this is a documentation test that makes the intent explicit. Prior art: the existingcalculateGitOpsAppsSyncStatedescribe block in the same file.Helm template rendering (
argocd-raw.gotmpl,argocd.gotmpl) is not unit-tested in this repo and is validated by deployment.Out of Scope
apl-addonsproject can target (ArgoCD has no native destination blacklist; this would require a Kyverno admission policy)project: apl-addonsapl-addonsnamespace from the ORCS registry Kyverno policyapplication.namespacesconfigurationFurther Notes
gitops-ns-apl-addonsApplication is created automatically by the existingaddGitOpsAppsreconciliation loop — no new operator code path is introduced for it.gitops-ns-apl-addonsApplication runs underproject: default(the existing behaviour for all gitops-ns-* applications) and syncs Application CRs into theapl-addonsnamespace. Thedefaultproject is fully permissive and allows this.adr/2026-07-07-apl-addons-argocd-project.mdfor the full record of design decisions and trade-offs.CONTEXT.mdfor the canonical glossary of terms used above.