rcf-lite 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/blueprints/application-account-settings/docs/topics.md +4 -1
- package/blueprints/application-admin-console/docs/topics.md +4 -1
- package/blueprints/application-api-rest/docs/topics.md +4 -1
- package/blueprints/application-charts/docs/topics.md +4 -1
- package/blueprints/application-dashboard/docs/topics.md +4 -1
- package/blueprints/application-datatable/docs/topics.md +4 -1
- package/blueprints/application-empty-error-states/docs/topics.md +4 -1
- package/blueprints/application-error-handling/docs/topics.md +4 -1
- package/blueprints/application-file-upload/docs/topics.md +4 -1
- package/blueprints/application-forms-wizard/docs/topics.md +4 -1
- package/blueprints/application-notifications-in-app/docs/topics.md +4 -1
- package/blueprints/application-onboarding-tour/docs/topics.md +4 -1
- package/blueprints/application-spa/docs/topics.md +4 -1
- package/blueprints/delivery-ci-workflows/docs/topics.md +4 -1
- package/blueprints/deploy-cloudflare-workers/docs/topics.md +4 -1
- package/blueprints/deploy-hetzner-server/CHANGELOG.md +55 -0
- package/blueprints/deploy-hetzner-server/README.md +202 -0
- package/blueprints/deploy-hetzner-server/blueprint.json +233 -0
- package/blueprints/deploy-hetzner-server/contributions/adrs/adr-3801-deploy-hetzner-server-cloud-host-contract.json +14 -0
- package/blueprints/deploy-hetzner-server/contributions/adrs/adr-3802-deploy-hetzner-server-snapshot-cadence.json +14 -0
- package/blueprints/deploy-hetzner-server/contributions/adrs/adr-3803-deploy-hetzner-server-provisioning-tool.json +20 -0
- package/blueprints/deploy-hetzner-server/contributions/adrs/adr-3804-deploy-hetzner-server-hardening-baseline.json +14 -0
- package/blueprints/deploy-hetzner-server/contributions/probes/cloud-init-render-lint.mjs +71 -0
- package/blueprints/deploy-hetzner-server/contributions/probes/hcloud-dry-run-mock.mjs +164 -0
- package/blueprints/deploy-hetzner-server/contributions/probes/manifest-schema-validate.mjs +182 -0
- package/blueprints/deploy-hetzner-server/contributions/probes/probe-utils.mjs +112 -0
- package/blueprints/deploy-hetzner-server/contributions/probes/real-account-cloud-init-hardened.mjs +64 -0
- package/blueprints/deploy-hetzner-server/contributions/probes/real-account-snapshot-on-demand.mjs +60 -0
- package/blueprints/deploy-hetzner-server/contributions/probes/real-account-throwaway-server-provision.mjs +58 -0
- package/blueprints/deploy-hetzner-server/contributions/probes/run-cloud-init-render-lint.mjs +9 -0
- package/blueprints/deploy-hetzner-server/contributions/probes/run-hcloud-dry-run-mock.mjs +9 -0
- package/blueprints/deploy-hetzner-server/contributions/probes/run-manifest-schema-validate.mjs +9 -0
- package/blueprints/deploy-hetzner-server/contributions/probes/run-real-account-cloud-init-hardened.mjs +9 -0
- package/blueprints/deploy-hetzner-server/contributions/probes/run-real-account-snapshot-on-demand.mjs +9 -0
- package/blueprints/deploy-hetzner-server/contributions/probes/run-real-account-throwaway-server-provision.mjs +9 -0
- package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-001.json +18 -0
- package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-002.json +18 -0
- package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-003.json +18 -0
- package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-004.json +18 -0
- package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-005.json +18 -0
- package/blueprints/deploy-hetzner-server/contributions/requirements/deploy-hetzner-server-req-006.json +18 -0
- package/blueprints/deploy-hetzner-server/contributions/schemas/hetzner-server.schema.json +108 -0
- package/blueprints/deploy-hetzner-server/contributions/tacs/tac-3801-deploy-hetzner-server-provisioner.json +27 -0
- package/blueprints/deploy-hetzner-server/contributions/tacs/tac-3802-deploy-hetzner-server-manifest-schema.json +26 -0
- package/blueprints/deploy-hetzner-server/contributions/tacs/tac-3803-deploy-hetzner-server-cloud-init-template.json +26 -0
- package/blueprints/deploy-hetzner-server/contributions/tacs/tac-3804-deploy-hetzner-server-firewall-shape.json +25 -0
- package/blueprints/deploy-hetzner-server/contributions/templates/cloud-init.yaml.tmpl +113 -0
- package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37101.json +24 -0
- package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37102.json +24 -0
- package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37103.json +25 -0
- package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37104.json +24 -0
- package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37105.json +24 -0
- package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37106.json +25 -0
- package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37107.json +24 -0
- package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37108.json +24 -0
- package/blueprints/deploy-hetzner-server/contributions/user-stories/deploy-hetzner-server-us-37109.json +24 -0
- package/blueprints/deploy-hetzner-server/docs/topics.md +48 -0
- package/blueprints/deploy-hetzner-server/guide/deploy-hetzner-server.md +180 -0
- package/blueprints/edge-cloudflare-access/docs/topics.md +4 -1
- package/blueprints/edge-cloudflare-rate-limiting/docs/topics.md +4 -1
- package/blueprints/edge-cloudflare-tunnel/CHANGELOG.md +23 -0
- package/blueprints/edge-cloudflare-tunnel/README.md +257 -0
- package/blueprints/edge-cloudflare-tunnel/blueprint.json +168 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/adrs/adr-4001-edge-cloudflare-tunnel-ingress-bridge-contract.json +23 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/adrs/adr-4002-edge-cloudflare-tunnel-connector-runtime.json +19 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/adrs/adr-4003-edge-cloudflare-tunnel-hostname-mode.json +19 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/aud-presence-check.mjs +114 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/cloudflared-config-lint.mjs +80 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/manifest-schema-validate.mjs +237 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/probe-utils.mjs +231 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/real-account-connector-healthy.mjs +143 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/real-account-tunnel-hostname-routes.mjs +166 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-aud-presence-check.mjs +4 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-cloudflared-config-lint.mjs +4 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-manifest-schema-validate.mjs +4 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-real-account-connector-healthy.mjs +4 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-real-account-tunnel-hostname-routes.mjs +4 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/probes/tunnel-manifest.schema.json +55 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-001.json +18 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-002.json +18 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-003.json +18 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-004.json +18 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/requirements/edge-cloudflare-tunnel-req-005.json +18 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/tacs/tac-4001-edge-cloudflare-tunnel-connector-shape.json +26 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/tacs/tac-4002-edge-cloudflare-tunnel-manifest-schema.json +26 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/tacs/tac-4003-edge-cloudflare-tunnel-access-binding.json +25 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39101.json +24 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39102.json +24 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39103.json +24 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39104.json +24 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39105.json +24 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39106.json +24 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39107.json +24 -0
- package/blueprints/edge-cloudflare-tunnel/contributions/user-stories/edge-cloudflare-tunnel-us-39108.json +24 -0
- package/blueprints/edge-cloudflare-tunnel/docs/topics.md +72 -0
- package/blueprints/edge-cloudflare-tunnel/guide/edge-cloudflare-tunnel.md +204 -0
- package/blueprints/edge-cloudflare-turnstile/docs/topics.md +4 -1
- package/blueprints/email-smtp-resend/docs/topics.md +4 -1
- package/blueprints/jobs-background/docs/topics.md +4 -1
- package/blueprints/messaging-queue-cloudflare/CHANGELOG.md +19 -0
- package/blueprints/messaging-queue-cloudflare/README.md +2 -2
- package/blueprints/messaging-queue-cloudflare/blueprint.json +1 -1
- package/blueprints/messaging-queue-cloudflare/contributions/probes/real-account-concurrency-smoke.mjs +192 -37
- package/blueprints/messaging-queue-cloudflare/docs/topics.md +4 -1
- package/blueprints/object-storage-s3/CHANGELOG.md +11 -0
- package/blueprints/object-storage-s3/README.md +14 -1
- package/blueprints/object-storage-s3/blueprint.json +12 -1
- package/blueprints/object-storage-s3/contributions/adrs/adr-2905-object-storage-s3-hetzner-object-storage-provider.json +30 -0
- package/blueprints/object-storage-s3/contributions/probes/hetzner-object-storage-round-trip.mjs +136 -0
- package/blueprints/object-storage-s3/contributions/probes/run-hetzner-object-storage-round-trip.mjs +11 -0
- package/blueprints/object-storage-s3/contributions/requirements/object-storage-s3-req-101.json +18 -0
- package/blueprints/object-storage-s3/contributions/tacs/tac-2904-object-storage-s3-hetzner-endpoint-helper.json +34 -0
- package/blueprints/object-storage-s3/contributions/user-stories/object-storage-s3-us-28110.json +25 -0
- package/blueprints/object-storage-s3/docs/topics.md +4 -1
- package/blueprints/object-storage-s3/guide/object-storage-s3.md +36 -0
- package/blueprints/observability-essentials/docs/topics.md +4 -1
- package/blueprints/observability-logging/docs/topics.md +4 -1
- package/blueprints/observability-probe-endpoints/docs/topics.md +4 -1
- package/blueprints/persistence-data-d1/docs/topics.md +4 -1
- package/blueprints/persistence-data-postgres/docs/topics.md +4 -1
- package/blueprints/persistence-data-sqlite/docs/topics.md +4 -1
- package/blueprints/platform-cloudflare-cron-triggers/docs/topics.md +4 -1
- package/blueprints/platform-cloudflare-durable-objects/CHANGELOG.md +15 -0
- package/blueprints/platform-cloudflare-durable-objects/blueprint.json +1 -1
- package/blueprints/platform-cloudflare-durable-objects/contributions/probes/namespace-facade-ready.mjs +3 -3
- package/blueprints/platform-cloudflare-durable-objects/contributions/probes/probe-utils.mjs +28 -14
- package/blueprints/platform-cloudflare-durable-objects/contributions/probes/real-account-storage-smoke.mjs +132 -26
- package/blueprints/platform-cloudflare-durable-objects/contributions/probes/sole-reader-scan.mjs +40 -39
- package/blueprints/platform-cloudflare-durable-objects/contributions/probes/storage-round-trip.mjs +26 -16
- package/blueprints/platform-cloudflare-durable-objects/contributions/probes/websocket-hub-broadcast.mjs +31 -34
- package/blueprints/platform-cloudflare-durable-objects/docs/topics.md +4 -1
- package/blueprints/platform-cloudflare-kv/CHANGELOG.md +40 -1
- package/blueprints/platform-cloudflare-kv/README.md +41 -13
- package/blueprints/platform-cloudflare-kv/blueprint.json +1 -1
- package/blueprints/platform-cloudflare-kv/contributions/probes/cache-aside-hit-then-miss.mjs +26 -26
- package/blueprints/platform-cloudflare-kv/contributions/probes/event-secrecy.mjs +49 -31
- package/blueprints/platform-cloudflare-kv/contributions/probes/facade-round-trip.mjs +58 -16
- package/blueprints/platform-cloudflare-kv/contributions/probes/list-with-prefix.mjs +6 -5
- package/blueprints/platform-cloudflare-kv/contributions/probes/real-account-eventual-consistency-smoke.mjs +131 -67
- package/blueprints/platform-cloudflare-kv/docs/topics.md +4 -1
- package/blueprints/platform-docker-compose-host/CHANGELOG.md +12 -0
- package/blueprints/platform-docker-compose-host/README.md +76 -0
- package/blueprints/platform-docker-compose-host/blueprint.json +209 -0
- package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3901-platform-docker-compose-host-contract.json +14 -0
- package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3902-platform-docker-compose-host-reverse-proxy.json +14 -0
- package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3903-platform-docker-compose-host-reject-coolify.json +14 -0
- package/blueprints/platform-docker-compose-host/contributions/adrs/adr-3904-platform-docker-compose-host-log-driver.json +14 -0
- package/blueprints/platform-docker-compose-host/contributions/probes/caddyfile-validate.mjs +108 -0
- package/blueprints/platform-docker-compose-host/contributions/probes/compose-config-lint.mjs +231 -0
- package/blueprints/platform-docker-compose-host/contributions/probes/probe-utils.mjs +185 -0
- package/blueprints/platform-docker-compose-host/contributions/probes/real-account-minimal-stack-up.mjs +41 -0
- package/blueprints/platform-docker-compose-host/contributions/probes/real-account-reload-burst.mjs +36 -0
- package/blueprints/platform-docker-compose-host/contributions/probes/run-caddyfile-validate.mjs +5 -0
- package/blueprints/platform-docker-compose-host/contributions/probes/run-compose-config-lint.mjs +5 -0
- package/blueprints/platform-docker-compose-host/contributions/probes/run-real-account-minimal-stack-up.mjs +5 -0
- package/blueprints/platform-docker-compose-host/contributions/probes/run-real-account-reload-burst.mjs +5 -0
- package/blueprints/platform-docker-compose-host/contributions/probes/run-secrets-as-files-scan.mjs +5 -0
- package/blueprints/platform-docker-compose-host/contributions/probes/secrets-as-files-scan.mjs +116 -0
- package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-001.json +18 -0
- package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-002.json +18 -0
- package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-003.json +18 -0
- package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-004.json +18 -0
- package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-005.json +18 -0
- package/blueprints/platform-docker-compose-host/contributions/requirements/platform-docker-compose-host-req-006.json +18 -0
- package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3901-platform-docker-compose-host-compose-layout.json +26 -0
- package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3902-platform-docker-compose-host-secrets-mount.json +26 -0
- package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3903-platform-docker-compose-host-healthcheck-lint.json +26 -0
- package/blueprints/platform-docker-compose-host/contributions/tacs/tac-3904-platform-docker-compose-host-reverse-proxy-artefact.json +26 -0
- package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38101.json +24 -0
- package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38102.json +24 -0
- package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38103.json +24 -0
- package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38104.json +24 -0
- package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38105.json +25 -0
- package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38106.json +24 -0
- package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38107.json +24 -0
- package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38108.json +24 -0
- package/blueprints/platform-docker-compose-host/contributions/user-stories/platform-docker-compose-host-us-38109.json +24 -0
- package/blueprints/platform-docker-compose-host/docs/topics.md +71 -0
- package/blueprints/platform-docker-compose-host/guide/platform-docker-compose-host.md +94 -0
- package/blueprints/security-auth-clerk/docs/topics.md +4 -1
- package/blueprints/security-auth-keycloak/docs/topics.md +4 -1
- package/blueprints/security-auth-magic-link/docs/topics.md +4 -1
- package/blueprints/security-auth-oauth2/docs/topics.md +4 -1
- package/blueprints/security-secrets-management/docs/topics.md +4 -1
- package/fixtures/canary-manifest.json +9 -9
- package/guidance/harness-template.md +67 -0
- package/guidance/managed/agent-instructions-block.hash +1 -1
- package/guidance/managed/agent-instructions-block.md +67 -0
- package/package.json +2 -2
- package/rcf/adrs/adr-3801-deploy-hetzner-server-cloud-host-contract.json +13 -0
- package/rcf/adrs/adr-3802-deploy-hetzner-server-snapshot-cadence.json +13 -0
- package/rcf/adrs/adr-3803-deploy-hetzner-server-provisioning-tool.json +13 -0
- package/rcf/adrs/adr-3804-deploy-hetzner-server-hardening-baseline.json +13 -0
- package/rcf/adrs/adr-3901-platform-docker-compose-host-contract.json +13 -0
- package/rcf/adrs/adr-3902-platform-docker-compose-host-reverse-proxy.json +13 -0
- package/rcf/adrs/adr-3903-platform-docker-compose-host-reject-coolify.json +13 -0
- package/rcf/adrs/adr-3904-platform-docker-compose-host-log-driver.json +13 -0
- package/rcf/adrs/adr-4001-edge-cloudflare-tunnel-ingress-bridge-contract.json +13 -0
- package/rcf/adrs/adr-4002-edge-cloudflare-tunnel-connector-runtime.json +13 -0
- package/rcf/adrs/adr-4003-edge-cloudflare-tunnel-hostname-mode.json +13 -0
- package/rcf/code-nodes/cn-410.json +20 -0
- package/rcf/code-nodes/cn-440.json +20 -0
- package/rcf/code-nodes/cn-470.json +19 -0
- package/rcf/code-nodes/cn-500.json +12 -0
- package/rcf/code-nodes/cn-510.json +12 -0
- package/rcf/code-nodes/cn-520.json +14 -0
- package/rcf/code-nodes/cn-521.json +12 -0
- package/rcf/code-nodes/cn-522.json +13 -0
- package/rcf/code-nodes/cn-523.json +12 -0
- package/rcf/code-nodes/cn-524.json +14 -0
- package/rcf/code-nodes/cn-525.json +12 -0
- package/rcf/code-nodes/cn-526.json +14 -0
- package/rcf/fbs/fbs-130.json +23 -0
- package/rcf/fbs/fbs-140.json +23 -0
- package/rcf/fbs/fbs-150.json +22 -0
- package/rcf/fbs/fbs-160.json +15 -0
- package/rcf/fbs/fbs-165.json +15 -0
- package/rcf/fbs/fbs-170.json +16 -0
- package/rcf/fbs/fbs-171.json +16 -0
- package/rcf/fbs/fbs-172.json +15 -0
- package/rcf/fbs/fbs-173.json +17 -0
- package/rcf/fbs/fbs-174.json +17 -0
- package/rcf/requirements/req-110.json +58 -0
- package/rcf/requirements/req-111.json +32 -0
- package/rcf/requirements/req-112.json +42 -0
- package/rcf/requirements/req-113.json +33 -0
- package/rcf/requirements/req-114.json +20 -0
- package/rcf/requirements/req-115.json +38 -0
- package/rcf/requirements/req-120.json +20 -0
- package/rcf/requirements/req-121.json +20 -0
- package/rcf/requirements/req-122.json +20 -0
- package/rcf/requirements/req-123.json +42 -0
- package/rcf/requirements/req-124.json +27 -0
- package/rcf/requirements/req-125.json +20 -0
- package/rcf/requirements/req-130.json +32 -0
- package/rcf/requirements/req-131.json +54 -0
- package/rcf/requirements/req-132.json +27 -0
- package/rcf/requirements/req-133.json +20 -0
- package/rcf/requirements/req-134.json +42 -0
- package/rcf/requirements/req-140.json +149 -0
- package/rcf/requirements/req-145.json +53 -0
- package/rcf/requirements/req-150.json +20 -0
- package/rcf/requirements/req-151.json +85 -0
- package/rcf/requirements/req-152.json +20 -0
- package/rcf/requirements/req-153.json +59 -0
- package/rcf/requirements/req-154.json +44 -0
- package/rcf/tacs/tac-3801-deploy-hetzner-server-provisioner.json +17 -0
- package/rcf/tacs/tac-3802-deploy-hetzner-server-manifest-schema.json +17 -0
- package/rcf/tacs/tac-3803-deploy-hetzner-server-cloud-init-template.json +17 -0
- package/rcf/tacs/tac-3804-deploy-hetzner-server-firewall-shape.json +16 -0
- package/rcf/tacs/tac-3901-platform-docker-compose-host-compose-layout.json +17 -0
- package/rcf/tacs/tac-3902-platform-docker-compose-host-secrets-mount.json +17 -0
- package/rcf/tacs/tac-3903-platform-docker-compose-host-healthcheck-lint.json +17 -0
- package/rcf/tacs/tac-3904-platform-docker-compose-host-reverse-proxy-artefact.json +17 -0
- package/rcf/tacs/tac-4001-edge-cloudflare-tunnel-connector-shape.json +17 -0
- package/rcf/tacs/tac-4002-edge-cloudflare-tunnel-manifest-schema.json +17 -0
- package/rcf/tacs/tac-4003-edge-cloudflare-tunnel-access-binding.json +16 -0
- package/rcf/test-suites/ts-071.json +2 -2
- package/rcf/test-suites/ts-086.json +2 -2
- package/rcf/test-suites/ts-140.json +86 -0
- package/rcf/test-suites/ts-150.json +86 -0
- package/rcf/test-suites/ts-160.json +78 -0
- package/rcf/test-suites/ts-170.json +22 -0
- package/rcf/test-suites/ts-175.json +22 -0
- package/rcf/test-suites/ts-180.json +30 -0
- package/rcf/test-suites/ts-181.json +30 -0
- package/rcf/test-suites/ts-182.json +22 -0
- package/rcf/test-suites/ts-183.json +38 -0
- package/rcf/test-suites/ts-184.json +38 -0
- package/rcf/user-stories/us-11001.json +23 -0
- package/rcf/user-stories/us-11101.json +23 -0
- package/rcf/user-stories/us-11102.json +24 -0
- package/rcf/user-stories/us-11201.json +23 -0
- package/rcf/user-stories/us-11202.json +23 -0
- package/rcf/user-stories/us-11301.json +24 -0
- package/rcf/user-stories/us-11401.json +23 -0
- package/rcf/user-stories/us-11402.json +23 -0
- package/rcf/user-stories/us-11501.json +23 -0
- package/rcf/user-stories/us-12001.json +23 -0
- package/rcf/user-stories/us-12101.json +23 -0
- package/rcf/user-stories/us-12102.json +23 -0
- package/rcf/user-stories/us-12201.json +23 -0
- package/rcf/user-stories/us-12202.json +24 -0
- package/rcf/user-stories/us-12301.json +23 -0
- package/rcf/user-stories/us-12401.json +23 -0
- package/rcf/user-stories/us-12402.json +23 -0
- package/rcf/user-stories/us-12501.json +23 -0
- package/rcf/user-stories/us-13001.json +23 -0
- package/rcf/user-stories/us-13002.json +23 -0
- package/rcf/user-stories/us-13101.json +23 -0
- package/rcf/user-stories/us-13102.json +23 -0
- package/rcf/user-stories/us-13201.json +23 -0
- package/rcf/user-stories/us-13301.json +23 -0
- package/rcf/user-stories/us-13302.json +23 -0
- package/rcf/user-stories/us-13401.json +23 -0
- package/rcf/user-stories/us-14001.json +21 -0
- package/rcf/user-stories/us-14501.json +24 -0
- package/rcf/user-stories/us-15001.json +26 -0
- package/rcf/user-stories/us-15101.json +26 -0
- package/rcf/user-stories/us-15201.json +21 -0
- package/rcf/user-stories/us-15301.json +31 -0
- package/rcf/user-stories/us-15401.json +31 -0
- package/releases/releases.yaml +21 -1
- package/src/blueprint/apply.js +64 -0
- package/src/blueprint/consistency-lint.js +432 -0
- package/src/blueprint/disposition-ledger.js +224 -0
- package/src/blueprint/dispositions.js +44 -0
- package/src/blueprint/index.js +19 -0
- package/src/browser-verify/manifest-writer.js +53 -2
- package/src/browser-verify/pack-runner.js +18 -0
- package/src/cli/blueprint.js +103 -0
- package/src/setup/identity-seed.js +18 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// Probe: real-account-tunnel-hostname-routes.
|
|
2
|
+
//
|
|
3
|
+
// anchorAcId list (unchanged per H-2 C4 ruling; the AC-tunnel-* ids
|
|
4
|
+
// ARE the shipped AC ids in the US-39104 / US-39106 contribution
|
|
5
|
+
// bodies):
|
|
6
|
+
// - AC-tunnel-hostnameRoutes (public-hostname sub-case: ingress rule
|
|
7
|
+
// routes public hostname to internal service URL and returns 200).
|
|
8
|
+
// - AC-tunnel-accessGated (access-gated sub-case: Access-gated
|
|
9
|
+
// composition; unauthenticated request refused, JWT-carrying
|
|
10
|
+
// request accepted; per US-39106).
|
|
11
|
+
//
|
|
12
|
+
// accountBound: true.
|
|
13
|
+
// Gating (amendment 4 verbatim):
|
|
14
|
+
// - Public-hostname sub-case activated on CI_HAS_CLOUDFLARE_ACCOUNT
|
|
15
|
+
// AND CI_HAS_HETZNER_ACCOUNT.
|
|
16
|
+
// - Access-gated sub-case activated only when CI_HAS_CLOUDFLARE_ACCESS
|
|
17
|
+
// is ALSO set. Env-absent branch keeps pass-with-skip; a pass must
|
|
18
|
+
// never be reachable from CI_HAS_CLOUDFLARE_ACCOUNT presence alone.
|
|
19
|
+
// - HQ note: Cloudflare Access is not enabled on the HQ account, so
|
|
20
|
+
// at gate time the AUD sub-case stays pass-with-skip; expected,
|
|
21
|
+
// not a driver defect.
|
|
22
|
+
//
|
|
23
|
+
// Env-present, sub-case active: undici-based fetch driver against the
|
|
24
|
+
// scratch subdomain via the cf-edge shim (production path uses Node's
|
|
25
|
+
// built-in fetch; shim-synthetic path lets local runs exercise the
|
|
26
|
+
// driver body without account access). For the access-gated
|
|
27
|
+
// sub-case: send an unauthenticated request (must be refused with
|
|
28
|
+
// 4xx) AND a JWT-carrying request (must be accepted with 2xx). A
|
|
29
|
+
// pass requires BOTH results.
|
|
30
|
+
|
|
31
|
+
import { runShim, accountBoundSkippedResult } from './probe-utils.mjs';
|
|
32
|
+
|
|
33
|
+
export const anchorAcIds = ['AC-tunnel-hostnameRoutes', 'AC-tunnel-accessGated'];
|
|
34
|
+
export const accountBound = true;
|
|
35
|
+
|
|
36
|
+
const PUBLIC_URL_ENV = 'CF_TUNNEL_PUBLIC_URL';
|
|
37
|
+
const ACCESS_URL_ENV = 'CF_TUNNEL_ACCESS_URL';
|
|
38
|
+
const ACCESS_AUDIENCE_ENV = 'CF_ACCESS_AUDIENCE';
|
|
39
|
+
const ACCESS_TEAM_SECRET_ENV = 'CF_ACCESS_TEAM_SECRET';
|
|
40
|
+
const DEFAULT_PUBLIC_URL = 'https://h2-cf-public.scratch.example.invalid/health';
|
|
41
|
+
const DEFAULT_ACCESS_URL = 'https://h2-cf-gated.scratch.example.invalid/protected';
|
|
42
|
+
|
|
43
|
+
export default async function runProbe() {
|
|
44
|
+
const results = [];
|
|
45
|
+
const extra = {
|
|
46
|
+
envRequired: {
|
|
47
|
+
publicHostname: ['CI_HAS_CLOUDFLARE_ACCOUNT', 'CI_HAS_HETZNER_ACCOUNT'],
|
|
48
|
+
accessGated: ['CI_HAS_CLOUDFLARE_ACCOUNT', 'CI_HAS_HETZNER_ACCOUNT', 'CI_HAS_CLOUDFLARE_ACCESS'],
|
|
49
|
+
},
|
|
50
|
+
envPresent: {
|
|
51
|
+
CI_HAS_CLOUDFLARE_ACCOUNT: process.env.CI_HAS_CLOUDFLARE_ACCOUNT === 'true',
|
|
52
|
+
CI_HAS_HETZNER_ACCOUNT: process.env.CI_HAS_HETZNER_ACCOUNT === 'true',
|
|
53
|
+
CI_HAS_CLOUDFLARE_ACCESS: process.env.CI_HAS_CLOUDFLARE_ACCESS === 'true',
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
const hasPublic = extra.envPresent.CI_HAS_CLOUDFLARE_ACCOUNT && extra.envPresent.CI_HAS_HETZNER_ACCOUNT;
|
|
57
|
+
const hasGated = hasPublic && extra.envPresent.CI_HAS_CLOUDFLARE_ACCESS;
|
|
58
|
+
|
|
59
|
+
if (!hasPublic) {
|
|
60
|
+
results.push(accountBoundSkippedResult(
|
|
61
|
+
'AC-tunnel-hostnameRoutes',
|
|
62
|
+
['CI_HAS_CLOUDFLARE_ACCOUNT', 'CI_HAS_HETZNER_ACCOUNT'],
|
|
63
|
+
'public-hostname sub-case: undici fetch against the scratch subdomain not exercised. Set CI_HAS_CLOUDFLARE_ACCOUNT=true AND CI_HAS_HETZNER_ACCOUNT=true to run this path.',
|
|
64
|
+
));
|
|
65
|
+
}
|
|
66
|
+
if (!hasGated) {
|
|
67
|
+
results.push(accountBoundSkippedResult(
|
|
68
|
+
'AC-tunnel-accessGated',
|
|
69
|
+
['CI_HAS_CLOUDFLARE_ACCOUNT', 'CI_HAS_HETZNER_ACCOUNT', 'CI_HAS_CLOUDFLARE_ACCESS'],
|
|
70
|
+
'access-gated sub-case: two-identity JWT check against the scratch subdomain not exercised. Requires all three env vars; a pass here is unreachable from CI_HAS_CLOUDFLARE_ACCOUNT presence alone (amendment 4 verbatim).',
|
|
71
|
+
));
|
|
72
|
+
}
|
|
73
|
+
if (!hasPublic && !hasGated) {
|
|
74
|
+
return { results, extra };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Sub-case active: load the shim seams.
|
|
78
|
+
const { fetchTunnelHostname, mintScratchIdentity } = await import(
|
|
79
|
+
'../../../../packages/rcf-lite/test/fixtures/cf-edge/h2-cf-tunnel-shim.mjs'
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
if (hasPublic) {
|
|
83
|
+
const url = process.env[PUBLIC_URL_ENV] || DEFAULT_PUBLIC_URL;
|
|
84
|
+
extra.publicHostnameUrl = url;
|
|
85
|
+
try {
|
|
86
|
+
const resp = await fetchTunnelHostname(url, { method: 'GET' });
|
|
87
|
+
extra.publicHostnameResponse = { status: resp.status, shimMode: resp.shimMode, bodyLength: resp.textBody.length };
|
|
88
|
+
if (resp.status >= 200 && resp.status < 300) {
|
|
89
|
+
results.push({
|
|
90
|
+
anchorAcId: 'AC-tunnel-hostnameRoutes',
|
|
91
|
+
verdict: 'pass',
|
|
92
|
+
detail: `public-hostname sub-case: fetch(${url}) returned ${resp.status} (shim mode ${resp.shimMode}), body length ${resp.textBody.length}. Ingress rule routes the public hostname to the internal service URL.`,
|
|
93
|
+
});
|
|
94
|
+
} else {
|
|
95
|
+
results.push({
|
|
96
|
+
anchorAcId: 'AC-tunnel-hostnameRoutes',
|
|
97
|
+
verdict: 'fail',
|
|
98
|
+
detail: `public-hostname sub-case: fetch(${url}) returned ${resp.status} (shim mode ${resp.shimMode}). Expected 2xx; the ingress rule did not route the public hostname to a healthy internal service.`,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
} catch (err) {
|
|
102
|
+
results.push({
|
|
103
|
+
anchorAcId: 'AC-tunnel-hostnameRoutes',
|
|
104
|
+
verdict: 'fail',
|
|
105
|
+
detail: `public-hostname sub-case: fetch(${url}) threw: ${err.message}. Check the scratch subdomain, the tunnel manifest ingress rules, or the cf-edge shim.`,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (hasGated) {
|
|
111
|
+
const url = process.env[ACCESS_URL_ENV] || DEFAULT_ACCESS_URL;
|
|
112
|
+
const audience = process.env[ACCESS_AUDIENCE_ENV] || 'h2-cf-scratch-audience';
|
|
113
|
+
const secret = process.env[ACCESS_TEAM_SECRET_ENV] || 'h2-cf-scratch-team-secret';
|
|
114
|
+
extra.accessGatedUrl = url;
|
|
115
|
+
let refusalResp;
|
|
116
|
+
let acceptResp;
|
|
117
|
+
try {
|
|
118
|
+
refusalResp = await fetchTunnelHostname(url, { method: 'GET' });
|
|
119
|
+
} catch (err) {
|
|
120
|
+
results.push({
|
|
121
|
+
anchorAcId: 'AC-tunnel-accessGated',
|
|
122
|
+
verdict: 'fail',
|
|
123
|
+
detail: `access-gated sub-case: unauthenticated fetch(${url}) threw: ${err.message}.`,
|
|
124
|
+
});
|
|
125
|
+
return { results, extra };
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const jwt = mintScratchIdentity({ audience, subject: 'h2-cf-scratch-identity', secret });
|
|
129
|
+
acceptResp = await fetchTunnelHostname(url, {
|
|
130
|
+
method: 'GET',
|
|
131
|
+
headers: { authorization: `Bearer ${jwt}` },
|
|
132
|
+
});
|
|
133
|
+
} catch (err) {
|
|
134
|
+
results.push({
|
|
135
|
+
anchorAcId: 'AC-tunnel-accessGated',
|
|
136
|
+
verdict: 'fail',
|
|
137
|
+
detail: `access-gated sub-case: JWT-carrying fetch(${url}) threw: ${err.message}.`,
|
|
138
|
+
});
|
|
139
|
+
return { results, extra };
|
|
140
|
+
}
|
|
141
|
+
extra.accessGatedRefusal = { status: refusalResp.status, shimMode: refusalResp.shimMode };
|
|
142
|
+
extra.accessGatedAccept = { status: acceptResp.status, shimMode: acceptResp.shimMode };
|
|
143
|
+
const refused = refusalResp.status >= 400 && refusalResp.status < 500;
|
|
144
|
+
const accepted = acceptResp.status >= 200 && acceptResp.status < 300;
|
|
145
|
+
if (refused && accepted) {
|
|
146
|
+
results.push({
|
|
147
|
+
anchorAcId: 'AC-tunnel-accessGated',
|
|
148
|
+
verdict: 'pass',
|
|
149
|
+
detail: `access-gated sub-case: unauthenticated request refused with ${refusalResp.status} and JWT-carrying request accepted with ${acceptResp.status} (shim mode ${refusalResp.shimMode}). Access gate composed correctly.`,
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
results.push({
|
|
153
|
+
anchorAcId: 'AC-tunnel-accessGated',
|
|
154
|
+
verdict: 'fail',
|
|
155
|
+
detail: `access-gated sub-case: expected 4xx-then-2xx; observed refusal status ${refusalResp.status} and JWT-accept status ${acceptResp.status} (shim mode ${refusalResp.shimMode}). Gate is drifted: unauthenticated request should be refused and JWT request accepted.`,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { results, extra };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const engine = { kind: 'real-account-tunnel-fetch', image: 'undici fetch + cf-edge shim (scratch subdomain)', healthy: true };
|
|
164
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
165
|
+
await runShim('real-account-tunnel-hostname-routes', engine, runProbe);
|
|
166
|
+
}
|
package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-cloudflared-config-lint.mjs
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import runProbe from './cloudflared-config-lint.mjs';
|
|
2
|
+
import { runShim, CLOUDFLARED_IMAGE } from './probe-utils.mjs';
|
|
3
|
+
const engine = { kind: 'cloudflared-ingress-validate', image: CLOUDFLARED_IMAGE, healthy: true };
|
|
4
|
+
runShim('cloudflared-config-lint', engine, runProbe);
|
package/blueprints/edge-cloudflare-tunnel/contributions/probes/run-manifest-schema-validate.mjs
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import runProbe from './manifest-schema-validate.mjs';
|
|
2
|
+
import { runShim } from './probe-utils.mjs';
|
|
3
|
+
const engine = { kind: 'schema-validate', image: 'edge-cloudflare-tunnel manifest schema (Ajv-free walker)', healthy: true };
|
|
4
|
+
runShim('manifest-schema-validate', engine, runProbe);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import runProbe from './real-account-tunnel-hostname-routes.mjs';
|
|
2
|
+
import { runShim } from './probe-utils.mjs';
|
|
3
|
+
const engine = { kind: 'real-account-tunnel-hostname-routes', image: 'account-bound', healthy: true };
|
|
4
|
+
runShim('real-account-tunnel-hostname-routes', engine, runProbe);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://stravica.ai/schemas/edge-cloudflare-tunnel/tunnel-manifest.v1.json",
|
|
4
|
+
"title": "edge-cloudflare-tunnel tunnel manifest v1.0.0",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["tunnel", "credentialsFile", "ingress"],
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"tunnel": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
|
|
12
|
+
"description": "uuid-shaped Cloudflare Tunnel id per the vendor tunnel primitive"
|
|
13
|
+
},
|
|
14
|
+
"credentialsFile": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"required": ["secretRef"],
|
|
17
|
+
"additionalProperties": false,
|
|
18
|
+
"properties": {
|
|
19
|
+
"secretRef": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"minLength": 1,
|
|
22
|
+
"description": "security-secrets-management reference to the credentials JSON; never a literal path or JSON body"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"ingress": {
|
|
27
|
+
"type": "array",
|
|
28
|
+
"minItems": 1,
|
|
29
|
+
"items": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"additionalProperties": false,
|
|
32
|
+
"properties": {
|
|
33
|
+
"hostname": { "type": "string", "minLength": 1 },
|
|
34
|
+
"service": { "type": "string", "minLength": 1 },
|
|
35
|
+
"originRequest": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"additionalProperties": true,
|
|
38
|
+
"properties": {
|
|
39
|
+
"access": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"required": ["aud"],
|
|
42
|
+
"properties": {
|
|
43
|
+
"aud": { "type": "string", "minLength": 1 },
|
|
44
|
+
"teamName": { "type": "string" },
|
|
45
|
+
"required": { "type": "boolean" }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"required": ["service"]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"reqId": "edge-cloudflare-tunnel-REQ-001",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"title": "Connector install shape switches on applied containerHost (compose-service default; systemd-unit for bare cloudHost); cloudflaredReady fires on boot",
|
|
5
|
+
"description": "The blueprint contributes a cloudflared connector whose runtime shape is elicited via ADR-4002 with enum {compose-service, systemd-unit} and recommendedDefault compose-service. When containerHost is applied (T-2 platform-docker-compose-host on top of T-1 deploy-hetzner-server) the connector ships as a compose service alongside the applying project services on the same web-net network. When the host runs bare (only T-1 applied) the connector ships as a systemd unit per the vendor local-configuration-file docs. Either shape emits cloudflaredReady on boot with connector id, region and version metadata (metadata only; no token or credentials in the payload).",
|
|
6
|
+
"category": "functional",
|
|
7
|
+
"domain": "edge",
|
|
8
|
+
"priority": "must",
|
|
9
|
+
"rationale": "Ratified in the Hetzner round 7 spec (2026-09-07 section 5.3 edge-cloudflare-tunnel-REQ-001). Ships as v1.0.0 of edge-cloudflare-tunnel.",
|
|
10
|
+
"tags": [
|
|
11
|
+
"blueprint:edge-cloudflare-tunnel",
|
|
12
|
+
"capability:tunnelBridge"
|
|
13
|
+
],
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"status": "approved",
|
|
16
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
17
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"reqId": "edge-cloudflare-tunnel-REQ-002",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"title": "Tunnel manifest schema at cloudflare/tunnels/<name>.yaml under the applying project (uuid tunnel id, secretRef credentials, ingress rules with catch-all 404)",
|
|
5
|
+
"description": "The applying project ships one tunnel manifest per named tunnel at cloudflare/tunnels/<name>.yaml. The shipped JSON schema requires a uuid-shaped tunnel id, a credentials-file reference into security-secrets-management (secretRef, never a literal path or JSON body inline), and an ingress rules array whose last entry is a mandatory catch-all rule returning http_status 404. cloudflared refuses to boot when the manifest fails validation.",
|
|
6
|
+
"category": "functional",
|
|
7
|
+
"domain": "edge",
|
|
8
|
+
"priority": "must",
|
|
9
|
+
"rationale": "Ratified in the Hetzner round 7 spec (2026-09-07 section 5.3 edge-cloudflare-tunnel-REQ-002). Ships as v1.0.0 of edge-cloudflare-tunnel.",
|
|
10
|
+
"tags": [
|
|
11
|
+
"blueprint:edge-cloudflare-tunnel",
|
|
12
|
+
"capability:tunnelBridge"
|
|
13
|
+
],
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"status": "approved",
|
|
16
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
17
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"reqId": "edge-cloudflare-tunnel-REQ-003",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"title": "No public origin ports on the host (T-1 firewall stays 22 open plus 80/443 only when a reverse proxy is applied; manifest refuses an origin-port request)",
|
|
5
|
+
"description": "The applying project MUST NOT open a public port on the host firewall for the origin path when only edge-cloudflare-tunnel provides ingress. T-1 deploy-hetzner-server firewall allows 22 unconditionally and 80/443 only when a reverse proxy is applied. A project applying only T-1 plus T-3 (no reverse proxy) declares zero 80/443 rules; the tunnel manifest schema refuses ingress rules whose service URL binds to a host public interface.",
|
|
6
|
+
"category": "functional",
|
|
7
|
+
"domain": "edge",
|
|
8
|
+
"priority": "must",
|
|
9
|
+
"rationale": "Ratified in the Hetzner round 7 spec (2026-09-07 section 5.3 edge-cloudflare-tunnel-REQ-003). Ships as v1.0.0 of edge-cloudflare-tunnel.",
|
|
10
|
+
"tags": [
|
|
11
|
+
"blueprint:edge-cloudflare-tunnel",
|
|
12
|
+
"capability:tunnelBridge"
|
|
13
|
+
],
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"status": "approved",
|
|
16
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
17
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"reqId": "edge-cloudflare-tunnel-REQ-004",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"title": "Access-gated composition: when zeroTrustGate is applied, ingress rules attach originRequest.access.aud; when absent, block omitted and tunnel serves in public-hostname mode",
|
|
5
|
+
"description": "The blueprint reads the applying project sidecar rcf/blueprints/edge-cloudflare-tunnel.applied.json at apply time and inspects appliedCapabilities. When zeroTrustGate is present every ingress rule attaches an originRequest.access block naming the Access AUD; cloudflared refuses at the edge any request without a valid Access JWT for that AUD. When zeroTrustGate is absent the block is omitted and the tunnel serves in public-hostname mode. The aud-presence-check probe refuses if the shape drifts to open ingress on a project that intended the gated shape.",
|
|
6
|
+
"category": "functional",
|
|
7
|
+
"domain": "edge",
|
|
8
|
+
"priority": "must",
|
|
9
|
+
"rationale": "Ratified in the Hetzner round 7 spec (2026-09-07 section 5.3 edge-cloudflare-tunnel-REQ-004). Ships as v1.0.0 of edge-cloudflare-tunnel.",
|
|
10
|
+
"tags": [
|
|
11
|
+
"blueprint:edge-cloudflare-tunnel",
|
|
12
|
+
"capability:tunnelBridge"
|
|
13
|
+
],
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"status": "approved",
|
|
16
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
17
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"reqId": "edge-cloudflare-tunnel-REQ-005",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"title": "Credentials-file discipline: JSON at mode 0o400, referenced via secretRef, not committed, grep-refuse on the literal, no leak in event bodies",
|
|
5
|
+
"description": "The tunnel credentials JSON file lands on the host at mode 0o400 (owner read only), is referenced from the manifest as a secretRef (never a literal committed file body), MUST NOT be committed to the applying project tree (grep-refuse on the tunnel id and account tag literals across the working tree), and MUST NOT appear in any cloudflaredReady, tunnelConnectorUp or ingressRuleReloaded event body.",
|
|
6
|
+
"category": "functional",
|
|
7
|
+
"domain": "edge",
|
|
8
|
+
"priority": "must",
|
|
9
|
+
"rationale": "Ratified in the Hetzner round 7 spec (2026-09-07 section 5.3 edge-cloudflare-tunnel-REQ-005). Ships as v1.0.0 of edge-cloudflare-tunnel.",
|
|
10
|
+
"tags": [
|
|
11
|
+
"blueprint:edge-cloudflare-tunnel",
|
|
12
|
+
"capability:tunnelBridge"
|
|
13
|
+
],
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"status": "approved",
|
|
16
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
17
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
18
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tacId": "TAC-4001-edge-cloudflare-tunnel-connector-shape",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"tadId": "TAD-001",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"name": "Edge-cloudflare-tunnel connector install shape",
|
|
8
|
+
"purpose": "The cloudflared connector installs in one of two runtime shapes as selected by ADR-4002 from the applied capability set. When containerHost is applied (T-2 platform-docker-compose-host on top of T-1 deploy-hetzner-server) the connector ships as a compose service alongside the applying project services on the same web-net network with no host ports and a healthcheck. When the host runs bare (only T-1 applied) the connector ships as a systemd unit per the vendor local-configuration-file docs. Either shape emits cloudflaredReady on boot with connector id, region and version metadata (metadata only).",
|
|
9
|
+
"responsibilities": [
|
|
10
|
+
"Ship the compose-service variant under packages/rcf-lite/test/fixtures/hetzner-throwaway-server/cloudflared/compose-service/ (AC-tunnel-connectorShape).",
|
|
11
|
+
"Ship the systemd-unit variant under packages/rcf-lite/test/fixtures/hetzner-throwaway-server/cloudflared/systemd-unit/ (AC-tunnel-connectorShape).",
|
|
12
|
+
"Emit cloudflaredReady on boot with connector id, region and version metadata (AC-tunnel-connectorHealthy).",
|
|
13
|
+
"Never expose an origin port on the host firewall (AC-tunnel-noPublicOrigin)."
|
|
14
|
+
],
|
|
15
|
+
"internalStructure": "One module discovering the applied fixture root and returning both variant paths; consumers walk the compose fragment or the systemd unit file per the applied capability set.",
|
|
16
|
+
"interfaces": [
|
|
17
|
+
{
|
|
18
|
+
"name": "default export",
|
|
19
|
+
"kind": "facade",
|
|
20
|
+
"summary": "Connector shape discovery facade exported from the applied fixture module."
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"dependencies": [],
|
|
24
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
25
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tacId": "TAC-4002-edge-cloudflare-tunnel-manifest-schema",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"tadId": "TAD-001",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"name": "Edge-cloudflare-tunnel manifest schema",
|
|
8
|
+
"purpose": "The tunnel manifest at cloudflare/tunnels/<name>.yaml under the applying project is the sole runtime source of truth for what the cloudflared connector serves. The shipped JSON schema requires a uuid-shaped tunnel id, a secretRef credentials reference (never a literal path or JSON body inline), and an ingress rules array whose last entry is a catch-all rule returning http_status 404. The schema refuses ingress rules whose service URL binds to a host public interface.",
|
|
9
|
+
"responsibilities": [
|
|
10
|
+
"Ship the JSON schema at contributions/probes/tunnel-manifest.schema.json (AC-tunnel-manifestSchema).",
|
|
11
|
+
"Validate the fixture manifests at cloudflared/*/cloudflare/tunnels/*.yaml on every reviewer boot (AC-tunnel-manifestSchema, AC-tunnel-hostnameRoutes).",
|
|
12
|
+
"Refuse ingress rules that bind to a host public port (AC-tunnel-noPublicOrigin).",
|
|
13
|
+
"Refuse a manifest whose credentials reference is not a secretRef (AC-tunnel-manifestSchema, AC-tunnel-credentialsDiscipline)."
|
|
14
|
+
],
|
|
15
|
+
"internalStructure": "One JSON schema plus one probe module; the probe reads the resolved fixture root from env (defaulted to the canonical fixture path) and validates every tunnel yaml against the schema.",
|
|
16
|
+
"interfaces": [
|
|
17
|
+
{
|
|
18
|
+
"name": "default export",
|
|
19
|
+
"kind": "facade",
|
|
20
|
+
"summary": "Manifest schema-validate facade exported from the blueprint probe module."
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"dependencies": [],
|
|
24
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
25
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
26
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tacId": "TAC-4003-edge-cloudflare-tunnel-access-binding",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"tadId": "TAD-001",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"name": "Edge-cloudflare-tunnel Access AUD binding",
|
|
8
|
+
"purpose": "The blueprint reads the applying project sidecar rcf/blueprints/edge-cloudflare-tunnel.applied.json at apply time and inspects appliedCapabilities. When zeroTrustGate is present every ingress rule attaches an originRequest.access.aud block naming the Access AUD; when absent the block is omitted and the tunnel serves in public-hostname mode. The shape is a pure function of the sidecar and never silently degrades to open ingress.",
|
|
9
|
+
"responsibilities": [
|
|
10
|
+
"Ship the aud-presence-check probe module (AC-tunnel-accessGated, AC-tunnel-accessGatedRefuse).",
|
|
11
|
+
"Ship both fixture variants (access-gated and public-hostname) under cloudflared/sidecars/*.applied.json (AC-tunnel-accessGated).",
|
|
12
|
+
"Refuse when the gated variant drifts to open ingress (AC-tunnel-accessGatedRefuse)."
|
|
13
|
+
],
|
|
14
|
+
"internalStructure": "One probe module reading the sidecar file resolved from the fixture root and validating the ingress shape flips correctly.",
|
|
15
|
+
"interfaces": [
|
|
16
|
+
{
|
|
17
|
+
"name": "default export",
|
|
18
|
+
"kind": "facade",
|
|
19
|
+
"summary": "AUD-presence-check facade exported from the blueprint probe module."
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"dependencies": [],
|
|
23
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
24
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
25
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "edge-cloudflare-tunnel-US-39101",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "edge-cloudflare-tunnel-REQ-001",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"title": "Connector install shape switches on applied containerHost (compose-service default; systemd-unit fallback for bare cloudHost)",
|
|
8
|
+
"asA": "shelf consumer applying edge-cloudflare-tunnel v1.0.0 alongside deploy-hetzner-server (and optionally platform-docker-compose-host)",
|
|
9
|
+
"iWant": "the cloudflared connector to install as a compose service when the containerHost capability is applied and as a systemd unit when the host runs bare",
|
|
10
|
+
"soThat": "the runtime shape matches the applied platform rather than forcing a container onto a bare host or a bare service into a compose-managed stack",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-tunnel-connectorShape",
|
|
14
|
+
"description": "The applied fixture ships both variants under packages/rcf-lite/test/fixtures/hetzner-throwaway-server/cloudflared/{compose-service,systemd-unit}/; the compose-service variant defines a cloudflared service on the same web-net network as the T-2 web and caddy services with no host ports, restart unless-stopped and a healthcheck; the systemd-unit variant ships a cloudflared.service unit file per the vendor local-configuration-file docs.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "runtime"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"tacIds": [
|
|
20
|
+
"TAC-4001-edge-cloudflare-tunnel-connector-shape"
|
|
21
|
+
],
|
|
22
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
23
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "edge-cloudflare-tunnel-US-39102",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "edge-cloudflare-tunnel-REQ-001",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"title": "Cloudflared connector reports healthy on boot (tunnelConnectorUp fires with connector id, region, version)",
|
|
8
|
+
"asA": "shelf consumer applying edge-cloudflare-tunnel v1.0.0 on a cloudHost",
|
|
9
|
+
"iWant": "the connector to emit a tunnelConnectorUp event on boot with connector id, region and version metadata (metadata only)",
|
|
10
|
+
"soThat": "the observability sink proves the tunnel is up without a shell into the host",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-tunnel-connectorHealthy",
|
|
14
|
+
"description": "real-account-connector-healthy probe declares accountBound true (on CI_HAS_CLOUDFLARE_ACCOUNT AND CI_HAS_HETZNER_ACCOUNT) and records accountBoundSkipped when either is unset. When both are set the probe drives cloudflared tunnel info against the fixture tunnel and asserts at least one healthy connector plus a tunnelConnectorUp event with connector id, region and version.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "runtime"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"tacIds": [
|
|
20
|
+
"TAC-4001-edge-cloudflare-tunnel-connector-shape"
|
|
21
|
+
],
|
|
22
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
23
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "edge-cloudflare-tunnel-US-39103",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "edge-cloudflare-tunnel-REQ-002",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"title": "Tunnel manifest at cloudflare/tunnels/<name>.yaml validates on shape (uuid tunnel id, secretRef credentials, ingress with catch-all 404)",
|
|
8
|
+
"asA": "shelf consumer applying edge-cloudflare-tunnel v1.0.0",
|
|
9
|
+
"iWant": "the tunnel manifest to validate against a shipped JSON schema requiring a uuid-shaped tunnel id, a secretRef credentials reference, and an ingress rules array whose last entry is a mandatory catch-all http_status 404",
|
|
10
|
+
"soThat": "a mis-shaped manifest is caught at apply-time rather than at first inbound request",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-tunnel-manifestSchema",
|
|
14
|
+
"description": "manifest-schema-validate probe passes on the canonical fixture and fails when the fixture is mutated (SIMULATE_MANIFEST_INVALID_TUNNEL_ID, SIMULATE_MANIFEST_CREDENTIALS_INLINE, SIMULATE_MANIFEST_MISSING_CATCHALL) naming the offending field.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "runtime"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"tacIds": [
|
|
20
|
+
"TAC-4002-edge-cloudflare-tunnel-manifest-schema"
|
|
21
|
+
],
|
|
22
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
23
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "edge-cloudflare-tunnel-US-39104",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "edge-cloudflare-tunnel-REQ-002",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"title": "Cloudflared tunnel ingress validate exits 0 on the fixture manifest (routes public hostname to internal service URL)",
|
|
8
|
+
"asA": "shelf consumer applying edge-cloudflare-tunnel v1.0.0 with a compose service listening on an internal port",
|
|
9
|
+
"iWant": "the ingress rule mapping the public hostname to the internal service URL (http://web:8080 in the compose-service variant, http://127.0.0.1:8080 in the systemd-unit variant) to pass cloudflared tunnel ingress validate cleanly",
|
|
10
|
+
"soThat": "a rule that would fail at boot fails at gate instead",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-tunnel-hostnameRoutes",
|
|
14
|
+
"description": "cloudflared-config-lint probe runs cloudflared tunnel ingress validate against the fixture manifest (through the cloudflare/cloudflared:2026.8.3 container image when the binary is not on PATH) and exits 0 on the canonical fixture; SIMULATE_INGRESS_INVALID rewrites the service URL to an unresolvable scheme and the probe FAILS.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "runtime"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"tacIds": [
|
|
20
|
+
"TAC-4002-edge-cloudflare-tunnel-manifest-schema"
|
|
21
|
+
],
|
|
22
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
23
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "edge-cloudflare-tunnel-US-39105",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "edge-cloudflare-tunnel-REQ-003",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"title": "No public origin ports on the host (firewall stays 22 open plus 80/443 only when a reverse proxy is applied; manifest refuses origin-port request)",
|
|
8
|
+
"asA": "shelf consumer applying edge-cloudflare-tunnel v1.0.0 without a reverse proxy",
|
|
9
|
+
"iWant": "the host firewall to stay closed on 80/443 and the manifest schema to refuse any ingress rule whose service URL binds to a host public interface",
|
|
10
|
+
"soThat": "the tunnel remains the only path from the edge to the origin",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-tunnel-noPublicOrigin",
|
|
14
|
+
"description": "manifest-schema-validate refuses under SIMULATE_ORIGIN_PORT_OPEN when an ingress rule service URL binds to 0.0.0.0 or a host public interface; the T-1 firewall on the ci-throwaway manifest stays 22 open plus 80/443 open only when a reverse proxy is applied.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "runtime"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"tacIds": [
|
|
20
|
+
"TAC-4002-edge-cloudflare-tunnel-manifest-schema"
|
|
21
|
+
],
|
|
22
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
23
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "edge-cloudflare-tunnel-US-39106",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "edge-cloudflare-tunnel-REQ-004",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"title": "Access-gated composition: zeroTrustGate applied attaches originRequest.access.aud; absent omits the block",
|
|
8
|
+
"asA": "shelf consumer applying edge-cloudflare-tunnel v1.0.0 alongside edge-cloudflare-access v1.0.0",
|
|
9
|
+
"iWant": "every ingress rule to attach an originRequest.access block naming the Access AUD when zeroTrustGate is applied and to omit the block when it is not",
|
|
10
|
+
"soThat": "the shape switches purely on the applied capability set and never silently degrades",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-tunnel-accessGated",
|
|
14
|
+
"description": "aud-presence-check probe reads each variant sidecar and asserts the ingress rules attach originRequest.access.aud on the access-gated variant and omit the block on the public-hostname variant. The shape flip is a pure function of the sidecar contents.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "runtime"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"tacIds": [
|
|
20
|
+
"TAC-4003-edge-cloudflare-tunnel-access-binding"
|
|
21
|
+
],
|
|
22
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
23
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"usId": "edge-cloudflare-tunnel-US-39107",
|
|
3
|
+
"prdId": "PRD-001",
|
|
4
|
+
"reqId": "edge-cloudflare-tunnel-REQ-004",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"status": "approved",
|
|
7
|
+
"title": "Access-gated shape does not silently degrade to open ingress when the AUD block drifts absent (aud-presence-check refuses)",
|
|
8
|
+
"asA": "shelf consumer who applied edge-cloudflare-tunnel v1.0.0 with zeroTrustGate on",
|
|
9
|
+
"iWant": "a probe that asserts the ingress rule keeps its originRequest.access.aud block on every reapply and refuses if the block drifts absent",
|
|
10
|
+
"soThat": "a silent config drift to open ingress is caught at gate rather than at first probing",
|
|
11
|
+
"acceptanceCriteria": [
|
|
12
|
+
{
|
|
13
|
+
"id": "AC-tunnel-accessGatedRefuse",
|
|
14
|
+
"description": "aud-presence-check FAILS under SIMULATE_AUD_DROP when the gated fixture ingress drifts to omit the originRequest.access block. The finding names the affected rule and the missing AUD value.",
|
|
15
|
+
"testable": true,
|
|
16
|
+
"scope": "runtime"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"tacIds": [
|
|
20
|
+
"TAC-4003-edge-cloudflare-tunnel-access-binding"
|
|
21
|
+
],
|
|
22
|
+
"createdAt": "2026-09-08T00:00:00.000Z",
|
|
23
|
+
"updatedAt": "2026-09-08T00:00:00.000Z"
|
|
24
|
+
}
|