Registering a source
repoUrl must include the project path — a bare host is rejected at create time, because the path is what Celum turns into the GitLab project identifier. branch is the single branch every read and write targets; there is no per-operation branch override.
The token is never returned. Reads carry
tokenMasked only, and the create and update responses blank the token field entirely. Supply a token on create, and on update only when you are rotating it — an update with an empty token leaves the stored one untouched.POST /api/repositories/branchestakes{ "repoUrl", "token" }— an unsaved pair — and returns the branch list, so you can pick a branch before committing to the credentials. It answers502with the upstream message when the repository or token is wrong. Action:repository:ListBranches.GET /api/repositories/<id>/checkre-runs that same branch listing against a saved repository and returns{ "ok": true }, or{ "ok": false, "error": "..." }with the reason. It is a reachability and credential probe, and deliberately returns200either way so the UI can render the failure inline. Action:repository:Check.
PUT /api/repositories/<id>/default, action repository:SetDefault). The default is what cluster operations use when nothing more specific is attached; a supervisor with its own repository attached uses that one instead.
What gets committed
Every cluster owns a directory in the repository, and its apps are files inside it:apps/kustomization.yaml is the source of truth for “what is declared”. GET /api/gitlab/clusters/<cluster>/apps reads it, strips the .yaml suffix from each entry to recover app names, and enriches each with chart metadata from the app catalog:
name only. GET /api/gitlab/clusters lists the cluster directories, and GET /api/gitlab/clusters/<cluster>/values returns the raw cluster-values.yaml as YAML.
Adding an app is a commit
POST /api/gitlab/clusters/<cluster>/apps takes { "appName", "namespace", "supervisor" } and writes several files in one commit through GitLab’s Commits API (POST /api/v4/projects/:id/repository/commits), each file an action of type create, update or delete. One request, one commit, one SHA — never a partially applied change:
1
The ArgoCD Application
apps/<app>.yaml — a multi-source Application. Source one is the chart (repo, name, targetRevision), source two is the GitOps repository itself as a ref named values, so the chart’s valueFiles can point at $values/clusters/<cluster>/apps/values/<app>.yaml. A definition with extraResources gets a third source pointing at the resources directory. Sync policy is automated with prune, self-heal, and CreateNamespace=true.2
The rendered values
apps/values/<app>.yaml — the app definition’s values template after Go-template substitution.3
The updated list
apps/kustomization.yaml, with the new <app>.yaml appended.4
The project's source list
project.yaml gains the chart’s Helm repo under spec.sourceRepos, if it is not already there — otherwise ArgoCD’s AppProject would refuse the new source.kustomization.yaml returns 409, and a cluster whose existing app files carry no workload-repository reference returns 422 — that reference is how Celum discovers which repository the Application’s $values source should point at, and it can only be recovered from a file that already exists.
Tanzu packages go through POST /api/gitlab/clusters/<cluster>/apps/package (action gitlab-app:AddPackage), which commits the definition’s package files under apps/packages/<app>/, generates that directory’s kustomization.yaml, and points a directory-sourced Application at it.
Removal (DELETE /api/gitlab/clusters/<cluster>/apps/<app>) is the same mechanism in reverse: delete apps/<app>.yaml, delete apps/values/<app>.yaml, rewrite kustomization.yaml without the entry — one commit, one SHA. An app that is not listed returns 404, so a repeat delete is safe.
Declared versus running
The commit is a declaration, not an installation. Nothing is on the cluster until ArgoCD syncs the Application. That is why two lists exist:
Read the difference in both directions. Declared but not running means ArgoCD has not synced — check the Application’s status on the instance that owns it, and confirm the AppProject allows the chart’s repo. Running but not declared means the release was installed outside GitOps, usually a direct Helm install from the cluster detail page; a future rebuild of the cluster from Git would not bring it back.
Celum never reconciles one list into the other. It reports both and leaves the decision to you, because the right resolution differs — sometimes the commit is wrong, sometimes the running state is.
Permissions
Because the write actions are scoped per cluster, you can grant a team the ability to change their own cluster’s app set without granting it across the fleet.
Related
App catalog
The definitions that become these files, and the values-template caveat when adding to an existing cluster.
Day-2 operations
The running side of the contrast — Helm releases read from the tenant itself.
Cluster templates
How a whole cluster’s initial file set is generated and committed the same way.
ArgoCD
The instances that turn these commits into workloads.
Celum AI reads both sides of the contrast:
list_repositories for the registered Git sources, list_cluster_gitops_apps for what a cluster’s repository declares, and list_cluster_helm_releases for what it actually runs.