tenant-engine/railiance/app.toml
tegwick 6644ad8402
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 50s
Onboard tenant-engine to the staged-promotion contract
TEN-WP-0008. railiance/app.toml declares criticality=high, empty secrets,
isolated canary, and the live PostgreSQL digest as previous_stable.
Manifests render through kustomize (deploy/ and deploy/canary/). Stage 1
passed. Stage 2/3 Helm-only CLI gap requested as RAIL-BS-IN-0001 rather
than a dummy chart.

Assistant: grok
Assistant-Session: 01a04cea-e5e8-7081-a0fc-808ebbc35fa9
2026-08-29 14:51:27 +02:00

242 lines
7.5 KiB
TOML

schema_version = "railiance.app.v1"
# TEN-WP-0008. Modelled on qonto-assistant (small headless service) for
# stage commands, and on flex-auth for an empty secrets list and isolated
# canary. Stage 2/3 *commands* are kubectl against this repo's kustomize
# overlays. `bin/railiance deploy|promote --apply` currently requires a Helm
# chart the schema does not; that gap is requested of railiance-bootstrap
# rather than papered over with a dummy chart. See railiance/README.md.
[app]
id = "tenant-engine"
name = "tenant-engine"
repo = "tenant-engine"
owner = "infotech"
criticality = "high"
description = """
Canonical PIP for tenant-as-an-entity facts. flex-auth consults it live on
high-stakes decisions and key-cape consults it at token issuance. It holds
no application secrets and is not internet-facing, but an outage makes the
PDP fail closed fleet-wide. high, not critical: the PDP itself is high,
this is a data source whose unavailability is deny rather than an
unauthorized allow, and it does not hold a credential of its own.
"""
[source]
revision = "git:main"
artifact = "image"
digest_policy = "required"
[rollback]
strategy = "image-digest"
command = "kubectl -n tenant-engine rollout undo deploy/tenant-engine"
verification = """
make verify-pin agrees repo, spec, and running pod; GET /health returns
200 with store_backend=postgresql; lifecycle routes still present.
Do not roll back to sha256:7dc9d7bf1fcbb3f3ad7035366f4cd44038322995a126b4423142e446ca5c9cd5
(SQLite/PVC) after any PostgreSQL write. The TEN-WP-0007 ladder still
holds for older images: rolling past a pre-lifecycle digest removes
GET/PATCH/retire/reactivate that user-engine depends on.
"""
[[platform.dependencies]]
name = "platform-pg"
kind = "postgres"
required = true
stage = "stage2"
evidence = "rapp-postgres consumer tenant-engine can reach platform-pg:5432; /health reports store_backend=postgresql."
[[platform.dependencies]]
name = "flex-auth-tenant-engine"
kind = "identity"
required = true
stage = "stage2"
evidence = "http://flex-auth-tenant-engine.flex-auth.svc.cluster.local:8080/healthz returns 200."
[[platform.dependencies]]
name = "forgejo-oci"
kind = "other"
required = true
stage = "stage2"
evidence = "Image forgejo.coulomb.social/coulomb/tenant-engine is pullable by digest."
[[platform.dependencies]]
name = "railiance01-k3s"
kind = "network"
required = true
stage = "stage2"
evidence = "kubectl -n tenant-engine get ns reports Active on railiance01."
[secrets]
# Runtime secret custody is OpenBao's. Postgres URL and flex-auth caller
# token are projected files owned by those platforms, not entries this
# repo catalogues. An empty list is the correct answer (TEN-WP-0008-T01).
references = []
[[observability.health_endpoints]]
name = "local-health"
url = "http://127.0.0.1:8090/health"
stage = "stage1"
expected_status = 200
[[observability.health_endpoints]]
name = "cluster-health"
url = "http://tenant-engine.tenant-engine.svc.cluster.local:8090/health"
stage = "stage2"
expected_status = 200
[[observability.health_endpoints]]
name = "canary-health"
url = "http://tenant-engine-canary.tenant-engine.svc.cluster.local:8090/health"
stage = "stage2"
expected_status = 200
[stages.stage1]
enabled = true
namespace = "local"
release = "tenant-engine-local"
commands = [
"make test",
"make validate-app-toml",
"kubectl kustomize deploy",
"kubectl kustomize deploy/canary",
]
checks = ["unit-tests", "contract-schema", "kustomize-production", "kustomize-canary"]
evidence = ["pytest output", "app.toml schema validation", "kustomize render of deploy/ and deploy/canary"]
requires_approval = false
[stages.stage2]
enabled = true
namespace = "tenant-engine"
release = "tenant-engine-canary"
commands = [
"kubectl apply -k deploy/canary --dry-run=server",
"kubectl apply -k deploy/canary",
]
checks = ["server-dry-run", "canary-ready", "canary-health", "operator-approval"]
evidence = ["server-side dry-run of isolated canary", "deploy/tenant-engine-canary Available", "canary /health 200", "State Hub approval id"]
requires_approval = true
canary_mode = "isolated"
observation_minutes = 15
[stages.stage3]
enabled = true
namespace = "tenant-engine"
release = "tenant-engine"
commands = [
"kubectl apply -k deploy --dry-run=server",
"kubectl apply -k deploy",
]
checks = ["stage2-accepted", "rollback-target", "cluster-health", "operator-approval"]
evidence = ["promotion commit", "new stable digest in previous_stable and deploy/base/tenant-engine.yaml", "post-promotion make verify-pin"]
requires_approval = true
promotion_mode = "release-replace"
# TEN-WP-0008-T03: the contract's recorded stable, not only the manifest pin.
# Current production (TEN-WP-0009 PostgreSQL runtime), CI-built from d2a7fe3.
previous_stable = "tenant-engine@sha256:a8e8086ffc5b772c1391b166f5e1884b90f7d327b152c205eceae129df555c24"
[[checks]]
id = "unit-tests"
type = "command"
stage = "stage1"
description = "Run repository unit tests."
required = true
run = "make test"
timeout_seconds = 600
[[checks]]
id = "contract-schema"
type = "command"
stage = "stage1"
description = "Validate railiance/app.toml against railiance.app.v1."
required = true
run = "make validate-app-toml"
timeout_seconds = 60
[[checks]]
id = "kustomize-production"
type = "command"
stage = "stage1"
description = "Render the production kustomization locally, no cluster contact."
required = true
run = "kubectl kustomize deploy"
[[checks]]
id = "kustomize-canary"
type = "command"
stage = "stage1"
description = "Render the isolated canary kustomization locally, no cluster contact."
required = true
run = "kubectl kustomize deploy/canary"
[[checks]]
id = "local-health"
type = "http"
stage = "stage1"
description = "Confirm a locally served /health when one is running."
required = false
url = "http://127.0.0.1:8090/health"
expected_status = 200
timeout_seconds = 10
[[checks]]
id = "server-dry-run"
type = "command"
stage = "stage2"
description = "Server-side dry run of the isolated canary overlay before apply."
required = true
run = "kubectl apply -k deploy/canary --dry-run=server"
[[checks]]
id = "canary-ready"
type = "kubernetes"
stage = "stage2"
description = "Isolated canary Deployment reaches Available."
required = true
namespace = "tenant-engine"
resource = "deploy/tenant-engine-canary"
condition = "Available"
[[checks]]
id = "canary-health"
type = "http"
stage = "stage2"
description = "Isolated canary Service /health returns 200."
required = true
url = "http://tenant-engine-canary.tenant-engine.svc.cluster.local:8090/health"
expected_status = 200
timeout_seconds = 10
[[checks]]
id = "cluster-health"
type = "http"
stage = "stage2"
description = "Production Service /health returns 200."
required = true
url = "http://tenant-engine.tenant-engine.svc.cluster.local:8090/health"
expected_status = 200
timeout_seconds = 10
[[checks]]
id = "operator-approval"
type = "manual"
stage = "stage2"
description = "Human approval recorded before the isolated canary is applied and before the production pin moves."
required = true
evidence_required = "State Hub approval note id, candidate digest, rollback target (previous_stable)."
[[checks]]
id = "stage2-accepted"
type = "manual"
stage = "stage3"
description = "Stage 2 gates passed for the same candidate artifact."
required = true
evidence_required = "State Hub Stage 2 acceptance progress id."
[[checks]]
id = "rollback-target"
type = "manual"
stage = "stage3"
description = "Previous stable digest is recorded in previous_stable before promotion."
required = true
evidence_required = "tenant-engine image digest currently in previous_stable and deploy/base/tenant-engine.yaml."