SHELL := /usr/bin/env bash .DEFAULT_GOAL := help KUBECONFIG ?= $(firstword $(wildcard $(HOME)/.kube/config-railiance01) $(wildcard $(HOME)/.kube/config)) KUBECTL_BIN ?= $(firstword $(shell command -v kubectl 2>/dev/null) $(wildcard $(HOME)/.local/bin/kubectl) kubectl) KUBECTL := $(KUBECTL_BIN) --kubeconfig=$(KUBECONFIG) # Cluster identity pin (RPF-WP-0019). # # Two reachable clusters each carry a CNPG Cluster named `apps-pg` in a # namespace named `databases`. Selecting by kubeconfig FILENAME does not # protect against applying to the wrong one: KUBECONFIG is an environment # variable, `?=` does not override it, and both files resolve to a # 127.0.0.1 tunnel port. So the guard pins cluster IDENTITY instead — # the kube-system namespace UID, which is unique per cluster and stable # for its lifetime. # # railiance01 (k3s v1.35.1) carries platform-pg, forgejo-db and both # apps-pg consumers. The other cluster carries gitea-db and only one # apps-pg consumer; `db-status` legitimately targets it, which is why the # guard is applied per-target rather than globally. RAILIANCE01_CLUSTER_UID ?= a553c742-0115-43d4-99a4-a5ca56fe0786 HELM := helm --kubeconfig=$(KUBECONFIG) NAMESPACE := platform PG_CHART_VERSION ?= 16.2.2 VALKEY_CHART_VERSION ?= 2.x OPENBAO_CHART_VERSION ?= 0.28.2 OPENBAO_NAMESPACE ?= openbao OPENBAO_RELEASE ?= openbao OPENBAO_VALUES ?= helm/openbao-values.yaml OPENBAO_MIDDLEWARE ?= helm/openbao-middleware.yaml OPENBAO_UI_OVERLAY_DIR ?= helm/openbao-ui-overlay OPENBAO_UI_OVERLAY_K8S ?= helm/openbao-ui-overlay-k8s.yaml OPENBAO_PUBLIC_INGRESS_ROLLBACK ?= helm/openbao-public-ingress.rollback.yaml OPENBAO_VERIFY_AUTH_ARGS ?= OPENBAO_RESTORE_EVIDENCE ?= /tmp/netkingdom-openbao-restore-drill/evidence.json OPENBAO_EMERGENCY_EVIDENCE ?= /tmp/netkingdom-openbao-emergency-drill/evidence.json EXTERNAL_SECRETS_NAMESPACE ?= external-secrets ARGOCD_NAMESPACE ?= argocd ARGOCD_BOOTSTRAP_DIR ?= argocd/bootstrap ARGOCD_REPOSITORY_SECRET ?= CREDENTIAL_GRANTS ?= credential-grants/catalog.yaml CREDENTIAL_CHANGE ?= CCR-2026-0001 CREDENTIAL_CHANGE_EVIDENCE_ARGS ?= CREDENTIAL_CHANGE_LIFECYCLE_ACTION ?= deactivate CREDENTIAL_CHANGE_LIFECYCLE_ARGS ?= CREDENTIAL_CHANGE_IMPORT_ARGS ?= STATE_HUB_URL ?= http://127.0.0.1:8000 OPENBAO_TOKEN_GRANT_ARGS ?= OPENBAO_CREDENTIAL_CHANGE_APPLIER_ARGS ?= OPENBAO_WORKLOAD_KV_ARGS ?= CREDENTIAL_HELPER_GLOBAL_ARGS ?= CREDENTIAL_HELPER_ARGS ?= CREDENTIAL_HELPER_CHILD_ENV ?= CREDENTIAL_HELPER_PURPOSE ?= flex-auth-openbao-smoke ##@ CloudNative PG (cnpg) — primary database operator db-deploy: ## Apply Gitea cnpg Cluster (creates gitea-db in databases namespace) $(KUBECTL) apply -f helm/gitea-db-cluster.yaml db-status: ## Show cnpg cluster health $(KUBECTL) cnpg status gitea-db -n databases 2>/dev/null || \ $(KUBECTL) get cluster gitea-db -n databases -o wide db-shell: ## Open psql shell on gitea-db primary $(KUBECTL) cnpg psql gitea-db -n databases -- -U gitea gitea db-logs: ## Tail gitea-db primary logs $(KUBECTL) logs -n databases -l cnpg.io/cluster=gitea-db -f --tail=50 ##@ Forgejo database (railiance01 production forge) forgejo-db-deploy: ## Apply forgejo-db cnpg Cluster + NetworkPolicies on railiance01 $(KUBECTL) apply -f helm/forgejo-db-cluster.yaml $(KUBECTL) apply -f helm/forgejo-db-networkpolicies.yaml forgejo-db-status: ## Show forgejo-db cnpg cluster health $(KUBECTL) cnpg status forgejo-db -n databases 2>/dev/null || \ $(KUBECTL) get cluster forgejo-db -n databases -o wide forgejo-db-shell: ## Open psql shell on forgejo-db primary $(KUBECTL) cnpg psql forgejo-db -n databases -- -U forgejo forgejo CONSUMING_ENTITY ?= ESTIMATE_EUR ?= CONSUMPTION_CLASS ?= new-order CONSUMPTION_MODE_FILE ?= data/consumption-mode/current.json ##@ Consumption mode (RAILIANCE-WP-0017) consumption-preflight: ## Refuse a new order that exceeds a restricted entity's allowance @test -n "$(CONSUMING_ENTITY)" || { echo 'set CONSUMING_ENTITY=entity:' >&2; exit 2; } CONSUMPTION_MODE_FILE='$(CONSUMPTION_MODE_FILE)' python3 scripts/consumption_mode.py check \ --entity '$(CONSUMING_ENTITY)' \ --class '$(CONSUMPTION_CLASS)' \ $(if $(ESTIMATE_EUR),--estimate-eur '$(ESTIMATE_EUR)') ##@ Shared apps-pg (S5 application databases) cluster-id: ## Print the kube-system UID of the cluster KUBECONFIG currently selects @echo "kubeconfig: $(KUBECONFIG)" @echo "cluster-uid: $$($(KUBECTL) get ns kube-system -o jsonpath='{.metadata.uid}' 2>/dev/null || echo UNREACHABLE)" railiance01-guard: ## Fail closed unless KUBECONFIG selects railiance01 @uid=$$($(KUBECTL) get ns kube-system -o jsonpath='{.metadata.uid}' 2>/dev/null); \ if [ -z "$$uid" ]; then \ echo "railiance01-guard: cluster unreachable via $(KUBECONFIG)" >&2; \ echo " hint: unset KUBECONFIG, or set it to ~/.kube/config-railiance01" >&2; \ exit 2; \ fi; \ if [ "$$uid" != "$(RAILIANCE01_CLUSTER_UID)" ]; then \ echo "railiance01-guard: REFUSING - wrong cluster" >&2; \ echo " kubeconfig: $(KUBECONFIG)" >&2; \ echo " expected: $(RAILIANCE01_CLUSTER_UID)" >&2; \ echo " found: $$uid" >&2; \ echo " both clusters carry apps-pg in namespace databases; applying here would hit the wrong one." >&2; \ exit 2; \ fi; \ echo "railiance01-guard: ok ($$uid)" apps-pg-verify-capacity: ## Verify cell ceiling, role limits and distinct backup prefixes python3 tools/verify_apps_pg_capacity.py helm/apps-pg-cluster.yaml helm/apps-pg-2-cluster.yaml apps-pg-deploy: railiance01-guard apps-pg-verify-capacity ## Apply shared apps-pg cnpg Cluster + NetworkPolicies @if [ -n "$(CONSUMING_ENTITY)" ]; then \ $(MAKE) consumption-preflight CONSUMING_ENTITY='$(CONSUMING_ENTITY)' \ ESTIMATE_EUR='$(ESTIMATE_EUR)' CONSUMPTION_CLASS=new-order; \ fi $(KUBECTL) apply -f helm/apps-pg-cluster.yaml $(KUBECTL) apply -f helm/apps-pg-networkpolicies.yaml apps-pg-backup-deploy: railiance01-guard ## Apply the daily backup after the governed S3 Secret exists @$(KUBECTL) get secret platform-pg-backup-s3 -n databases >/dev/null || { echo 'missing governed platform-pg-backup-s3 secret' >&2; exit 2; } $(KUBECTL) apply -f helm/apps-pg-backup.yaml apps-pg-isolation-probe: railiance01-guard ## Run bounded live consumer-boundary and connection-saturation probes KUBECONFIG='$(KUBECONFIG)' KUBECTL_BIN='$(KUBECTL_BIN)' tools/apps-pg-isolation-probe.sh apps-pg-overflow-dry-run: railiance01-guard apps-pg-verify-capacity ## Server-validate the unapplied overflow cell @$(KUBECTL) get secret apps-pg-2-credentials -n databases >/dev/null || { echo 'missing distinct apps-pg-2-credentials secret' >&2; exit 2; } $(KUBECTL) apply --dry-run=server -f helm/apps-pg-2-cluster.yaml $(KUBECTL) apply --dry-run=server -f helm/apps-pg-2-networkpolicies.yaml $(KUBECTL) apply --dry-run=server -f helm/apps-pg-2-backup.yaml apps-pg-overflow-deploy: apps-pg-overflow-dry-run ## Provision only for an approved fourth consumer $(KUBECTL) apply -f helm/apps-pg-2-cluster.yaml $(KUBECTL) apply -f helm/apps-pg-2-networkpolicies.yaml $(KUBECTL) apply -f helm/apps-pg-2-backup.yaml apps-pg-status: railiance01-guard ## Show apps-pg cnpg cluster health $(KUBECTL) cnpg status apps-pg -n databases 2>/dev/null || \ $(KUBECTL) get cluster apps-pg -n databases -o wide apps-pg-shell: railiance01-guard ## Open psql shell on apps-pg primary as apps_admin / apps_meta $(KUBECTL) cnpg psql apps-pg -n databases -- -U apps_admin apps_meta 2>/dev/null || \ $(KUBECTL) exec -it -n databases apps-pg-1 -- psql -U apps_admin apps_meta apps-pg-logs: ## Tail apps-pg primary logs $(KUBECTL) logs -n databases -l cnpg.io/cluster=apps-pg -f --tail=50 net-kingdom-pg-inter-hub-networkpolicy-deploy: ## Allow inter-hub to reach net-kingdom-pg $(KUBECTL) apply -f helm/net-kingdom-pg-inter-hub-networkpolicy.yaml ##@ PostgreSQL HA (retired — CNPG superseded this) pg-deploy: ## Retired: refuse to deploy Bitnami postgresql-ha @echo 'pg-deploy is retired (RAILIANCE-WP-0016 item 14). Use apps-pg / forgejo-db / rapp-postgres.' >&2; exit 2 pg-status: ## Confirm Bitnami postgresql-ha is absent @if $(KUBECTL) get ns $(NAMESPACE) >/dev/null 2>&1; then \ $(KUBECTL) get pods -n $(NAMESPACE) -l app.kubernetes.io/name=postgresql-ha; \ else \ echo 'postgresql-ha retired: namespace $(NAMESPACE) is absent'; \ fi pg-pgpool-check: ## Retired: no pgpool secret to check @echo 'pg-pgpool-check is retired with postgresql-ha (RAILIANCE-WP-0016 item 14).' >&2; exit 2 ##@ Valkey (cache) — declared, not deployed valkey-deploy: ## Gated: do not deploy Valkey until a consumer rapp exists @echo 'valkey-deploy is gated (RAILIANCE-WP-0016 item 14): no live consumer, nothing to extract from S2.' >&2; exit 2 valkey-status: ## Confirm Valkey is absent @if $(KUBECTL) get ns $(NAMESPACE) >/dev/null 2>&1; then \ $(KUBECTL) get pods -n $(NAMESPACE) -l app.kubernetes.io/name=valkey; \ else \ echo 'valkey undeployed: namespace $(NAMESPACE) is absent'; \ fi ##@ OpenBao (secrets) openbao-repo: ## Add / update the official OpenBao Helm repository $(HELM) repo add openbao https://openbao.github.io/openbao-helm --force-update $(HELM) repo update openbao openbao-dry-run: openbao-repo ## Render the OpenBao Helm release without applying it $(HELM) upgrade --install $(OPENBAO_RELEASE) openbao/openbao \ --version $(OPENBAO_CHART_VERSION) \ --namespace $(OPENBAO_NAMESPACE) \ --create-namespace \ -f $(OPENBAO_VALUES) \ --dry-run openbao-overlay-apply: ## Apply KeyCape login overlay gateway and assets OPENBAO_UI_OVERLAY_DIR=$(OPENBAO_UI_OVERLAY_DIR) \ OPENBAO_UI_OVERLAY_K8S=$(OPENBAO_UI_OVERLAY_K8S) \ KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ scripts/openbao-ui-overlay-apply.sh openbao-verify-login-overlay: ## Verify public KeyCape login overlay is active OPENBAO_UI_OVERLAY_DIR=$(OPENBAO_UI_OVERLAY_DIR) \ scripts/openbao-verify-login-overlay.sh $(OPENBAO_VERIFY_LOGIN_OVERLAY_ARGS) openbao-deploy: openbao-repo ## Deploy / upgrade OpenBao to the openbao namespace $(KUBECTL) create namespace $(OPENBAO_NAMESPACE) --dry-run=client -o yaml | $(KUBECTL) apply -f - $(HELM) upgrade --install $(OPENBAO_RELEASE) openbao/openbao \ --version $(OPENBAO_CHART_VERSION) \ --namespace $(OPENBAO_NAMESPACE) \ -f $(OPENBAO_VALUES) \ --wait --timeout 5m $(MAKE) openbao-overlay-apply .PHONY: openbao-public-listener-dry-run openbao-public-listener-status openbao-public-listener-retract openbao-public-listener-rollback openbao-public-listener-dry-run: ## Validate the guarded public-listener retraction without mutation KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_UI_OVERLAY_K8S=$(OPENBAO_UI_OVERLAY_K8S) \ OPENBAO_PUBLIC_INGRESS_ROLLBACK=$(OPENBAO_PUBLIC_INGRESS_ROLLBACK) \ scripts/openbao-public-listener-transition.sh dry-run openbao-public-listener-status: ## Report public Ingress and private tunnel readiness KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ scripts/openbao-public-listener-transition.sh status openbao-public-listener-retract: ## Attended deletion after exact tunnel/login confirmation KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_PUBLIC_INGRESS_ROLLBACK=$(OPENBAO_PUBLIC_INGRESS_ROLLBACK) \ scripts/openbao-public-listener-transition.sh apply openbao-public-listener-rollback: ## Attended restoration of the prior public Ingress KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_MIDDLEWARE=$(OPENBAO_MIDDLEWARE) \ OPENBAO_PUBLIC_INGRESS_ROLLBACK=$(OPENBAO_PUBLIC_INGRESS_ROLLBACK) \ scripts/openbao-public-listener-transition.sh rollback openbao-status: ## Show OpenBao pods, services, PVCs, and seal/init status $(KUBECTL) get pods,svc,pvc -n $(OPENBAO_NAMESPACE) \ -l app.kubernetes.io/instance=$(OPENBAO_RELEASE) -o wide -$(KUBECTL) exec -n $(OPENBAO_NAMESPACE) $(OPENBAO_RELEASE)-0 -- bao status openbao-verify: ## Run non-secret OpenBao deployment checks KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) scripts/openbao-verify.sh basic openbao-verify-post-unseal: ## Run post-unseal OpenBao filesystem checks KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) scripts/openbao-verify.sh post-unseal openbao-configure-initial: ## Apply first post-unseal audit, auth, mounts, and policies KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) scripts/openbao-apply-initial-config.sh openbao-configure-ssh: ## Enable SSH secrets engine, roles, and warden-sign policy KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) scripts/openbao-apply-ssh-engine.sh openbao-verify-ssh: ## Verify SSH engine mount, roles, and warden-sign policy KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) scripts/openbao-verify-ssh-engine.sh openbao-verify-authenticated: ## Run authenticated non-mutating OpenBao audit/auth/mount checks KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) scripts/openbao-verify-authenticated.sh $(OPENBAO_VERIFY_AUTH_ARGS) openbao-configure-external-secrets-issue-core: ## Configure OpenBao policy/role for issue-core ESO pilot KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) ESO_NAMESPACE=$(EXTERNAL_SECRETS_NAMESPACE) \ scripts/openbao-apply-external-secrets-issue-core.sh openbao-configure-external-secrets-activity-core: ## Configure OpenBao policy/role for activity-core ESO lane KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) OPENBAO_RELEASE=$(OPENBAO_RELEASE) ESO_NAMESPACE=$(EXTERNAL_SECRETS_NAMESPACE) OPENBAO_ESO_ROLE=external-secrets-activity-core OPENBAO_ESO_POLICY=external-secrets-activity-core POLICY_FILE='$(CURDIR)/openbao/policies/external-secrets-activity-core.hcl' OPENBAO_ESO_NEXT_PATH=platform/workloads/issue-core/issue-core/issue-core-runtime OPENBAO_ESO_NEXT_FIELDS=ISSUE_CORE_API_KEY OPENBAO_ESO_NEXT_TARGET=ExternalSecret/activity-core/actcore-issue-core-runtime scripts/openbao-apply-external-secrets-issue-core.sh openbao-configure-external-secrets-forgejo: ## Configure OpenBao policy/role for Forgejo mailer ESO lane KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) ESO_NAMESPACE=$(EXTERNAL_SECRETS_NAMESPACE) \ OPENBAO_ESO_ROLE=external-secrets-forgejo \ OPENBAO_ESO_POLICY=external-secrets-forgejo \ POLICY_FILE='$(CURDIR)/openbao/policies/external-secrets-forgejo.hcl' \ OPENBAO_ESO_NEXT_PATH=platform/workloads/forgejo/forgejo-mailer \ OPENBAO_ESO_NEXT_FIELDS=MAILER_PASSWD \ OPENBAO_ESO_NEXT_TARGET=ExternalSecret/forgejo/forgejo-mailer \ scripts/openbao-apply-external-secrets-issue-core.sh openbao-workload-kv-lanes-dry-run: ## Dry-run OpenBao workload KV read-lane policy apply scripts/openbao-apply-workload-kv-lanes.sh --dry-run $(OPENBAO_WORKLOAD_KV_ARGS) openbao-configure-workload-kv-lanes: ## Configure OpenBao workload KV read-lane policies KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) \ scripts/openbao-apply-workload-kv-lanes.sh $(OPENBAO_WORKLOAD_KV_ARGS) openbao-validate-restore-evidence: ## Validate non-secret OpenBao restore-drill evidence JSON OPENBAO_RESTORE_EVIDENCE='$(OPENBAO_RESTORE_EVIDENCE)' \ scripts/openbao-validate-restore-evidence.sh openbao-validate-emergency-evidence: ## Validate non-secret OpenBao emergency seal/unseal drill evidence JSON OPENBAO_EMERGENCY_EVIDENCE='$(OPENBAO_EMERGENCY_EVIDENCE)' \ scripts/openbao-validate-emergency-drill-evidence.sh ##@ Credential broker credential-grants-validate: ## Validate non-secret credential grant catalog scripts/credential-grants-validate.py $(CREDENTIAL_GRANTS) credential-change-validate: ## Validate non-secret credential change requests scripts/credential-change.py validate credential-change-render: ## Render a credential change request review summary scripts/credential-change.py render $(CREDENTIAL_CHANGE) credential-change-plan: ## Render a credential change request apply plan for review scripts/credential-change.py plan $(CREDENTIAL_CHANGE) credential-change-decision-templates: ## Render CCR approve/deny/needs-changes templates scripts/credential-change.py decision-templates $(CREDENTIAL_CHANGE) credential-change-status: ## Render credential change request readiness status scripts/credential-change.py status $(CREDENTIAL_CHANGE) credential-change-status-json: ## Render credential change request readiness status as JSON scripts/credential-change.py status --json $(CREDENTIAL_CHANGE) credential-change-sync-decision: ## Sync resolved State Hub decision back into a CCR scripts/credential-change.py sync-decision $(CREDENTIAL_CHANGE) --state-hub-url $(STATE_HUB_URL) credential-change-apply-plan: ## Render approved-only operator apply plan scripts/credential-change.py apply-plan $(CREDENTIAL_CHANGE) credential-change-operator-commands: ## Render approved-only non-secret OpenBao operator commands scripts/credential-change.py operator-commands $(CREDENTIAL_CHANGE) credential-change-applier-dry-run: ## Validate delegated OpenBao metadata mutations for a CCR scripts/credential-change.py applier-dry-run $(CREDENTIAL_CHANGE) credential-change-applier-apply-plan: ## Render delegated OpenBao metadata apply plan scripts/credential-change.py applier-apply $(CREDENTIAL_CHANGE) --plan-only credential-change-applier-apply: ## Apply delegated metadata; pass confirmation/actor args via CREDENTIAL_CHANGE_EVIDENCE_ARGS scripts/credential-change.py applier-apply $(CREDENTIAL_CHANGE) $(CREDENTIAL_CHANGE_EVIDENCE_ARGS) credential-change-runbook: ## Render the attended CCR apply/verify runbook scripts/credential-change.py runbook $(CREDENTIAL_CHANGE) credential-change-record-evidence: ## Record non-secret CCR evidence; pass CREDENTIAL_CHANGE_EVIDENCE_ARGS scripts/credential-change.py record-evidence $(CREDENTIAL_CHANGE) $(CREDENTIAL_CHANGE_EVIDENCE_ARGS) credential-change-lifecycle-plan: ## Render deactivation/rotation/compromise lifecycle guidance scripts/credential-change.py lifecycle-plan $(CREDENTIAL_CHANGE) --action $(CREDENTIAL_CHANGE_LIFECYCLE_ACTION) credential-change-lifecycle-event: ## Record lifecycle event; pass CREDENTIAL_CHANGE_LIFECYCLE_ARGS scripts/credential-change.py lifecycle-event $(CREDENTIAL_CHANGE) --action $(CREDENTIAL_CHANGE_LIFECYCLE_ACTION) $(CREDENTIAL_CHANGE_LIFECYCLE_ARGS) agent-high-risk-boundary-check: ## Check policy against the versioned ops-warden high-risk path input python3 scripts/agent_high_risk_boundary.py --json credential-change-import-inventory: ## Import existing lane as non-secret CCR; pass CREDENTIAL_CHANGE_IMPORT_ARGS scripts/credential-change.py import-inventory $(CREDENTIAL_CHANGE_IMPORT_ARGS) openbao-credential-change-appliers-dry-run: ## Dry-run credential-change applier policies/token roles scripts/openbao-apply-credential-change-appliers.py --dry-run $(OPENBAO_CREDENTIAL_CHANGE_APPLIER_ARGS) openbao-configure-credential-change-appliers: ## Apply credential-change applier policies/token roles KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) \ scripts/openbao-apply-credential-change-appliers.py $(OPENBAO_CREDENTIAL_CHANGE_APPLIER_ARGS) openbao-token-grants-dry-run: ## Dry-run OpenBao token roles and issuer policies for credential grants scripts/openbao-apply-token-grants.py --dry-run $(OPENBAO_TOKEN_GRANT_ARGS) openbao-configure-token-grants: ## Apply OpenBao token roles and issuer policies for credential grants KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) \ scripts/openbao-apply-token-grants.py $(OPENBAO_TOKEN_GRANT_ARGS) openbao-verify-token-grants-dry-run: ## Dry-run OpenBao token grant verification scripts/openbao-verify-token-grants.py --dry-run $(OPENBAO_TOKEN_GRANT_ARGS) openbao-verify-token-grants: ## Verify OpenBao token roles and issuer policies for credential grants KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) \ scripts/openbao-verify-token-grants.py $(OPENBAO_TOKEN_GRANT_ARGS) openbao-verify-token-grants-smoke: ## Mint/revoke a child token and prove bounded warden-sign capabilities KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) \ scripts/openbao-verify-token-grants.py --issue-smoke-token $(OPENBAO_TOKEN_GRANT_ARGS) credential-helper-dry-run: ## Dry-run credential request, exec, status, and revoke helper flows scripts/credential.py $(CREDENTIAL_HELPER_GLOBAL_ARGS) request --dry-run \ --grant ops-warden/warden-sign --purpose $(CREDENTIAL_HELPER_PURPOSE) \ $(CREDENTIAL_HELPER_ARGS) scripts/credential.py $(CREDENTIAL_HELPER_GLOBAL_ARGS) request --dry-run \ --grant ops-warden/warden-sign --purpose $(CREDENTIAL_HELPER_PURPOSE) \ --delivery kubernetes-auth $(CREDENTIAL_HELPER_ARGS) scripts/credential.py $(CREDENTIAL_HELPER_GLOBAL_ARGS) exec --dry-run \ --grant ops-warden/warden-sign --purpose $(CREDENTIAL_HELPER_PURPOSE) \ $(CREDENTIAL_HELPER_ARGS) -- SMOKE_VAULT=1 /bin/true scripts/credential.py $(CREDENTIAL_HELPER_GLOBAL_ARGS) status --dry-run example-accessor scripts/credential.py $(CREDENTIAL_HELPER_GLOBAL_ARGS) revoke --dry-run example-accessor credential-tests: ## Run offline credential broker unit tests python3 -m unittest discover -s tests -p 'test_credential*.py' credential-change-tests: ## Run credential change request unit tests python3 -m unittest discover -s tests -p 'test_credential_change.py' credential-exec-ops-warden-smoke: ## Run ops-warden smoke with an exec-injected warden-sign token KUBECTL='$(KUBECTL)' OPENBAO_NAMESPACE=$(OPENBAO_NAMESPACE) \ OPENBAO_RELEASE=$(OPENBAO_RELEASE) \ scripts/credential.py $(CREDENTIAL_HELPER_GLOBAL_ARGS) exec \ --grant ops-warden/warden-sign --purpose ops-warden-production-sign-smoke \ $(CREDENTIAL_HELPER_ARGS) -- \ $(CREDENTIAL_HELPER_CHILD_ENV) \ SMOKE_VAULT=1 /home/worsch/ops-warden/scripts/policy_gate_production_smoke.sh ##@ ArgoCD GitOps bootstrap argocd-bootstrap-dry-run: ## Server-side dry-run ArgoCD AppProjects and root Application $(KUBECTL) apply --dry-run=server -k $(ARGOCD_BOOTSTRAP_DIR) argocd-bootstrap-deploy: ## Apply ArgoCD AppProjects and root Application $(KUBECTL) apply -k $(ARGOCD_BOOTSTRAP_DIR) argocd-repo-apply: ## Apply a SOPS-encrypted ArgoCD repository Secret (set ARGOCD_REPOSITORY_SECRET) @test -n "$(ARGOCD_REPOSITORY_SECRET)" || \ (echo "ERROR: set ARGOCD_REPOSITORY_SECRET=argocd/repositories/.repository.sops.yaml"; exit 1) sops -d $(ARGOCD_REPOSITORY_SECRET) | $(KUBECTL) apply -f - argocd-status: ## Show Railiance ArgoCD projects, root app, and registered repos $(KUBECTL) get appprojects.argoproj.io -n $(ARGOCD_NAMESPACE) \ railiance-bootstrap railiance-tenants railiance-platform-addons $(KUBECTL) get applications.argoproj.io -n $(ARGOCD_NAMESPACE) \ railiance-apps-root external-secrets openbao-secretstore issue-core $(KUBECTL) get secrets -n $(ARGOCD_NAMESPACE) \ -l argocd.argoproj.io/secret-type=repository ##@ Backup RAILIANCE01_KUBECONFIG ?= $(HOME)/.kube/config-railiance01 backup: ## Backup platform services (PostgreSQL logical dump) — age-encrypted to Nextcloud @test -x tools/cmd/railiance-backup || { echo "tools/cmd/railiance-backup not installed; use forgejo-backup for Forgejo." >&2; exit 1; } sudo tools/cmd/railiance-backup forgejo-backup: ## Forgejo dump + forgejo-db pg_dump → age → Nextcloud (T04/T09 Option A) KUBECONFIG="$(RAILIANCE01_KUBECONFIG)" tools/cmd/forgejo-backup forgejo-backup-dry-run: ## Forgejo backup without Nextcloud upload KUBECONFIG="$(RAILIANCE01_KUBECONFIG)" RAILIANCE_BACKUP_DRY_RUN=1 tools/cmd/forgejo-backup forgejo-backup-status: ## Show last Forgejo backup success and 7-day gate @STAMP="$(HOME)/.cache/railiance/backups/forgejo/success-log"; \ if [[ ! -f "$$STAMP" ]]; then echo "No forgejo backups recorded yet."; exit 1; fi; \ echo "Last success: $$(tail -1 "$$STAMP")"; \ echo "Recent successes:"; tail -7 "$$STAMP"; \ echo "7-day gate: $$(tail -7 "$$STAMP" | wc -l)/7 consecutive days logged (verify cron separately)" forgejo-package-prune-dry-run: ## List Forgejo package versions beyond retention depth (no deletes) tools/cmd/forgejo-package-prune forgejo-package-prune: ## Prune Forgejo packages — keep newest 3 versions per package tools/cmd/forgejo-package-prune --apply ##@ Family declarations reef-deployables: ## Capture live reef-railiance deployables for the family validator python3 scripts/capture-reef-deployables.py -o docs/evidence/reef-railiance-deployables.json apps-pg-evidence: ## Capture non-secret apps-pg capacity evidence for resource-control python3 scripts/capture-apps-pg-evidence.py -o docs/evidence/RAILIANCE-WP-0016-apps-pg-observation.json ##@ Help help: ## Show this help @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} \ /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-22s\033[0m %s\n", $$1, $$2 } \ /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST) audit-whitehat-e2-preflight: ## Value-safe live preflight for the approved audit-core E2 window python3 scripts/audit-core-whitehat-e2-credentials.py preflight audit-whitehat-e2-project: ## Project two exact mount-only credentials during the approved window python3 scripts/audit-core-whitehat-e2-credentials.py project --confirm WH-ENG-20260822-AUDIT-E2-01:attended audit-whitehat-e2-cleanup: ## Remove the exact engagement credentials and reload audit-core python3 scripts/audit-core-whitehat-e2-credentials.py cleanup --confirm WH-ENG-20260822-AUDIT-E2-01:attended audit-database-lease-recovery-preflight: ## Read-only gate for the restart-free database lease exercise python3 scripts/audit-core-recovery-preflight.py database-lease $(AUDIT_DATABASE_LEASE_PREFLIGHT_ARGS) audit-database-lease-approval-check: ## Validate an approved T02 receipt without mutation @test -n "$(AUDIT_DATABASE_LEASE_APPROVAL)" || { echo 'set AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json' >&2; exit 2; } python3 scripts/audit-core-database-lease-recovery.py validate-approval \ --approval "$(AUDIT_DATABASE_LEASE_APPROVAL)" audit-database-lease-recovery-exercise: ## Run the separately approved attended T02 exercise @test -n "$(AUDIT_DATABASE_LEASE_APPROVAL)" || { echo 'set AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json' >&2; exit 2; } @test -n "$(AUDIT_DATABASE_LEASE_DRIVER)" || { echo 'set AUDIT_DATABASE_LEASE_DRIVER=/path/to/approved-load-driver' >&2; exit 2; } python3 scripts/audit-core-database-lease-recovery.py exercise \ --approval "$(AUDIT_DATABASE_LEASE_APPROVAL)" \ --load-driver "$(AUDIT_DATABASE_LEASE_DRIVER)" \ --confirm RAILIANCE-WP-0024-T02:attended railiance01-reboot-preflight: ## Read-only gate for the coordinated single-node reboot python3 scripts/audit-core-recovery-preflight.py node-reboot $(RAILIANCE01_REBOOT_PREFLIGHT_ARGS) wp0024-owner-show: ## Show one owner's hash-bound review contract (set OWNER) @test -n "$(OWNER)" || { echo 'set OWNER=audit-core|rapp-postgres|railiance-cluster|railiance-infra' >&2; exit 2; } @python3 scripts/wp0024-owner-review.py show --owner "$(OWNER)" wp0024-owner-verify: ## Run one owner's named read-only checks (set OWNER) @test -n "$(OWNER)" || { echo 'set OWNER=audit-core|rapp-postgres|railiance-cluster|railiance-infra' >&2; exit 2; } @python3 scripts/wp0024-owner-review.py verify --owner "$(OWNER)" wp0024-owner-status: ## Collect current hash-bound owner receipts from State Hub @python3 scripts/wp0024-owner-review.py status wp0024-t02-driver-show: ## Show the exact T02 synthetic-load driver interface @python3 scripts/wp0024-t02-driver-candidate.py show wp0024-t02-driver-status: ## Collect a revision-pinned audit-core driver candidate @python3 scripts/wp0024-t02-driver-candidate.py status wp0024-t02-driver-register: ## Register committed audit-core driver source without executing it @test -n "$(DRIVER)" || { echo 'set DRIVER to a path relative to the audit-core repo' >&2; exit 2; } @test -n "$(REVIEWER)" || { echo 'set REVIEWER to the audit-core reviewer identifier' >&2; exit 2; } @python3 scripts/wp0024-t02-driver-candidate.py register \ --repo "$(abspath ../audit-core)" --driver "$(DRIVER)" --reviewer "$(REVIEWER)" .PHONY: audit-whitehat-e2-preflight audit-whitehat-e2-project audit-whitehat-e2-cleanup audit-database-lease-recovery-preflight audit-database-lease-approval-check audit-database-lease-recovery-exercise railiance01-reboot-preflight wp0024-owner-show wp0024-owner-verify wp0024-owner-status wp0024-t02-driver-show wp0024-t02-driver-status wp0024-t02-driver-register apps-pg-isolation-probe .PHONY: db-deploy db-status db-shell db-logs forgejo-db-deploy forgejo-db-status forgejo-db-shell apps-pg-deploy apps-pg-status apps-pg-shell apps-pg-logs net-kingdom-pg-inter-hub-networkpolicy-deploy pg-deploy pg-status pg-pgpool-check valkey-deploy valkey-status openbao-repo openbao-dry-run openbao-overlay-apply openbao-verify-login-overlay openbao-deploy openbao-status openbao-verify openbao-verify-post-unseal openbao-configure-initial openbao-configure-ssh openbao-verify-ssh openbao-verify-authenticated openbao-configure-external-secrets-issue-core openbao-configure-external-secrets-activity-core openbao-configure-external-secrets-forgejo openbao-validate-restore-evidence openbao-validate-emergency-evidence credential-grants-validate credential-change-applier-dry-run credential-change-applier-apply-plan credential-change-applier-apply credential-change-runbook credential-change-record-evidence credential-change-lifecycle-plan credential-change-lifecycle-event credential-change-import-inventory agent-high-risk-boundary-check openbao-credential-change-appliers-dry-run openbao-configure-credential-change-appliers openbao-token-grants-dry-run openbao-configure-token-grants openbao-verify-token-grants-dry-run openbao-verify-token-grants openbao-verify-token-grants-smoke credential-helper-dry-run credential-tests credential-exec-ops-warden-smoke argocd-bootstrap-dry-run argocd-bootstrap-deploy argocd-repo-apply argocd-status backup forgejo-backup forgejo-backup-dry-run forgejo-backup-status forgejo-package-prune forgejo-package-prune-dry-run reef-deployables apps-pg-evidence consumption-preflight help