2026-03-10 00:36:06 +01:00
SHELL := /usr/bin/env bash
.DEFAULT_GOAL := help
Pin apps-pg targets to railiance01 by cluster identity; seed RPF-WP-0020
Two reachable clusters each carry a CNPG Cluster named apps-pg in a
namespace named databases. KUBECONFIG is an environment variable, so the
Makefile ?= default never applied, and RAILIANCE01_KUBECONFIG pointed at
config-hosteurope - a different cluster. Had the environment pointed at the
other reachable cluster instead of an unauthorized one, make apps-pg-deploy
would have applied RPF-WP-0019 connection limits, role timeouts and backup
config to the wrong cluster and reported success. The Unauthorized error was
the only thing that prevented it.
Filename selection cannot protect against this: both kubeconfigs resolve to
a 127.0.0.1 tunnel port and the environment wins either way. railiance01-guard
pins identity instead, comparing the live kube-system namespace UID against
RAILIANCE01_CLUSTER_UID, and fails closed on mismatch or unreachability. It
gates apps-pg deploy, backup-deploy, overflow-dry-run, status and shell.
Verified refusing on the wrong cluster, refusing when unreachable, and
passing on railiance01. Not global: db-status legitimately targets the other
cluster for gitea-db.
RPF-WP-0019 blocker note corrected - the cluster was never unreachable, our
wiring was wrong.
RPF-WP-0020 seeded for the pre-existing CCR test failure, which is two
unrelated problems: CCR-2026-0010 is an active lane missing its whole
openbao.auth block, and CCR-2026-0011 is an honest in-flight draft the suite
has no way to express.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:18:37 +02:00
KUBECONFIG ?= $( firstword $( wildcard $( HOME) /.kube/config-railiance01) $( wildcard $( HOME) /.kube/config) )
2026-05-19 04:50:40 +02:00
KUBECTL_BIN ?= $( firstword $( shell command -v kubectl 2>/dev/null) $( wildcard $( HOME) /.local/bin/kubectl) kubectl)
KUBECTL := $( KUBECTL_BIN) --kubeconfig= $( KUBECONFIG)
Pin apps-pg targets to railiance01 by cluster identity; seed RPF-WP-0020
Two reachable clusters each carry a CNPG Cluster named apps-pg in a
namespace named databases. KUBECONFIG is an environment variable, so the
Makefile ?= default never applied, and RAILIANCE01_KUBECONFIG pointed at
config-hosteurope - a different cluster. Had the environment pointed at the
other reachable cluster instead of an unauthorized one, make apps-pg-deploy
would have applied RPF-WP-0019 connection limits, role timeouts and backup
config to the wrong cluster and reported success. The Unauthorized error was
the only thing that prevented it.
Filename selection cannot protect against this: both kubeconfigs resolve to
a 127.0.0.1 tunnel port and the environment wins either way. railiance01-guard
pins identity instead, comparing the live kube-system namespace UID against
RAILIANCE01_CLUSTER_UID, and fails closed on mismatch or unreachability. It
gates apps-pg deploy, backup-deploy, overflow-dry-run, status and shell.
Verified refusing on the wrong cluster, refusing when unreachable, and
passing on railiance01. Not global: db-status legitimately targets the other
cluster for gitea-db.
RPF-WP-0019 blocker note corrected - the cluster was never unreachable, our
wiring was wrong.
RPF-WP-0020 seeded for the pre-existing CCR test failure, which is two
unrelated problems: CCR-2026-0010 is an active lane missing its whole
openbao.auth block, and CCR-2026-0011 is an honest in-flight draft the suite
has no way to express.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:18:37 +02:00
# 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
feat(platform): T01 — standalone PostgreSQL HA chart scaffold
Lays out the S3 platform layer foundation for RAIL-PL-WP-0001 T01:
- .sops.yaml: age encryption policy (shared key, *.sops.yaml pattern)
- .gitignore: prevents accidental commit of decrypted values files
- Makefile: pg-deploy, pg-status, pg-pgpool-check, valkey-deploy,
valkey-status, backup targets with KUBECONFIG/HELM wiring
- helm/postgresql-ha-values.yaml.template: annotated values schema
with CHANGEME_ placeholders; includes pgpool-password fix from
RAIL-BS-WP-0003; notes on single-node vs ThreePhoenix scaling
- docs/postgresql-ha.md: connection strings, DB creation, password
rotation, pgpool-password critical note, HA failover test ref,
ThreePhoenix scaling path
To complete T01: fill in CHANGEME_ values, encrypt with sops -e -i,
then run make pg-deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 02:17:55 +01:00
HELM := helm --kubeconfig= $( KUBECONFIG)
NAMESPACE := platform
PG_CHART_VERSION ?= 16.2.2
VALKEY_CHART_VERSION ?= 2.x
2026-05-18 11:53:59 +02:00
OPENBAO_CHART_VERSION ?= 0.28.2
OPENBAO_NAMESPACE ?= openbao
OPENBAO_RELEASE ?= openbao
OPENBAO_VALUES ?= helm/openbao-values.yaml
2026-06-18 01:06:43 +02:00
OPENBAO_MIDDLEWARE ?= helm/openbao-middleware.yaml
2026-06-19 20:28:16 +02:00
OPENBAO_UI_OVERLAY_DIR ?= helm/openbao-ui-overlay
OPENBAO_UI_OVERLAY_K8S ?= helm/openbao-ui-overlay-k8s.yaml
2026-08-23 12:50:23 +02:00
OPENBAO_PUBLIC_INGRESS_ROLLBACK ?= helm/openbao-public-ingress.rollback.yaml
2026-06-01 22:46:14 +02:00
OPENBAO_VERIFY_AUTH_ARGS ?=
2026-09-10 08:15:41 +02:00
SESSION_QUESTIONS ?= Q1,Q2,Q3,Q4,Q5
2026-06-01 23:57:00 +02:00
OPENBAO_RESTORE_EVIDENCE ?= /tmp/netkingdom-openbao-restore-drill/evidence.json
2026-06-02 00:08:17 +02:00
OPENBAO_EMERGENCY_EVIDENCE ?= /tmp/netkingdom-openbao-emergency-drill/evidence.json
2026-06-25 20:08:36 +02:00
EXTERNAL_SECRETS_NAMESPACE ?= external-secrets
2026-06-19 20:28:16 +02:00
ARGOCD_NAMESPACE ?= argocd
ARGOCD_BOOTSTRAP_DIR ?= argocd/bootstrap
ARGOCD_REPOSITORY_SECRET ?=
2026-06-26 17:49:40 +02:00
CREDENTIAL_GRANTS ?= credential-grants/catalog.yaml
2026-06-27 22:57:21 +02:00
CREDENTIAL_CHANGE ?= CCR-2026-0001
2026-07-01 20:07:26 +02:00
CREDENTIAL_CHANGE_EVIDENCE_ARGS ?=
CREDENTIAL_CHANGE_LIFECYCLE_ACTION ?= deactivate
CREDENTIAL_CHANGE_LIFECYCLE_ARGS ?=
CREDENTIAL_CHANGE_IMPORT_ARGS ?=
2026-06-28 00:00:02 +02:00
STATE_HUB_URL ?= http://127.0.0.1:8000
2026-06-27 00:06:03 +02:00
OPENBAO_TOKEN_GRANT_ARGS ?=
2026-07-01 20:07:26 +02:00
OPENBAO_CREDENTIAL_CHANGE_APPLIER_ARGS ?=
2026-06-27 22:48:24 +02:00
OPENBAO_WORKLOAD_KV_ARGS ?=
2026-06-27 00:29:53 +02:00
CREDENTIAL_HELPER_GLOBAL_ARGS ?=
2026-06-27 00:06:03 +02:00
CREDENTIAL_HELPER_ARGS ?=
2026-07-01 23:10:38 +02:00
CREDENTIAL_HELPER_CHILD_ENV ?=
2026-06-27 00:06:03 +02:00
CREDENTIAL_HELPER_PURPOSE ?= flex-auth-openbao-smoke
feat(platform): T01 — standalone PostgreSQL HA chart scaffold
Lays out the S3 platform layer foundation for RAIL-PL-WP-0001 T01:
- .sops.yaml: age encryption policy (shared key, *.sops.yaml pattern)
- .gitignore: prevents accidental commit of decrypted values files
- Makefile: pg-deploy, pg-status, pg-pgpool-check, valkey-deploy,
valkey-status, backup targets with KUBECONFIG/HELM wiring
- helm/postgresql-ha-values.yaml.template: annotated values schema
with CHANGEME_ placeholders; includes pgpool-password fix from
RAIL-BS-WP-0003; notes on single-node vs ThreePhoenix scaling
- docs/postgresql-ha.md: connection strings, DB creation, password
rotation, pgpool-password critical note, HA failover test ref,
ThreePhoenix scaling path
To complete T01: fill in CHANGEME_ values, encrypt with sops -e -i,
then run make pg-deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 02:17:55 +01:00
2026-03-27 02:07:31 +01:00
##@ CloudNative PG (cnpg) — primary database operator
2026-10-22 00:09:03 +02:00
db-deploy db-status db-shell db-logs : ## Retired Gitea-era gitea-db targets; use forgejo-db-*
@echo "gitea-db was the CoulombCore-era Gitea database and does not exist on railiance01." >& 2
@echo "Use forgejo-db-deploy / forgejo-db-status / forgejo-db-shell (RAPPS-WP-0019-T05)." >& 2
@exit 2
2026-03-27 02:07:31 +01:00
2026-07-04 13:26:07 +02:00
##@ 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
2026-09-06 00:48:25 +02:00
$( KUBECTL) apply -f helm/forgejo-db-backup-networkpolicies.yaml
$( KUBECTL) apply -f helm/forgejo-db-backup.yaml
2026-07-04 13:26:07 +02:00
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
2026-08-15 14:56:02 +02:00
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:<slug>' >& 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)' )
2026-05-19 04:50:40 +02:00
##@ Shared apps-pg (S5 application databases)
Pin apps-pg targets to railiance01 by cluster identity; seed RPF-WP-0020
Two reachable clusters each carry a CNPG Cluster named apps-pg in a
namespace named databases. KUBECONFIG is an environment variable, so the
Makefile ?= default never applied, and RAILIANCE01_KUBECONFIG pointed at
config-hosteurope - a different cluster. Had the environment pointed at the
other reachable cluster instead of an unauthorized one, make apps-pg-deploy
would have applied RPF-WP-0019 connection limits, role timeouts and backup
config to the wrong cluster and reported success. The Unauthorized error was
the only thing that prevented it.
Filename selection cannot protect against this: both kubeconfigs resolve to
a 127.0.0.1 tunnel port and the environment wins either way. railiance01-guard
pins identity instead, comparing the live kube-system namespace UID against
RAILIANCE01_CLUSTER_UID, and fails closed on mismatch or unreachability. It
gates apps-pg deploy, backup-deploy, overflow-dry-run, status and shell.
Verified refusing on the wrong cluster, refusing when unreachable, and
passing on railiance01. Not global: db-status legitimately targets the other
cluster for gitea-db.
RPF-WP-0019 blocker note corrected - the cluster was never unreachable, our
wiring was wrong.
RPF-WP-0020 seeded for the pre-existing CCR test failure, which is two
unrelated problems: CCR-2026-0010 is an active lane missing its whole
openbao.auth block, and CCR-2026-0011 is an honest in-flight draft the suite
has no way to express.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:18:37 +02:00
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) "
2026-08-18 13:35:04 +02:00
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
Pin apps-pg targets to railiance01 by cluster identity; seed RPF-WP-0020
Two reachable clusters each carry a CNPG Cluster named apps-pg in a
namespace named databases. KUBECONFIG is an environment variable, so the
Makefile ?= default never applied, and RAILIANCE01_KUBECONFIG pointed at
config-hosteurope - a different cluster. Had the environment pointed at the
other reachable cluster instead of an unauthorized one, make apps-pg-deploy
would have applied RPF-WP-0019 connection limits, role timeouts and backup
config to the wrong cluster and reported success. The Unauthorized error was
the only thing that prevented it.
Filename selection cannot protect against this: both kubeconfigs resolve to
a 127.0.0.1 tunnel port and the environment wins either way. railiance01-guard
pins identity instead, comparing the live kube-system namespace UID against
RAILIANCE01_CLUSTER_UID, and fails closed on mismatch or unreachability. It
gates apps-pg deploy, backup-deploy, overflow-dry-run, status and shell.
Verified refusing on the wrong cluster, refusing when unreachable, and
passing on railiance01. Not global: db-status legitimately targets the other
cluster for gitea-db.
RPF-WP-0019 blocker note corrected - the cluster was never unreachable, our
wiring was wrong.
RPF-WP-0020 seeded for the pre-existing CCR test failure, which is two
unrelated problems: CCR-2026-0010 is an active lane missing its whole
openbao.auth block, and CCR-2026-0011 is an honest in-flight draft the suite
has no way to express.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:18:37 +02:00
apps-pg-deploy : railiance 01-guard apps -pg -verify -capacity ## Apply shared apps-pg cnpg Cluster + NetworkPolicies
2026-08-15 14:56:02 +02:00
@if [ -n " $( CONSUMING_ENTITY) " ] ; then \
$( MAKE) consumption-preflight CONSUMING_ENTITY = '$(CONSUMING_ENTITY)' \
ESTIMATE_EUR = '$(ESTIMATE_EUR)' CONSUMPTION_CLASS = new-order; \
fi
2026-05-19 04:50:40 +02:00
$( KUBECTL) apply -f helm/apps-pg-cluster.yaml
$( KUBECTL) apply -f helm/apps-pg-networkpolicies.yaml
Pin apps-pg targets to railiance01 by cluster identity; seed RPF-WP-0020
Two reachable clusters each carry a CNPG Cluster named apps-pg in a
namespace named databases. KUBECONFIG is an environment variable, so the
Makefile ?= default never applied, and RAILIANCE01_KUBECONFIG pointed at
config-hosteurope - a different cluster. Had the environment pointed at the
other reachable cluster instead of an unauthorized one, make apps-pg-deploy
would have applied RPF-WP-0019 connection limits, role timeouts and backup
config to the wrong cluster and reported success. The Unauthorized error was
the only thing that prevented it.
Filename selection cannot protect against this: both kubeconfigs resolve to
a 127.0.0.1 tunnel port and the environment wins either way. railiance01-guard
pins identity instead, comparing the live kube-system namespace UID against
RAILIANCE01_CLUSTER_UID, and fails closed on mismatch or unreachability. It
gates apps-pg deploy, backup-deploy, overflow-dry-run, status and shell.
Verified refusing on the wrong cluster, refusing when unreachable, and
passing on railiance01. Not global: db-status legitimately targets the other
cluster for gitea-db.
RPF-WP-0019 blocker note corrected - the cluster was never unreachable, our
wiring was wrong.
RPF-WP-0020 seeded for the pre-existing CCR test failure, which is two
unrelated problems: CCR-2026-0010 is an active lane missing its whole
openbao.auth block, and CCR-2026-0011 is an honest in-flight draft the suite
has no way to express.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:18:37 +02:00
apps-pg-backup-deploy : railiance 01-guard ## Apply the daily backup after the governed S3 Secret exists
2026-08-18 13:35:04 +02:00
@$( 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
2026-08-20 22:58:45 +02:00
apps-pg-isolation-probe : railiance 01-guard ## Run bounded live consumer-boundary and connection-saturation probes
KUBECONFIG = '$(KUBECONFIG)' KUBECTL_BIN = '$(KUBECTL_BIN)' tools/apps-pg-isolation-probe.sh
Pin apps-pg targets to railiance01 by cluster identity; seed RPF-WP-0020
Two reachable clusters each carry a CNPG Cluster named apps-pg in a
namespace named databases. KUBECONFIG is an environment variable, so the
Makefile ?= default never applied, and RAILIANCE01_KUBECONFIG pointed at
config-hosteurope - a different cluster. Had the environment pointed at the
other reachable cluster instead of an unauthorized one, make apps-pg-deploy
would have applied RPF-WP-0019 connection limits, role timeouts and backup
config to the wrong cluster and reported success. The Unauthorized error was
the only thing that prevented it.
Filename selection cannot protect against this: both kubeconfigs resolve to
a 127.0.0.1 tunnel port and the environment wins either way. railiance01-guard
pins identity instead, comparing the live kube-system namespace UID against
RAILIANCE01_CLUSTER_UID, and fails closed on mismatch or unreachability. It
gates apps-pg deploy, backup-deploy, overflow-dry-run, status and shell.
Verified refusing on the wrong cluster, refusing when unreachable, and
passing on railiance01. Not global: db-status legitimately targets the other
cluster for gitea-db.
RPF-WP-0019 blocker note corrected - the cluster was never unreachable, our
wiring was wrong.
RPF-WP-0020 seeded for the pre-existing CCR test failure, which is two
unrelated problems: CCR-2026-0010 is an active lane missing its whole
openbao.auth block, and CCR-2026-0011 is an honest in-flight draft the suite
has no way to express.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:18:37 +02:00
apps-pg-overflow-dry-run : railiance 01-guard apps -pg -verify -capacity ## Server-validate the unapplied overflow cell
2026-08-18 13:35:04 +02:00
@$( 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
Pin apps-pg targets to railiance01 by cluster identity; seed RPF-WP-0020
Two reachable clusters each carry a CNPG Cluster named apps-pg in a
namespace named databases. KUBECONFIG is an environment variable, so the
Makefile ?= default never applied, and RAILIANCE01_KUBECONFIG pointed at
config-hosteurope - a different cluster. Had the environment pointed at the
other reachable cluster instead of an unauthorized one, make apps-pg-deploy
would have applied RPF-WP-0019 connection limits, role timeouts and backup
config to the wrong cluster and reported success. The Unauthorized error was
the only thing that prevented it.
Filename selection cannot protect against this: both kubeconfigs resolve to
a 127.0.0.1 tunnel port and the environment wins either way. railiance01-guard
pins identity instead, comparing the live kube-system namespace UID against
RAILIANCE01_CLUSTER_UID, and fails closed on mismatch or unreachability. It
gates apps-pg deploy, backup-deploy, overflow-dry-run, status and shell.
Verified refusing on the wrong cluster, refusing when unreachable, and
passing on railiance01. Not global: db-status legitimately targets the other
cluster for gitea-db.
RPF-WP-0019 blocker note corrected - the cluster was never unreachable, our
wiring was wrong.
RPF-WP-0020 seeded for the pre-existing CCR test failure, which is two
unrelated problems: CCR-2026-0010 is an active lane missing its whole
openbao.auth block, and CCR-2026-0011 is an honest in-flight draft the suite
has no way to express.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:18:37 +02:00
apps-pg-status : railiance 01-guard ## Show apps-pg cnpg cluster health
2026-05-19 04:50:40 +02:00
$( KUBECTL) cnpg status apps-pg -n databases 2>/dev/null || \
$( KUBECTL) get cluster apps-pg -n databases -o wide
Pin apps-pg targets to railiance01 by cluster identity; seed RPF-WP-0020
Two reachable clusters each carry a CNPG Cluster named apps-pg in a
namespace named databases. KUBECONFIG is an environment variable, so the
Makefile ?= default never applied, and RAILIANCE01_KUBECONFIG pointed at
config-hosteurope - a different cluster. Had the environment pointed at the
other reachable cluster instead of an unauthorized one, make apps-pg-deploy
would have applied RPF-WP-0019 connection limits, role timeouts and backup
config to the wrong cluster and reported success. The Unauthorized error was
the only thing that prevented it.
Filename selection cannot protect against this: both kubeconfigs resolve to
a 127.0.0.1 tunnel port and the environment wins either way. railiance01-guard
pins identity instead, comparing the live kube-system namespace UID against
RAILIANCE01_CLUSTER_UID, and fails closed on mismatch or unreachability. It
gates apps-pg deploy, backup-deploy, overflow-dry-run, status and shell.
Verified refusing on the wrong cluster, refusing when unreachable, and
passing on railiance01. Not global: db-status legitimately targets the other
cluster for gitea-db.
RPF-WP-0019 blocker note corrected - the cluster was never unreachable, our
wiring was wrong.
RPF-WP-0020 seeded for the pre-existing CCR test failure, which is two
unrelated problems: CCR-2026-0010 is an active lane missing its whole
openbao.auth block, and CCR-2026-0011 is an honest in-flight draft the suite
has no way to express.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:18:37 +02:00
apps-pg-shell : railiance 01-guard ## Open psql shell on apps-pg primary as apps_admin / apps_meta
2026-05-19 04:50:40 +02:00
$( 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
2026-06-14 21:43:26 +02:00
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
2026-08-15 14:43:44 +02:00
##@ 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; \
feat(platform): T01 — standalone PostgreSQL HA chart scaffold
Lays out the S3 platform layer foundation for RAIL-PL-WP-0001 T01:
- .sops.yaml: age encryption policy (shared key, *.sops.yaml pattern)
- .gitignore: prevents accidental commit of decrypted values files
- Makefile: pg-deploy, pg-status, pg-pgpool-check, valkey-deploy,
valkey-status, backup targets with KUBECONFIG/HELM wiring
- helm/postgresql-ha-values.yaml.template: annotated values schema
with CHANGEME_ placeholders; includes pgpool-password fix from
RAIL-BS-WP-0003; notes on single-node vs ThreePhoenix scaling
- docs/postgresql-ha.md: connection strings, DB creation, password
rotation, pgpool-password critical note, HA failover test ref,
ThreePhoenix scaling path
To complete T01: fill in CHANGEME_ values, encrypt with sops -e -i,
then run make pg-deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 02:17:55 +01:00
else \
2026-08-15 14:43:44 +02:00
echo 'postgresql-ha retired: namespace $(NAMESPACE) is absent' ; \
feat(platform): T01 — standalone PostgreSQL HA chart scaffold
Lays out the S3 platform layer foundation for RAIL-PL-WP-0001 T01:
- .sops.yaml: age encryption policy (shared key, *.sops.yaml pattern)
- .gitignore: prevents accidental commit of decrypted values files
- Makefile: pg-deploy, pg-status, pg-pgpool-check, valkey-deploy,
valkey-status, backup targets with KUBECONFIG/HELM wiring
- helm/postgresql-ha-values.yaml.template: annotated values schema
with CHANGEME_ placeholders; includes pgpool-password fix from
RAIL-BS-WP-0003; notes on single-node vs ThreePhoenix scaling
- docs/postgresql-ha.md: connection strings, DB creation, password
rotation, pgpool-password critical note, HA failover test ref,
ThreePhoenix scaling path
To complete T01: fill in CHANGEME_ values, encrypt with sops -e -i,
then run make pg-deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 02:17:55 +01:00
fi
2026-08-15 14:43:44 +02:00
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
feat(platform): T01 — standalone PostgreSQL HA chart scaffold
Lays out the S3 platform layer foundation for RAIL-PL-WP-0001 T01:
- .sops.yaml: age encryption policy (shared key, *.sops.yaml pattern)
- .gitignore: prevents accidental commit of decrypted values files
- Makefile: pg-deploy, pg-status, pg-pgpool-check, valkey-deploy,
valkey-status, backup targets with KUBECONFIG/HELM wiring
- helm/postgresql-ha-values.yaml.template: annotated values schema
with CHANGEME_ placeholders; includes pgpool-password fix from
RAIL-BS-WP-0003; notes on single-node vs ThreePhoenix scaling
- docs/postgresql-ha.md: connection strings, DB creation, password
rotation, pgpool-password critical note, HA failover test ref,
ThreePhoenix scaling path
To complete T01: fill in CHANGEME_ values, encrypt with sops -e -i,
then run make pg-deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 02:17:55 +01:00
2026-08-15 14:43:44 +02:00
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
feat(platform): T01 — standalone PostgreSQL HA chart scaffold
Lays out the S3 platform layer foundation for RAIL-PL-WP-0001 T01:
- .sops.yaml: age encryption policy (shared key, *.sops.yaml pattern)
- .gitignore: prevents accidental commit of decrypted values files
- Makefile: pg-deploy, pg-status, pg-pgpool-check, valkey-deploy,
valkey-status, backup targets with KUBECONFIG/HELM wiring
- helm/postgresql-ha-values.yaml.template: annotated values schema
with CHANGEME_ placeholders; includes pgpool-password fix from
RAIL-BS-WP-0003; notes on single-node vs ThreePhoenix scaling
- docs/postgresql-ha.md: connection strings, DB creation, password
rotation, pgpool-password critical note, HA failover test ref,
ThreePhoenix scaling path
To complete T01: fill in CHANGEME_ values, encrypt with sops -e -i,
then run make pg-deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 02:17:55 +01:00
2026-08-15 14:43:44 +02:00
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
feat(platform): T01 — standalone PostgreSQL HA chart scaffold
Lays out the S3 platform layer foundation for RAIL-PL-WP-0001 T01:
- .sops.yaml: age encryption policy (shared key, *.sops.yaml pattern)
- .gitignore: prevents accidental commit of decrypted values files
- Makefile: pg-deploy, pg-status, pg-pgpool-check, valkey-deploy,
valkey-status, backup targets with KUBECONFIG/HELM wiring
- helm/postgresql-ha-values.yaml.template: annotated values schema
with CHANGEME_ placeholders; includes pgpool-password fix from
RAIL-BS-WP-0003; notes on single-node vs ThreePhoenix scaling
- docs/postgresql-ha.md: connection strings, DB creation, password
rotation, pgpool-password critical note, HA failover test ref,
ThreePhoenix scaling path
To complete T01: fill in CHANGEME_ values, encrypt with sops -e -i,
then run make pg-deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 02:17:55 +01:00
2026-05-18 11:53:59 +02:00
##@ 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
2026-06-19 20:28:16 +02:00
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)
2026-05-18 11:53:59 +02:00
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
2026-06-19 20:28:16 +02:00
$( MAKE) openbao-overlay-apply
2026-05-18 11:53:59 +02:00
2026-08-23 12:50:23 +02:00
.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
2026-05-18 11:53:59 +02:00
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
2026-05-23 13:59:58 +02:00
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
2026-06-18 01:06:43 +02:00
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
2026-06-01 22:46:14 +02:00
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)
2026-06-25 20:08:36 +02:00
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
2026-07-01 20:07:26 +02:00
openbao-configure-external-secrets-activity-core : ## Configure OpenBao policy/role for activity-core ESO lane
2026-07-08 00:04:59 +02:00
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
2026-07-01 20:07:26 +02:00
2026-07-07 14:30:02 +02:00
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
2026-06-27 22:48:24 +02:00
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)
2026-06-01 23:57:00 +02:00
openbao-validate-restore-evidence : ## Validate non-secret OpenBao restore-drill evidence JSON
OPENBAO_RESTORE_EVIDENCE = '$(OPENBAO_RESTORE_EVIDENCE)' \
scripts/openbao-validate-restore-evidence.sh
2026-06-02 00:08:17 +02:00
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
RPF-WP-0064-T04: native synthetic rehearsal; T02-T04 done
12 native tests against the pinned-tag OpenBao image with --network none and
generated material only: restored end to end plus wrong identity, ciphertext,
plaintext, share, missing objects (refused), source drift, timeout, operator
abort, parent loss and reaper deadline. Only the network-interface abort is
mocked (inspect wrapper). Make targets for offline tests, image pull,
rehearsal and receipt verification. T05/T06 blocking text names the exact
admission fields and the PINNED_IMAGE_DIGEST step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4384@bnt-lap001
Assistant-Session: 80d1f3da-7a91-4519-9737-9ba128b4da64
2026-10-23 20:49:40 +02:00
OPENBAO_REHEARSAL_IMAGE ?= quay.io/openbao/openbao:2.5.4
openbao-isolated-restore-test : ## RPF-WP-0064 offline tests: admission, v2 receipt verifier, launcher (no Docker)
PYTHONDONTWRITEBYTECODE = 1 python3 -m unittest tests.test_openbao_isolated_restore
openbao-isolated-restore-image : ## Pull the public OpenBao image used by the synthetic rehearsal (no custody)
docker pull -q $( OPENBAO_REHEARSAL_IMAGE)
openbao-isolated-restore-rehearsal : ## RPF-WP-0064-T04 native rehearsal: --network none containers, generated material only
PYTHONDONTWRITEBYTECODE = 1 python3 -m unittest -v tests.test_openbao_isolated_restore_native
openbao-verify-restore-receipt : ## Verify a v2 isolated-restore receipt (RECEIPT=/abs/path SOURCE_ROOT=clean checkout [NOT_AFTER=])
python3 scripts/openbao-isolated-restore/verify-restore-receipt.py --receipt '$(RECEIPT)' \
--source-root '$(or $(SOURCE_ROOT),$(CURDIR))' $( if $( NOT_AFTER) ,--not-after '$(NOT_AFTER)' )
.PHONY : openbao -isolated -restore -test openbao -isolated -restore -image openbao -isolated -restore -rehearsal openbao -verify -restore -receipt
2026-06-26 17:49:40 +02:00
##@ Credential broker
credential-grants-validate : ## Validate non-secret credential grant catalog
scripts/credential-grants-validate.py $( CREDENTIAL_GRANTS)
2026-06-27 22:57:21 +02:00
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)
2026-07-01 20:07:26 +02:00
credential-change-decision-templates : ## Render CCR approve/deny/needs-changes templates
scripts/credential-change.py decision-templates $( CREDENTIAL_CHANGE)
2026-06-27 23:30:29 +02:00
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)
2026-06-28 00:00:02 +02:00
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)
2026-09-10 08:15:41 +02:00
.PHONY : openbao -open -questions
openbao-open-questions : ## Attended read-only session for the open custody questions (SESSION_RECEIPT=<new path>)
@test -n " $( SESSION_RECEIPT) " || { echo "SESSION_RECEIPT=<new file path> is required" >& 2; exit 2; }
@test ! -e " $( SESSION_RECEIPT) " || { echo " SESSION_RECEIPT already exists: $( SESSION_RECEIPT) " >& 2; exit 2; }
env -u OPENBAO_TOKEN -u VAULT_TOKEN -u BAO_TOKEN \
scripts/openbao-attended-exec.py -- /usr/bin/python3 \
scripts/openbao_open_questions_session.py \
--questions $( SESSION_QUESTIONS) --receipt $( SESSION_RECEIPT)
2026-06-27 22:57:21 +02:00
credential-change-apply-plan : ## Render approved-only operator apply plan
scripts/credential-change.py apply-plan $( CREDENTIAL_CHANGE)
2026-06-28 00:21:02 +02:00
credential-change-operator-commands : ## Render approved-only non-secret OpenBao operator commands
scripts/credential-change.py operator-commands $( CREDENTIAL_CHANGE)
2026-07-01 20:07:26 +02:00
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)
2026-08-22 10:03:54 +02:00
agent-high-risk-boundary-check : ## Check policy against the versioned ops-warden high-risk path input
python3 scripts/agent_high_risk_boundary.py --json
2026-07-01 20:07:26 +02:00
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)
2026-06-27 00:06:03 +02:00
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
2026-06-27 00:29:53 +02:00
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'
2026-06-27 00:06:03 +02:00
2026-06-27 22:57:21 +02:00
credential-change-tests : ## Run credential change request unit tests
python3 -m unittest discover -s tests -p 'test_credential_change.py'
2026-06-27 00:06:03 +02:00
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) \
2026-06-27 00:29:53 +02:00
scripts/credential.py $( CREDENTIAL_HELPER_GLOBAL_ARGS) exec \
--grant ops-warden/warden-sign --purpose ops-warden-production-sign-smoke \
$( CREDENTIAL_HELPER_ARGS) -- \
2026-07-01 23:10:38 +02:00
$( CREDENTIAL_HELPER_CHILD_ENV) \
2026-06-27 00:06:03 +02:00
SMOKE_VAULT = 1 /home/worsch/ops-warden/scripts/policy_gate_production_smoke.sh
2026-06-19 20:28:16 +02:00
##@ 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/<repo>.repository.sops.yaml" ; exit 1)
sops -d $( ARGOCD_REPOSITORY_SECRET) | $( KUBECTL) apply -f -
2026-10-20 00:04:33 +02:00
argocd-pin-drift : ## Read-only: compare railiance01 child Application pins in Git with live (run before any root sync)
python3 scripts/argocd_child_pin_drift.py
2026-06-19 20:28:16 +02:00
argocd-status : ## Show Railiance ArgoCD projects, root app, and registered repos
$( KUBECTL) get appprojects.argoproj.io -n $( ARGOCD_NAMESPACE) \
2026-06-25 20:08:36 +02:00
railiance-bootstrap railiance-tenants railiance-platform-addons
2026-06-19 20:28:16 +02:00
$( KUBECTL) get applications.argoproj.io -n $( ARGOCD_NAMESPACE) \
2026-06-25 20:08:36 +02:00
railiance-apps-root external-secrets openbao-secretstore issue-core
2026-06-19 20:28:16 +02:00
$( KUBECTL) get secrets -n $( ARGOCD_NAMESPACE) \
-l argocd.argoproj.io/secret-type= repository
feat(platform): T01 — standalone PostgreSQL HA chart scaffold
Lays out the S3 platform layer foundation for RAIL-PL-WP-0001 T01:
- .sops.yaml: age encryption policy (shared key, *.sops.yaml pattern)
- .gitignore: prevents accidental commit of decrypted values files
- Makefile: pg-deploy, pg-status, pg-pgpool-check, valkey-deploy,
valkey-status, backup targets with KUBECONFIG/HELM wiring
- helm/postgresql-ha-values.yaml.template: annotated values schema
with CHANGEME_ placeholders; includes pgpool-password fix from
RAIL-BS-WP-0003; notes on single-node vs ThreePhoenix scaling
- docs/postgresql-ha.md: connection strings, DB creation, password
rotation, pgpool-password critical note, HA failover test ref,
ThreePhoenix scaling path
To complete T01: fill in CHANGEME_ values, encrypt with sops -e -i,
then run make pg-deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 02:17:55 +01:00
##@ Backup
Pin apps-pg targets to railiance01 by cluster identity; seed RPF-WP-0020
Two reachable clusters each carry a CNPG Cluster named apps-pg in a
namespace named databases. KUBECONFIG is an environment variable, so the
Makefile ?= default never applied, and RAILIANCE01_KUBECONFIG pointed at
config-hosteurope - a different cluster. Had the environment pointed at the
other reachable cluster instead of an unauthorized one, make apps-pg-deploy
would have applied RPF-WP-0019 connection limits, role timeouts and backup
config to the wrong cluster and reported success. The Unauthorized error was
the only thing that prevented it.
Filename selection cannot protect against this: both kubeconfigs resolve to
a 127.0.0.1 tunnel port and the environment wins either way. railiance01-guard
pins identity instead, comparing the live kube-system namespace UID against
RAILIANCE01_CLUSTER_UID, and fails closed on mismatch or unreachability. It
gates apps-pg deploy, backup-deploy, overflow-dry-run, status and shell.
Verified refusing on the wrong cluster, refusing when unreachable, and
passing on railiance01. Not global: db-status legitimately targets the other
cluster for gitea-db.
RPF-WP-0019 blocker note corrected - the cluster was never unreachable, our
wiring was wrong.
RPF-WP-0020 seeded for the pre-existing CCR test failure, which is two
unrelated problems: CCR-2026-0010 is an active lane missing its whole
openbao.auth block, and CCR-2026-0011 is an honest in-flight draft the suite
has no way to express.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 15:18:37 +02:00
RAILIANCE01_KUBECONFIG ?= $( HOME) /.kube/config-railiance01
2026-07-07 17:16:30 +02:00
feat(platform): T01 — standalone PostgreSQL HA chart scaffold
Lays out the S3 platform layer foundation for RAIL-PL-WP-0001 T01:
- .sops.yaml: age encryption policy (shared key, *.sops.yaml pattern)
- .gitignore: prevents accidental commit of decrypted values files
- Makefile: pg-deploy, pg-status, pg-pgpool-check, valkey-deploy,
valkey-status, backup targets with KUBECONFIG/HELM wiring
- helm/postgresql-ha-values.yaml.template: annotated values schema
with CHANGEME_ placeholders; includes pgpool-password fix from
RAIL-BS-WP-0003; notes on single-node vs ThreePhoenix scaling
- docs/postgresql-ha.md: connection strings, DB creation, password
rotation, pgpool-password critical note, HA failover test ref,
ThreePhoenix scaling path
To complete T01: fill in CHANGEME_ values, encrypt with sops -e -i,
then run make pg-deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 02:17:55 +01:00
backup : ## Backup platform services (PostgreSQL logical dump) — age-encrypted to Nextcloud
2026-07-07 17:16:30 +02:00
@test -x tools/cmd/railiance-backup || { echo "tools/cmd/railiance-backup not installed; use forgejo-backup for Forgejo." >& 2; exit 1; }
feat(platform): T01 — standalone PostgreSQL HA chart scaffold
Lays out the S3 platform layer foundation for RAIL-PL-WP-0001 T01:
- .sops.yaml: age encryption policy (shared key, *.sops.yaml pattern)
- .gitignore: prevents accidental commit of decrypted values files
- Makefile: pg-deploy, pg-status, pg-pgpool-check, valkey-deploy,
valkey-status, backup targets with KUBECONFIG/HELM wiring
- helm/postgresql-ha-values.yaml.template: annotated values schema
with CHANGEME_ placeholders; includes pgpool-password fix from
RAIL-BS-WP-0003; notes on single-node vs ThreePhoenix scaling
- docs/postgresql-ha.md: connection strings, DB creation, password
rotation, pgpool-password critical note, HA failover test ref,
ThreePhoenix scaling path
To complete T01: fill in CHANGEME_ values, encrypt with sops -e -i,
then run make pg-deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 02:17:55 +01:00
sudo tools/cmd/railiance-backup
2026-07-07 17:16:30 +02:00
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)"
2026-07-12 11:35:04 +02:00
forgejo-package-prune-dry-run : ## List Forgejo package versions beyond retention depth (no deletes)
tools/cmd/forgejo-package-prune
2026-09-05 01:53:24 +02:00
LIVE_IMAGES_OUTPUT ?= $( HOME) /.local/state/railiance-platform/live-images/all.txt
LIVE_IMAGES_SOURCES ?=
.PHONY : live -images -refresh
live-images-refresh : ## Atomically preserve and merge image exports outside the checkout
@test -n " $( LIVE_IMAGES_SOURCES) " || { echo 'set LIVE_IMAGES_SOURCES to nonempty cluster export files' >& 2; exit 2; }
python3 scripts/refresh_live_images.py --output " $( LIVE_IMAGES_OUTPUT) " \
$( foreach source,$( LIVE_IMAGES_SOURCES) ,--source " $( source ) " )
2026-07-12 11:35:04 +02:00
forgejo-package-prune : ## Prune Forgejo packages — keep newest 3 versions per package
tools/cmd/forgejo-package-prune --apply
2026-08-14 00:39:06 +02:00
##@ 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
2026-08-14 02:05:25 +02:00
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
feat(platform): T01 — standalone PostgreSQL HA chart scaffold
Lays out the S3 platform layer foundation for RAIL-PL-WP-0001 T01:
- .sops.yaml: age encryption policy (shared key, *.sops.yaml pattern)
- .gitignore: prevents accidental commit of decrypted values files
- Makefile: pg-deploy, pg-status, pg-pgpool-check, valkey-deploy,
valkey-status, backup targets with KUBECONFIG/HELM wiring
- helm/postgresql-ha-values.yaml.template: annotated values schema
with CHANGEME_ placeholders; includes pgpool-password fix from
RAIL-BS-WP-0003; notes on single-node vs ThreePhoenix scaling
- docs/postgresql-ha.md: connection strings, DB creation, password
rotation, pgpool-password critical note, HA failover test ref,
ThreePhoenix scaling path
To complete T01: fill in CHANGEME_ values, encrypt with sops -e -i,
then run make pg-deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 02:17:55 +01:00
##@ Help
2026-03-10 00:36:06 +01:00
help : ## Show this help
feat(platform): T01 — standalone PostgreSQL HA chart scaffold
Lays out the S3 platform layer foundation for RAIL-PL-WP-0001 T01:
- .sops.yaml: age encryption policy (shared key, *.sops.yaml pattern)
- .gitignore: prevents accidental commit of decrypted values files
- Makefile: pg-deploy, pg-status, pg-pgpool-check, valkey-deploy,
valkey-status, backup targets with KUBECONFIG/HELM wiring
- helm/postgresql-ha-values.yaml.template: annotated values schema
with CHANGEME_ placeholders; includes pgpool-password fix from
RAIL-BS-WP-0003; notes on single-node vs ThreePhoenix scaling
- docs/postgresql-ha.md: connection strings, DB creation, password
rotation, pgpool-password critical note, HA failover test ref,
ThreePhoenix scaling path
To complete T01: fill in CHANGEME_ values, encrypt with sops -e -i,
then run make pg-deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 02:17:55 +01:00
@awk ' BEGIN { FS = ":.*##" ; printf "\nUsage:\n make \033[36m<target>\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)
2026-08-22 12:59:38 +02:00
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
2026-08-22 13:16:13 +02:00
audit-database-lease-recovery-preflight : ## Read-only gate for the restart-free database lease exercise
2026-08-22 14:00:18 +02:00
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) "
2026-10-14 14:23:46 +02:00
ATTENDED_EXEC = python3 scripts/openbao-attended-exec.py --
AUDIT_T02_NEED_APPROVAL = @test -n " $( AUDIT_DATABASE_LEASE_APPROVAL) " || { echo 'set AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json' >& 2; exit 2; }
AUDIT_T02_NEED_DRIVER = @test -n " $( AUDIT_DATABASE_LEASE_DRIVER) " || { echo 'set AUDIT_DATABASE_LEASE_DRIVER=/path/to/approved-load-driver' >& 2; exit 2; }
2026-10-14 12:49:40 +02:00
AUDIT_SYNTHETIC_SENDER_NEED_CONFIRM = @test -n " $( AUDIT_SYNTHETIC_SENDER_CONFIRM) " || { echo 'set AUDIT_SYNTHETIC_SENDER_CONFIRM to the confirm string printed by preflight' >& 2; exit 2; }
2026-10-14 14:23:46 +02:00
AUDIT_T02_NEED_BEARER = @test -n " $( AUDIT_T02_TOKEN_FILE) " || { echo 'set AUDIT_T02_TOKEN_FILE=/abs/path/outside/git (0700 dir)' >& 2; exit 2; }
audit-t02-attended-window : ## One contained login: project, port-forward, T02 exercise, evidence, cleanup
$( AUDIT_T02_NEED_APPROVAL)
$( AUDIT_T02_NEED_DRIVER)
$( AUDIT_SYNTHETIC_SENDER_NEED_CONFIRM)
$( AUDIT_T02_NEED_BEARER)
@test -n " $( AUDIT_T02_EVIDENCE_DIR) " || { echo 'set AUDIT_T02_EVIDENCE_DIR=/abs/dir/outside/git (0700)' >& 2; exit 2; }
$( ATTENDED_EXEC) scripts/t02-attended-window.py \
--approval " $( abspath $( AUDIT_DATABASE_LEASE_APPROVAL) ) " \
--load-driver " $( abspath $( AUDIT_DATABASE_LEASE_DRIVER) ) " \
--bearer-file " $( AUDIT_T02_TOKEN_FILE) " --evidence-dir " $( AUDIT_T02_EVIDENCE_DIR) " \
2026-10-14 20:30:11 +02:00
--confirm " $( AUDIT_SYNTHETIC_SENDER_CONFIRM) " $( if $( AUDIT_T02_PORT) ,--port $( AUDIT_T02_PORT) ) \
$( if $( AUDIT_T02_MODE) ,--mode " $( AUDIT_T02_MODE) " )
2026-10-14 14:23:46 +02:00
audit-database-lease-recovery-exercise : ## Exercise step alone, inside its own contained login (prefer audit-t02-attended-window)
$( AUDIT_T02_NEED_APPROVAL)
$( AUDIT_T02_NEED_DRIVER)
@test -n " $( AUDIT_T02_RESULT_FILE) " || { echo 'set AUDIT_T02_RESULT_FILE=/abs/new/file outside Git (the contained child must stay silent)' >& 2; exit 2; }
$( ATTENDED_EXEC) scripts/audit-core-database-lease-recovery.py exercise --contained --result-file " $( AUDIT_T02_RESULT_FILE) " \
--approval " $( abspath $( AUDIT_DATABASE_LEASE_APPROVAL) ) " \
--load-driver " $( abspath $( AUDIT_DATABASE_LEASE_DRIVER) ) " \
--confirm RAILIANCE-WP-0024-T02:attended
2026-10-14 12:49:40 +02:00
2026-10-14 14:23:46 +02:00
audit-synthetic-sender-test : ## Unit tests for the synthetic sender lane and T02 window (no network)
PYTHONDONTWRITEBYTECODE = 1 python3 -m unittest tests.test_audit_core_synthetic_sender_lane tests.test_t02_attended_window
2026-10-14 12:49:40 +02:00
2026-10-14 14:23:46 +02:00
audit-synthetic-sender-preflight : ## Read-only T02 sender gate; cluster checks only (registry metadata needs --contained)
$( AUDIT_T02_NEED_APPROVAL)
python3 scripts/audit_core_synthetic_sender_lane.py preflight --approval " $( AUDIT_DATABASE_LEASE_APPROVAL) " \
2026-10-14 12:49:40 +02:00
$( if $( AUDIT_T02_TOKEN_FILE) ,--bearer-file " $( AUDIT_T02_TOKEN_FILE) " ) $( AUDIT_SYNTHETIC_SENDER_PREFLIGHT_ARGS)
2026-10-14 14:23:46 +02:00
audit-synthetic-sender-project : ## Projection step alone, inside its own contained login (prefer audit-t02-attended-window)
$( AUDIT_T02_NEED_APPROVAL)
2026-10-14 12:49:40 +02:00
$( AUDIT_SYNTHETIC_SENDER_NEED_CONFIRM)
2026-10-14 14:23:46 +02:00
$( AUDIT_T02_NEED_BEARER)
@test -n " $( AUDIT_T02_RESULT_FILE) " || { echo 'set AUDIT_T02_RESULT_FILE=/abs/new/file outside Git (the contained child must stay silent)' >& 2; exit 2; }
$( ATTENDED_EXEC) scripts/audit_core_synthetic_sender_lane.py project --contained --result-file " $( AUDIT_T02_RESULT_FILE) " \
--approval " $( abspath $( AUDIT_DATABASE_LEASE_APPROVAL) ) " \
2026-10-14 12:49:40 +02:00
--bearer-file " $( AUDIT_T02_TOKEN_FILE) " --confirm " $( AUDIT_SYNTHETIC_SENDER_CONFIRM) "
2026-10-14 14:23:46 +02:00
audit-synthetic-sender-cleanup : ## Idempotent cleanup inside a contained login (evidence or abort reason required)
$( AUDIT_T02_NEED_APPROVAL)
2026-10-14 12:49:40 +02:00
$( AUDIT_SYNTHETIC_SENDER_NEED_CONFIRM)
@test -n " $( AUDIT_T02_EVIDENCE) $( AUDIT_SYNTHETIC_SENDER_ABORT_REASON) " || { echo 'set AUDIT_T02_EVIDENCE=/path/to/exercise-result.json or AUDIT_SYNTHETIC_SENDER_ABORT_REASON=...' >& 2; exit 2; }
2026-10-14 14:23:46 +02:00
@test -n " $( AUDIT_T02_RESULT_FILE) " || { echo 'set AUDIT_T02_RESULT_FILE=/abs/new/file outside Git (the contained child must stay silent)' >& 2; exit 2; }
$( ATTENDED_EXEC) scripts/audit_core_synthetic_sender_lane.py cleanup --contained --result-file " $( AUDIT_T02_RESULT_FILE) " \
--approval " $( abspath $( AUDIT_DATABASE_LEASE_APPROVAL) ) " \
2026-10-14 12:49:40 +02:00
--confirm " $( AUDIT_SYNTHETIC_SENDER_CONFIRM) " \
$( if $( AUDIT_T02_TOKEN_FILE) ,--bearer-file " $( AUDIT_T02_TOKEN_FILE) " ) \
2026-10-14 14:23:46 +02:00
$( if $( AUDIT_T02_EVIDENCE) ,--exercise-evidence " $( abspath $( AUDIT_T02_EVIDENCE) ) " ) \
2026-10-14 12:49:40 +02:00
$( if $( AUDIT_SYNTHETIC_SENDER_ABORT_REASON) ,--abort-reason " $( AUDIT_SYNTHETIC_SENDER_ABORT_REASON) " )
2026-08-22 13:16:13 +02:00
railiance01-reboot-preflight : ## Read-only gate for the coordinated single-node reboot
2026-08-22 14:00:18 +02:00
python3 scripts/audit-core-recovery-preflight.py node-reboot $( RAILIANCE01_REBOOT_PREFLIGHT_ARGS)
2026-08-22 13:16:13 +02:00
2026-10-14 19:49:02 +02:00
.PHONY : openbao -reboot -snapshot openbao -reboot -snapshot -test
openbao-reboot-snapshot : ## Attended Raft snapshot -> age -> verify -> offsite; silent child writes SNAPSHOT_RECEIPT
@test -n " $( SNAPSHOT_SCENARIO_ID) " || { echo 'set SNAPSHOT_SCENARIO_ID=<scenario id, e.g. RPF-WP-0015-T03-...>' >& 2; exit 2; }
@test -n " $( SNAPSHOT_RECEIPT) " || { echo 'set SNAPSHOT_RECEIPT=/abs/new/file outside Git (the contained child must stay silent)' >& 2; exit 2; }
@test ! -e " $( SNAPSHOT_RECEIPT) " || { echo 'SNAPSHOT_RECEIPT already exists' >& 2; exit 2; }
@test -n " $( SNAPSHOT_CUSTODIAN_ESCROW_REF) " || { echo 'set SNAPSHOT_CUSTODIAN_ESCROW_REF=<where a decryption key is held outside OpenBao>' >& 2; exit 2; }
env -u OPENBAO_TOKEN -u VAULT_TOKEN -u BAO_TOKEN \
$( ATTENDED_EXEC) scripts/openbao-reboot-snapshot.py \
--scenario-id " $( SNAPSHOT_SCENARIO_ID) " --receipt-file " $( abspath $( SNAPSHOT_RECEIPT) ) " \
$( if $( SNAPSHOT_PRIVATE_DIR) ,--private-dir " $( abspath $( SNAPSHOT_PRIVATE_DIR) ) " ) \
--custodian-escrow-ref " $( SNAPSHOT_CUSTODIAN_ESCROW_REF) " \
$( if $( SNAPSHOT_CUSTODIAN_RECIPIENT) ,--custodian-recipient " $( SNAPSHOT_CUSTODIAN_RECIPIENT) " )
openbao-reboot-snapshot-test : ## Offline unit tests for the attended snapshot lane (no OpenBao, no network)
PYTHONDONTWRITEBYTECODE = 1 python3 -m unittest tests.test_openbao_reboot_snapshot
2026-08-22 14:15:02 +02:00
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
2026-08-22 16:17:03 +02:00
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) "
2026-10-14 14:23:46 +02:00
.PHONY : audit -t 02-attended -window audit -synthetic -sender -test audit -synthetic -sender -preflight audit -synthetic -sender -project audit -synthetic -sender -cleanup audit -whitehat -e 2-preflight audit -whitehat -e 2-project audit -whitehat -e 2-cleanup audit -database -lease -recovery -preflight audit -database -lease -approval -check audit -database -lease -recovery -exercise railiance 01-reboot -preflight wp 0024-owner -show wp 0024-owner -verify wp 0024-owner -status wp 0024-t 02-driver -show wp 0024-t 02-driver -status wp 0024-t 02-driver -register apps -pg -isolation -probe
2026-08-22 10:03:54 +02:00
.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
2026-09-05 11:43:55 +02:00
##@ S3 service assurance (read-only)
.PHONY : assurance -admission assurance -capture assurance -check
ASSURANCE_OBSERVATION ?= /tmp/railiance-platform-observation.json
assurance-admission : ## Verify owner-native admission and disclosure baseline
python3 scripts/service_assurance.py admission --check assurance/admission-baseline.json
assurance-capture : ## Capture selected status metadata from the pinned railiance01 cluster
python3 scripts/capture_service_observation.py --output " $( ASSURANCE_OBSERVATION) "
assurance-check : ## Classify freshness/failure; nonzero means incomplete or invalid evidence
python3 scripts/service_assurance.py evaluate " $( ASSURANCE_OBSERVATION) "
2026-09-13 16:25:33 +02:00
.PHONY : keycape -factor -tests
keycape-factor-tests : ## Validate factor custody, scope, renewal and config preservation offline
PYTHONDONTWRITEBYTECODE = 1 python3 -m unittest discover -s tests -p 'test_keycape_factor_*.py'
.PHONY : keycape -factor -live -acceptance
keycape-factor-live-acceptance : ## Verify native scope and rotation (creates bounded proof/renewal Jobs)
PYTHONDONTWRITEBYTECODE = 1 python3 scripts/keycape_factor_acceptance.py
2026-10-19 13:30:55 +02:00
# CUST-WP-0079-T05: staged-source generation; hook uses the isolated recipe.
.PHONY : generated -work -records generated -work -records -check
generated-work-records :
$( MAKE) --no-print-directory -f .generated-work-records.mk generated-work-records
generated-work-records-check :
$( MAKE) --no-print-directory -f .generated-work-records.mk generated-work-records-check
2026-10-28 09:52:15 +01:00
##@ Service-identity metadata (NK-WP-0054-T04, RPF-WP-0067)
.PHONY : service -identity -metadata -tests service -identity -metadata -openbao service -identity -metadata -postgresql -sql
service-identity-metadata-tests : ## Offline fixture tests for the value-safe identity metadata exporter
PYTHONDONTWRITEBYTECODE = 1 python3 -m unittest -v tests.test_service_identity_metadata_export
service-identity-metadata-openbao : ## Read-only OpenBao auth-role/policy export via the operator's bao login (OUT=file)
python3 scripts/service_identity_metadata_export.py --output " $$ {OUT:-openbao-identity-metadata.json} " openbao
service-identity-metadata-postgresql-sql : ## Print the read-only pg_roles/pg_auth_members SELECT
@python3 scripts/service_identity_metadata_export.py postgresql-sql