diff --git a/.github/workflows/self-test.yml b/.github/workflows/self-test.yml index 9c7af3c5f..a4238f0f6 100644 --- a/.github/workflows/self-test.yml +++ b/.github/workflows/self-test.yml @@ -54,6 +54,17 @@ jobs: - name: Run tests/*.sh and scripts/tests/*.sh run: bash scripts/run-shell-test-suite.sh + # KYAML ratchet (YAML-POLICY Y-3, §5 step 5, standards#1024): every file + # already converted must stay KYAML. A file joins this list in the PR that + # converts it and never leaves it. Workflows are not listed here (step 6, + # standards#1025), and neither are files read by a third-party bot + # (Dependabot, FUNDING, issue forms, GitLab CI, Copilot). + - name: Converted YAML stays KYAML + run: | + set -euo pipefail + bash scripts/kyaml-format.sh --check \ + _shared/container/.gatekeeper.yaml + # Not part of the offline suite: this one needs real git history, so it # cannot live in scripts/tests/*.sh where a contributor runs it on a # shallow or detached tree. On a pull request the comparison is the base diff --git a/3-practice/YAML-POLICY.adoc b/3-practice/YAML-POLICY.adoc index 5664078fd..490d9cc95 100644 --- a/3-practice/YAML-POLICY.adoc +++ b/3-practice/YAML-POLICY.adoc @@ -262,10 +262,17 @@ These issues are now booked, in this dependency order: `scripts/kyaml-format.sh [--check] FILE...`. It runs `yq -o kyaml` verbatim, the one rewriter step 2's proof covers, and it refuses (exit 2) a file that does not parse, has no final newline, or would change its data. - *Not yet a gate:* `gh actions-lock` v0.1.6 does not see a `uses:` inside a - KYAML workflow. A planted unlocked SHA stayed `valid: true`, while the same - plant in block YAML went `stale`. So workflows stay out of scope until that - tool reads flow syntax (see step 6). + It is a gate for converted non-workflow files: the Self Test workflow runs + `--check` on every file step 5 has converted (a ratchet list). + _Correction, 2026-10-01:_ an earlier version of this step said that + `gh actions-lock` v0.1.6 cannot see a `uses:` inside a KYAML workflow. That + is false. When the same ref was planted in each syntax, the result was the + same: a tag ref fails `--verify-local` in both, and a bare SHA passes in both + (the tool accepts any bare SHA as already pinned). Fix mode also left a + KYAML workflow byte-identical and kept its lock entries. The gap that + remains has nothing to do with syntax: the required + `.githooks/validate-actions-lock.sh` checks lock membership across the whole + file, not per workflow (standards#1025). * Step 4 — `hyperpolymath/standards#1023` — owner ruling on scope. * Step 5 — `hyperpolymath/standards#1024` — migrate non-bot estate YAML. * Step 6 — `hyperpolymath/standards#1025` — workflows, if step 4 rules them in. @@ -338,4 +345,17 @@ ruling and §5 explicitly forbids inferring scope from the probe. | §5 step 1 discharged: GitHub Actions proven to parse KYAML (standards#1020, closed). Steps 2–6 booked as #1021–#1025 in dependency order and cross-referenced from §5. + +| 2026-10-01 +| Owner ruling D280 +| §5 step 4 decided (standards#1023, closed): KYAML everywhere, workflows + included. + +| 2026-10-01 +| Correction + step 5 batch 1 +| Step 3: the "gh actions-lock is blind to KYAML" rationale is withdrawn + (refuted by a matched control). The formatter now gates converted files in + Self Test. Step 5 batch 1 in this repo: `_shared/container/.gatekeeper.yaml`. + The remaining 73 non-workflow YAML files here are bot-read, vendored + satellite copies, or the proof's own fixtures. |=== diff --git a/_shared/container/.gatekeeper.yaml b/_shared/container/.gatekeeper.yaml index 4aac671d0..46afaa1ac 100644 --- a/_shared/container/.gatekeeper.yaml +++ b/_shared/container/.gatekeeper.yaml @@ -9,114 +9,149 @@ # # See: stapeln/container-stack/svalinn/ -version: "1.0" - -# ============================================================================ -# Authentication -# ============================================================================ -# -# Define which endpoints require authentication and at what level. - -auth: - # Public endpoints — no authentication required. - # Health and readiness probes must always be public so that - # orchestrators (selur, Podman, k8s) can check service status. - public: - - path: "/health" - methods: ["GET"] - - path: "/ready" - methods: ["GET"] - - path: "/metrics" - methods: ["GET"] - - # Endpoints requiring JWT or OAuth2 authentication. - # Svalinn validates the token before forwarding the request. - authenticated: - - path: "/api/v1/*" - methods: ["GET", "POST", "PUT", "DELETE"] - -# ============================================================================ -# Rate Limiting -# ============================================================================ -# -# Protects backend services from overload. Values here are moderate -# defaults — adjust based on your service capacity. - -rate_limits: - # Global limit: applied to all authenticated clients. - global: - requests_per_second: 500 - burst: 1000 - - # Write operations: stricter limit to protect data stores. - writes: - paths: ["/api/v1/*"] - methods: ["POST", "PUT", "DELETE"] - requests_per_second: 100 - burst: 200 - -# ============================================================================ -# Container Trust -# ============================================================================ -# -# Svalinn verifies that all .ctp bundles in the stack are signed by -# trusted keys and carry the required attestations. - -trust: - # Only accept .ctp bundles signed by these keys. - trusted_signers: - - key_id: "{{SERVICE_NAME}}-release" - algorithm: "Ed25519" - public_key_file: "/etc/svalinn/keys/{{SERVICE_NAME}}-release.pub" - - # Require these attestations on all .ctp bundles. - required_attestations: - - "source-signature" - - "sbom-complete" - - # Reject unsigned or untrusted images. - reject_unsigned: true - -# ============================================================================ -# Request Validation -# ============================================================================ -# -# Input validation at the gateway layer — catches malformed requests -# before they reach the application. - -validation: - # Maximum request body size. - max_body_size: "8MB" - - # Reject requests with NaN or Infinity in numeric fields. - reject_nan_inf: true - - # Maximum result limit per list/search query. - max_result_limit: 500 - -# ============================================================================ -# CORS -# ============================================================================ -# -# Cross-Origin Resource Sharing policy. The defaults below allow all -# origins — restrict to your frontend domain(s) in production. - -cors: - allow_origins: ["*"] - allow_methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"] - allow_headers: ["Content-Type", "Authorization"] - max_age: 3600 - -# ============================================================================ -# Logging -# ============================================================================ -# -# Structured logging for svalinn itself. Audit paths log all requests -# (including body hashes) for post-incident investigation. - -logging: - format: "json" - level: "info" - # Log all write operations for audit trail. - audit_paths: - - "/api/v1/*" +{ + version: "1.0", + # ============================================================================ + # Authentication + # ============================================================================ + # + # Define which endpoints require authentication and at what level. + auth: { + # Public endpoints — no authentication required. + # Health and readiness probes must always be public so that + # orchestrators (selur, Podman, k8s) can check service status. + public: [ + { + path: "/health", + methods: [ + "GET", + ], + }, + { + path: "/ready", + methods: [ + "GET", + ], + }, + { + path: "/metrics", + methods: [ + "GET", + ], + }, + ], + # Endpoints requiring JWT or OAuth2 authentication. + # Svalinn validates the token before forwarding the request. + authenticated: [ + { + path: "/api/v1/*", + methods: [ + "GET", + "POST", + "PUT", + "DELETE", + ], + }, + ], + }, + # ============================================================================ + # Rate Limiting + # ============================================================================ + # + # Protects backend services from overload. Values here are moderate + # defaults — adjust based on your service capacity. + rate_limits: { + # Global limit: applied to all authenticated clients. + global: { + requests_per_second: 500, + burst: 1000, + }, + # Write operations: stricter limit to protect data stores. + writes: { + paths: [ + "/api/v1/*", + ], + methods: [ + "POST", + "PUT", + "DELETE", + ], + requests_per_second: 100, + burst: 200, + }, + }, + # ============================================================================ + # Container Trust + # ============================================================================ + # + # Svalinn verifies that all .ctp bundles in the stack are signed by + # trusted keys and carry the required attestations. + trust: { + # Only accept .ctp bundles signed by these keys. + trusted_signers: [ + { + key_id: "{{SERVICE_NAME}}-release", + algorithm: "Ed25519", + public_key_file: "/etc/svalinn/keys/{{SERVICE_NAME}}-release.pub", + }, + ], + # Require these attestations on all .ctp bundles. + required_attestations: [ + "source-signature", + "sbom-complete", + ], + # Reject unsigned or untrusted images. + reject_unsigned: true, + }, + # ============================================================================ + # Request Validation + # ============================================================================ + # + # Input validation at the gateway layer — catches malformed requests + # before they reach the application. + validation: { + # Maximum request body size. + max_body_size: "8MB", + # Reject requests with NaN or Infinity in numeric fields. + reject_nan_inf: true, + # Maximum result limit per list/search query. + max_result_limit: 500, + }, + # ============================================================================ + # CORS + # ============================================================================ + # + # Cross-Origin Resource Sharing policy. The defaults below allow all + # origins — restrict to your frontend domain(s) in production. + cors: { + allow_origins: [ + "*", + ], + allow_methods: [ + "GET", + "POST", + "PUT", + "DELETE", + "OPTIONS", + ], + allow_headers: [ + "Content-Type", + "Authorization", + ], + max_age: 3600, + }, + # ============================================================================ + # Logging + # ============================================================================ + # + # Structured logging for svalinn itself. Audit paths log all requests + # (including body hashes) for post-incident investigation. + logging: { + format: "json", + level: "info", + # Log all write operations for audit trail. + audit_paths: [ + "/api/v1/*", + ], + }, +}