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."