All source Secret manifests under k8s/ are SOPS-encrypted. vcap v1 was retired on 2026-10-27.
The retired v2 workloads were removed on 2026-10-23. Their namespaces, Secrets, PVCs, and
PVs remain for recovery.
Encryption is not rotation. A value that was ever committed is compromised. Rotation makes it dead.
See runbooks/rotate-secrets.md.
Secret manifest remains under k8s/.helm-secrets is not installed. Kubernetes Secrets use the ksops generator.sops exec-env.community.sops vars plugin.The old v2 Secret directories k8s/apps/vcap/secrets/ do not exist. Do not add them back.
.sops.yaml at the repository root defines two age recipients. Either key decrypts.
age-ops: the owner workstation key. Keep an offline copy in the owner password manager.age-cluster: installed as the argocd/sops-age Secret. ArgoCD repo-server uses it at runtime.Losing both keys loses every git-held Secret. etcd snapshots of live Secret objects are the last
resort. See backups.md.
Rules:
data and stringData. kind, metadata, and annotations stay
readable and diffable.k8s/platform/argocd/values.yaml sets
kustomize.buildOptions: "--enable-alpha-plugins --enable-exec".viaductoss/ksops:v4.5.1 init container.ksops and a compatible kustomize into /custom-tools.sops-age Secret mounts at the default age key path.secrets.sops.yaml under files:.Test a generator locally:
kustomize build --enable-alpha-plugins --enable-exec k8s/apps/mochi
One app keeps its encrypted Secrets outside this repository. upayanmazumder/gitorgasm holds
deploy/argocd/secrets.sops.yaml, encrypted to the same two recipients. The repo-server decrypts
it with the same key. ArgoCD reads that repository with creds-github-upayanmazumder.
CI does not decrypt. scripts/check-secrets.sh only checks sops: and age: metadata. Test the
plaintext build locally before a commit.
On a new cluster, ArgoCD cannot read this private repository until its git credential exists. The same is true for every other repository credential.
sops-age by hand.sops -d k8s/platform/argocd/secret/secrets.sops.yaml | kubectl apply -f -
The old argocd-repo-vps Secret does not exist. Do not add it. The cluster repository uses
git-creds.
| Secret | In git | Used by |
|---|---|---|
argocd-secret |
k8s/platform/argocd/secret/secrets.sops.yaml |
ArgoCD server |
git-creds |
same file | ArgoCD access to this repository |
creds-github-upayanmazumder |
same file | GitHub access under upayanmazumder/ |
repo-vcap-backend |
same file | ArgoCD access to the unused v2 staging backend repository |
repo-vcap-frontend |
same file | ArgoCD access to the unused v2 staging frontend repository |
repo-creds-devlabs |
k8s/platform/argocd/secret/repo-devlabs-secrets.sops.yaml |
ArgoCD URL-prefix credential for upayanmazumder-DevLabs |
sops-age |
never | ArgoCD repo-server |
ghcr-creds |
k8s/platform/argocd-image-updater/secrets.sops.yaml |
image-updater registry reads |
git-creds-updater |
same file | image-updater git write-back over SSH |
wildcard-upayan-dev-tls |
k8s/platform/traefik/secrets.sops.yaml |
Traefik TLSStore default |
hcloud |
k8s/hetzner-csi/secrets.sops.yaml |
hcloud CSI, key token |
grafana-admin-credentials |
k8s/monitoring/secrets.sops.yaml |
Grafana admin |
grafana-dev-credentials |
same file | retained dev viewer |
grafana-staging-credentials |
same file | retained staging viewer |
grafana-kirro-credentials |
same file | retained kirro viewer |
| App Secrets | k8s/apps/<app>/secrets.sops.yaml |
that application |
restic_password |
ansible/group_vars/vps/secrets.sops.yaml |
restic backup repository |
admin_ssh_public_key |
same file | host access |
| R2 backup keys | never | /etc/vps-backup/r2.env, mode 0600 |
| Terraform secrets | never | external secrets.enc.env, read by scripts/tf.sh |
| age private keys | never | owner and argocd/sops-age |
The vcap v1 repositories under upayanmazumder-DevLabs are private and are not changed. No ArgoCD
Application reads them since 2026-10-27. ArgoCD reads other DevLabs repositories through
repo-creds-devlabs. An unauthenticated fetch returns 401.
git-creds-noodle and git-creds-rankstack exist only in the live cluster. They have no source
manifest. They support retained workloads that no longer run. Do not make them a prerequisite.
These Secrets remain in their namespaces for data recovery. No workload mounts them. They are not in git.
| Namespace | Secrets |
|---|---|
vcap-v1 |
auth-env, backend-env, pull-secret, vcap-v1-postgres-edge-tls |
vcap-dev |
auth-env, backend-env, pull-secret |
vcap-staging |
auth-env, backend-env, pull-secret |
bitvault |
bitvault-ghcr-pull, bitvault-minio-env, bitvault-postgres-env, bitvaultd-env |
meghmitra |
meghmitra-api-env, meghmitra-postgres-env |
rankstack |
rankstack-api-env |
kirro |
kirro-mock-admin, kirro-pinelabs, kirro-voice, kirro-whatsapp |
bandit |
bandit-secrets |
upayan-v5 |
upayan-v5-api-env |
Keep these Secrets until the owner deletes the retained data. Rotation is unnecessary while no workload runs. If a dataset is restored, rotate its credential first.
The upayan-api/upayan-api-env Secret is managed through SOPS in
k8s/apps/upayan-api/secrets.sops.yaml. It uses the eight values from the retired
upayan-v5-api-env Secret. The old namespace remains retained, but it serves no workload.
git-creds.repo-vcap-backend and repo-vcap-frontend.repo-creds-devlabs.git-creds-updater over SSH and ghcr-creds for the registry.gho_ token is not part of the current design.git-creds-updater writes image digests straight to main. Do not add a branch step.vcap-v1/pull-secret is retained and unused.ghcr-creds works. Do not treat the old 401 incident as current. Verify with the image-updater pod
logs after a credential change.
Rotation order:
Owner confirmation is required at each external provider step. Deploy and verify the new value
before revoking the old value. The full procedure is in
runbooks/rotate-secrets.md.
Encryption alone does not reduce exposure. Git history still holds the old plaintext.
Run a history rewrite only after rotation is complete and the owner confirms. Use git filter-repo,
then scan the full history with gitleaks. Push with git push --force only after the owner confirms.
The 2026-09-28 baseline was 177 findings across 35 commits. Treat that number as historical. A rewrite does not neutralise a leaked credential. Rotation does.
kubectl get secret -o yaml in shared output.runbooks/rotate-secrets.md: rotation procedure.certificates.md: live TLS Secrets.runbooks/recover-vm.md: from-scratch bootstrap.../.claude/skills/secrets-tls/SKILL.md: daily
conventions.