From 356cb7a867150d89f3edab7b4cbb2d6aa68f9466 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 29 Jun 2026 12:46:15 +0800 Subject: [PATCH] feat(governance): add product manifest standard readiness --- .gitea/workflows/cd.yaml | 12 ++ apps/api/src/api/v1/agents.py | 32 ++++ .../product_awoooi_manifest_standard.py | 157 ++++++++++++++++++ ...st_product_awoooi_manifest_standard_api.py | 46 +++++ docs/LOGBOOK.md | 18 ++ ...uct-awoooi-manifest-standard.snapshot.json | 61 +++++++ .../product_awoooi_manifest_v1.schema.json | 97 +++++++++++ .../test_cd_controlled_runtime_profile.py | 8 + product.awoooi.yaml | 29 ++++ 9 files changed, 460 insertions(+) create mode 100644 apps/api/src/services/product_awoooi_manifest_standard.py create mode 100644 apps/api/tests/test_product_awoooi_manifest_standard_api.py create mode 100644 docs/operations/product-awoooi-manifest-standard.snapshot.json create mode 100644 docs/schemas/product_awoooi_manifest_v1.schema.json create mode 100644 product.awoooi.yaml diff --git a/.gitea/workflows/cd.yaml b/.gitea/workflows/cd.yaml index 4c404889..d5bcf7f0 100644 --- a/.gitea/workflows/cd.yaml +++ b/.gitea/workflows/cd.yaml @@ -198,8 +198,14 @@ jobs: ;; .gitea/workflows/cd.yaml) ;; + product.awoooi.yaml) + ;; docs/LOGBOOK.md) ;; + docs/schemas/product_awoooi_manifest_v1.schema.json) + ;; + docs/operations/product-awoooi-manifest-standard.snapshot.json) + ;; docs/operations/awoooi-priority-work-order-readback.snapshot.json) ;; docs/operations/p0-cicd-baseline-source-readiness.snapshot.json) @@ -220,6 +226,8 @@ jobs: ;; apps/api/src/services/p0_cicd_baseline_source_readiness.py) ;; + apps/api/src/services/product_awoooi_manifest_standard.py) + ;; apps/api/src/api/v1/platform/events.py) ;; apps/api/src/jobs/ai_slo_watchdog_job.py) @@ -256,6 +264,8 @@ jobs: ;; apps/api/tests/test_p0_cicd_baseline_source_readiness_api.py) ;; + apps/api/tests/test_product_awoooi_manifest_standard_api.py) + ;; apps/api/tests/test_trust_drift_watchdog.py) ;; apps/web/src/app/\[locale\]/governance/tabs/events-tab.tsx) @@ -368,6 +378,7 @@ jobs: src/services/delivery_closure_workbench.py \ src/services/heartbeat_report_service.py \ src/services/p0_cicd_baseline_source_readiness.py \ + src/services/product_awoooi_manifest_standard.py \ src/services/platform_operator_service.py \ src/services/telegram_gateway.py DATABASE_URL="${DATABASE_URL:-postgresql+asyncpg://ci:ci@localhost/ci}" \ @@ -380,6 +391,7 @@ jobs: tests/test_delivery_closure_workbench_api.py \ tests/e2e_network_test.py::TestHMACVerification::test_valid_hmac_signature \ tests/test_p0_cicd_baseline_source_readiness_api.py \ + tests/test_product_awoooi_manifest_standard_api.py \ tests/test_trust_drift_watchdog.py \ ../../ops/runner/test_read_public_gitea_actions_queue.py \ ../../ops/runner/test_cd_controlled_runtime_profile.py \ diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 80ad5a15..e23347e5 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -361,6 +361,9 @@ from src.services.package_supply_chain_inventory import ( from src.services.p0_cicd_baseline_source_readiness import ( load_latest_p0_cicd_baseline_source_readiness, ) +from src.services.product_awoooi_manifest_standard import ( + load_latest_product_awoooi_manifest_standard, +) from src.services.product_code_review_gate import ( load_latest_product_code_review_gate, ) @@ -967,6 +970,35 @@ async def get_delivery_closure_workbench() -> dict[str, Any]: ) from exc +@router.get( + "/product-awoooi-manifest-standard", + response_model=dict[str, Any], + summary="取得 P0-002 product.awoooi.yaml manifest standard readiness", + description=( + "讀取已提交的 P0-002 product.awoooi.yaml manifest standard readiness;" + "此端點只檢查 AWOOOI manifest、schema 與 Data/Security/Runtime contract refs。" + "它不建立 repo、不同步 refs、不觸發 workflow、不呼叫 GitHub、" + "不讀 secret、不操作 host / K8s。" + ), +) +async def get_product_awoooi_manifest_standard() -> dict[str, Any]: + """回傳 P0-002 product.awoooi.yaml manifest standard readiness 只讀快照。""" + try: + payload = await asyncio.to_thread(load_latest_product_awoooi_manifest_standard) + return redact_public_lan_topology(payload) + except FileNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(exc), + ) from exc + except (json.JSONDecodeError, ValueError) as exc: + logger.error("product_awoooi_manifest_standard_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="P0-002 product.awoooi.yaml manifest standard 快照無效", + ) from exc + + @router.get( "/p0-cicd-baseline-source-readiness", response_model=dict[str, Any], diff --git a/apps/api/src/services/product_awoooi_manifest_standard.py b/apps/api/src/services/product_awoooi_manifest_standard.py new file mode 100644 index 00000000..e275e335 --- /dev/null +++ b/apps/api/src/services/product_awoooi_manifest_standard.py @@ -0,0 +1,157 @@ +"""P0-002 product.awoooi.yaml manifest standard readiness.""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + +import yaml + +from src.services.snapshot_paths import default_operations_dir, resolve_repo_root + +_DEFAULT_OPERATIONS_DIR = default_operations_dir(Path(__file__)) +_SNAPSHOT_FILE = "product-awoooi-manifest-standard.snapshot.json" +_SCHEMA_VERSION = "product_awoooi_manifest_standard_readiness_v1" +_MANIFEST_SCHEMA_VERSION = "product_awoooi_manifest_v1" + + +def load_latest_product_awoooi_manifest_standard( + operations_dir: Path | None = None, +) -> dict[str, Any]: + """Load and validate the committed P0-002 product manifest standard.""" + directory = operations_dir or _DEFAULT_OPERATIONS_DIR + path = directory / _SNAPSHOT_FILE + with path.open(encoding="utf-8") as handle: + payload = json.load(handle) + + if not isinstance(payload, dict): + raise ValueError(f"{path}: expected JSON object") + repo_root = resolve_repo_root(path) + _enrich_manifest_readiness(payload, repo_root) + _require_schema(payload, str(path)) + _require_operation_boundaries(payload, str(path)) + _require_rollup_consistency(payload, str(path)) + return payload + + +def _enrich_manifest_readiness(payload: dict[str, Any], repo_root: Path) -> None: + manifest_meta = _dict(payload.get("manifest")) + manifest_path = repo_root / str(manifest_meta.get("path") or "") + schema_path = repo_root / str(manifest_meta.get("schema_ref") or "") + manifest = _load_yaml_object(manifest_path) + + manifest_meta["present"] = manifest_path.is_file() + manifest_meta["schema_present"] = schema_path.is_file() + manifest_meta["schema_version"] = str(manifest.get("schema_version") or "") + manifest_meta["product_id"] = str(_dict(manifest.get("product")).get("id") or "") + manifest_meta["source_control_authority"] = str( + _dict(manifest.get("source_control")).get("authority") or "" + ) + manifest_meta["github_status"] = str( + _dict(manifest.get("source_control")).get("github_status") or "" + ) + + sections = _strings(payload.get("required_sections")) + missing_sections = [section for section in sections if section not in manifest] + refs = _strings(payload.get("required_contract_refs")) + missing_refs = [ref for ref in refs if not (repo_root / ref).is_file()] + + rollups = _dict(payload.get("rollups")) + rollups["required_section_count"] = len(sections) + rollups["present_required_section_count"] = len(sections) - len(missing_sections) + rollups["missing_required_section_count"] = len(missing_sections) + rollups["missing_required_sections"] = missing_sections + rollups["required_contract_ref_count"] = len(refs) + rollups["present_contract_ref_count"] = len(refs) - len(missing_refs) + rollups["missing_contract_ref_count"] = len(missing_refs) + rollups["missing_contract_refs"] = missing_refs + rollups["hard_blocker_count"] = len(_list(payload.get("blockers"))) + rollups["next_action_count"] = len(_list(payload.get("next_actions"))) + ready_count = rollups["present_required_section_count"] + rollups[ + "present_contract_ref_count" + ] + required_count = rollups["required_section_count"] + rollups[ + "required_contract_ref_count" + ] + rollups["source_readiness_percent"] = round( + ready_count / max(required_count, 1) * 100 + ) + payload["status"] = ( + "ready_for_product_manifest_adoption" + if not missing_sections + and not missing_refs + and manifest_meta["present"] + and manifest_meta["schema_present"] + and manifest_meta["schema_version"] == _MANIFEST_SCHEMA_VERSION + and manifest_meta["source_control_authority"] == "gitea" + and manifest_meta["github_status"] == "stopped_retired_do_not_use" + else "blocked_product_manifest_source_missing" + ) + + +def _load_yaml_object(path: Path) -> dict[str, Any]: + if not path.is_file(): + return {} + with path.open(encoding="utf-8") as handle: + loaded = yaml.safe_load(handle) + if not isinstance(loaded, dict): + raise ValueError(f"{path}: expected YAML object") + return loaded + + +def _require_schema(payload: dict[str, Any], label: str) -> None: + actual = payload.get("schema_version") + if actual != _SCHEMA_VERSION: + raise ValueError( + f"{label}: expected schema_version={_SCHEMA_VERSION}, got {actual!r}" + ) + + +def _require_operation_boundaries(payload: dict[str, Any], label: str) -> None: + boundaries = _dict(payload.get("operation_boundaries")) + if boundaries.get("read_only_api_allowed") is not True: + raise ValueError(f"{label}: read_only_api_allowed must be true") + if boundaries.get("manifest_write_allowed") is not True: + raise ValueError(f"{label}: manifest_write_allowed must be true for P0-002 source") + blocked_flags = { + "remaining_product_repo_write_allowed", + "repo_creation_allowed", + "refs_sync_allowed", + "workflow_trigger_allowed", + "github_api_allowed", + "host_or_k8s_write_allowed", + "secret_read_allowed", + "raw_session_or_sqlite_read_allowed", + } + allowed = sorted(flag for flag in blocked_flags if boundaries.get(flag) is not False) + if allowed: + raise ValueError(f"{label}: operation boundaries must remain false: {allowed}") + + +def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None: + rollups = _dict(payload.get("rollups")) + sections = _strings(payload.get("required_sections")) + refs = _strings(payload.get("required_contract_refs")) + blockers = _list(payload.get("blockers")) + next_actions = _list(payload.get("next_actions")) + if rollups.get("required_section_count") != len(sections): + raise ValueError(f"{label}: required_section_count mismatch") + if rollups.get("required_contract_ref_count") != len(refs): + raise ValueError(f"{label}: required_contract_ref_count mismatch") + if rollups.get("hard_blocker_count") != len(blockers): + raise ValueError(f"{label}: hard_blocker_count mismatch") + if rollups.get("next_action_count") != len(next_actions): + raise ValueError(f"{label}: next_action_count mismatch") + + +def _dict(value: Any) -> dict[str, Any]: + return value if isinstance(value, dict) else {} + + +def _list(value: Any) -> list[Any]: + return value if isinstance(value, list) else [] + + +def _strings(value: Any) -> list[str]: + return [str(item) for item in _list(value) if str(item)] diff --git a/apps/api/tests/test_product_awoooi_manifest_standard_api.py b/apps/api/tests/test_product_awoooi_manifest_standard_api.py new file mode 100644 index 00000000..e333e6a6 --- /dev/null +++ b/apps/api/tests/test_product_awoooi_manifest_standard_api.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from src.api.v1.agents import router +from src.services.product_awoooi_manifest_standard import ( + load_latest_product_awoooi_manifest_standard, +) + + +def test_product_awoooi_manifest_standard_loader_reports_ready_manifest(): + payload = load_latest_product_awoooi_manifest_standard() + + assert payload["schema_version"] == "product_awoooi_manifest_standard_readiness_v1" + assert payload["status"] == "ready_for_product_manifest_adoption" + assert payload["priority"] == "P0-002" + assert payload["readback"]["workplan_id"] == "P0-002" + assert payload["manifest"]["present"] is True + assert payload["manifest"]["schema_present"] is True + assert payload["manifest"]["schema_version"] == "product_awoooi_manifest_v1" + assert payload["manifest"]["product_id"] == "awoooi" + assert payload["manifest"]["source_control_authority"] == "gitea" + assert payload["manifest"]["github_status"] == "stopped_retired_do_not_use" + assert payload["rollups"]["source_readiness_percent"] == 100 + assert payload["rollups"]["missing_required_section_count"] == 0 + assert payload["rollups"]["missing_contract_ref_count"] == 0 + assert payload["operation_boundaries"]["read_only_api_allowed"] is True + assert payload["operation_boundaries"]["remaining_product_repo_write_allowed"] is False + assert payload["operation_boundaries"]["github_api_allowed"] is False + assert payload["operation_boundaries"]["secret_read_allowed"] is False + + +def test_product_awoooi_manifest_standard_endpoint_returns_snapshot(): + app = FastAPI() + app.include_router(router, prefix="/api/v1") + client = TestClient(app) + + response = client.get("/api/v1/agents/product-awoooi-manifest-standard") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "product_awoooi_manifest_standard_readiness_v1" + assert data["status"] == "ready_for_product_manifest_adoption" + assert data["rollups"]["source_readiness_percent"] == 100 + assert data["operation_boundaries"]["repo_creation_allowed"] is False diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 7478b0c3..4d127121 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,21 @@ +## 2026-06-29 — 13:05 P0-002 product.awoooi.yaml manifest standard readiness + +**完成內容**: +- 新增根目錄 `product.awoooi.yaml`,作為 AWOOOI 自身產品 manifest 標準樣板;source control authority 固定為 Gitea,GitHub 狀態固定 `stopped_retired_do_not_use`。 +- 新增 `docs/schemas/product_awoooi_manifest_v1.schema.json` 與 `docs/operations/product-awoooi-manifest-standard.snapshot.json`。 +- 新增 `product_awoooi_manifest_standard` loader 與 `/api/v1/agents/product-awoooi-manifest-standard` 只讀 API,檢查 manifest、schema、Data / Security / Runtime contract refs 與 operation boundaries。 +- Gitea CD controlled-runtime profile 納入 P0-002 manifest/schema/snapshot/service/test,避免回退 full API suite;`ops/runner/test_cd_controlled_runtime_profile.py` 補測試鎖住此行為。 + +**驗證結果**: +- `python3.11 -m json.tool docs/schemas/product_awoooi_manifest_v1.schema.json docs/operations/product-awoooi-manifest-standard.snapshot.json`:通過。 +- `python3.11` 解析 `product.awoooi.yaml`:通過。 +- `python3.11 -m py_compile apps/api/src/services/product_awoooi_manifest_standard.py apps/api/src/api/v1/agents.py`:通過。 +- `DATABASE_URL=postgresql+asyncpg://ci:ci@localhost/ci PYTHONPATH=apps/api python3.11 -m pytest apps/api/tests/test_product_awoooi_manifest_standard_api.py apps/api/tests/test_p0_cicd_baseline_source_readiness_api.py ops/runner/test_cd_controlled_runtime_profile.py -q`:`7 passed`。 +- CD controlled-runtime 同等測試清單:`78 passed`。 +- `node scripts/ci/check-gitea-step-env-secrets.js`、`python3.11 ops/runner/guard-gitea-runner-pressure.py --root .`、`git diff --check`:通過。 + +**邊界**:未使用 GitHub / `gh` / GitHub API;未建立 repo;未 sync refs;未 workflow_dispatch;未讀 token / cookie / session / secret / auth / `.env`;未操作 host / Docker / K8s / DB;未 force push。 + ## 2026-06-29 — 12:06 P0-004 CI/CD baseline source readiness readback **完成內容**: diff --git a/docs/operations/product-awoooi-manifest-standard.snapshot.json b/docs/operations/product-awoooi-manifest-standard.snapshot.json new file mode 100644 index 00000000..a137761f --- /dev/null +++ b/docs/operations/product-awoooi-manifest-standard.snapshot.json @@ -0,0 +1,61 @@ +{ + "schema_version": "product_awoooi_manifest_standard_readiness_v1", + "generated_at": "2026-06-29T13:05:00+08:00", + "status": "ready_for_product_manifest_adoption", + "priority": "P0-002", + "scope": "product_manifest_standard", + "readback": { + "workplan_id": "P0-002", + "workplan_title": "建立 product.awoooi.yaml 產品 manifest 標準", + "scorecard_completion_percent": 55, + "safe_next_step": "adopt_product_manifest_schema_for_remaining_products_without_repo_creation_or_secret_read" + }, + "manifest": { + "path": "product.awoooi.yaml", + "schema_ref": "docs/schemas/product_awoooi_manifest_v1.schema.json", + "product_id": "awoooi", + "source_control_authority": "gitea", + "github_status": "stopped_retired_do_not_use" + }, + "required_sections": [ + "product", + "source_control", + "runtime", + "contracts", + "operation_boundaries" + ], + "required_contract_refs": [ + "docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json", + "docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md", + "docs/evaluations/runtime_surface_inventory_2026-06-05.json" + ], + "blockers": [], + "next_actions": [ + "add_product_awoooi_manifest_to_remaining_product_repos_after_gitea_inventory_source_is_available", + "keep_github_status_stopped_retired_do_not_use_for_all_manifests", + "project_manifest_fields_to_product_governance_ui_after_schema_adoption" + ], + "rollups": { + "required_section_count": 5, + "present_required_section_count": 5, + "missing_required_section_count": 0, + "required_contract_ref_count": 3, + "present_contract_ref_count": 3, + "missing_contract_ref_count": 0, + "source_readiness_percent": 100, + "hard_blocker_count": 0, + "next_action_count": 3 + }, + "operation_boundaries": { + "read_only_api_allowed": true, + "manifest_write_allowed": true, + "remaining_product_repo_write_allowed": false, + "repo_creation_allowed": false, + "refs_sync_allowed": false, + "workflow_trigger_allowed": false, + "github_api_allowed": false, + "host_or_k8s_write_allowed": false, + "secret_read_allowed": false, + "raw_session_or_sqlite_read_allowed": false + } +} diff --git a/docs/schemas/product_awoooi_manifest_v1.schema.json b/docs/schemas/product_awoooi_manifest_v1.schema.json new file mode 100644 index 00000000..5dd2e83a --- /dev/null +++ b/docs/schemas/product_awoooi_manifest_v1.schema.json @@ -0,0 +1,97 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "product_awoooi_manifest_v1", + "title": "AWOOOI product manifest", + "type": "object", + "additionalProperties": false, + "required": [ + "schema_version", + "product", + "source_control", + "runtime", + "contracts", + "operation_boundaries" + ], + "properties": { + "schema_version": { + "const": "product_awoooi_manifest_v1" + }, + "product": { + "type": "object", + "additionalProperties": false, + "required": ["id", "display_name", "owner", "lifecycle", "priority"], + "properties": { + "id": { "type": "string", "minLength": 1 }, + "display_name": { "type": "string", "minLength": 1 }, + "owner": { "type": "string", "minLength": 1 }, + "lifecycle": { "enum": ["production", "development", "retired"] }, + "priority": { "enum": ["P0", "P1", "P2", "P3"] } + } + }, + "source_control": { + "type": "object", + "additionalProperties": false, + "required": [ + "authority", + "repo", + "development_branch", + "production_branch", + "github_status" + ], + "properties": { + "authority": { "const": "gitea" }, + "repo": { "type": "string", "pattern": "^[^/]+/[^/]+$" }, + "development_branch": { "type": "string", "minLength": 1 }, + "production_branch": { "type": "string", "minLength": 1 }, + "github_status": { "const": "stopped_retired_do_not_use" } + } + }, + "runtime": { + "type": "object", + "additionalProperties": false, + "required": ["production_url", "delivery_workbench_api"], + "properties": { + "production_url": { "type": "string", "pattern": "^https://[^\\s]+$" }, + "delivery_workbench_api": { "type": "string", "pattern": "^/api/v1/" } + } + }, + "contracts": { + "type": "object", + "additionalProperties": false, + "required": [ + "data_contract_ref", + "security_contract_ref", + "runtime_inventory_ref" + ], + "properties": { + "data_contract_ref": { "type": "string", "minLength": 1 }, + "security_contract_ref": { "type": "string", "minLength": 1 }, + "runtime_inventory_ref": { "type": "string", "minLength": 1 } + } + }, + "operation_boundaries": { + "type": "object", + "additionalProperties": false, + "required": [ + "read_only_manifest_allowed", + "github_api_allowed", + "repo_creation_allowed", + "refs_sync_allowed", + "workflow_trigger_allowed", + "host_or_k8s_write_allowed", + "secret_read_allowed", + "raw_session_or_sqlite_read_allowed" + ], + "properties": { + "read_only_manifest_allowed": { "const": true }, + "github_api_allowed": { "const": false }, + "repo_creation_allowed": { "const": false }, + "refs_sync_allowed": { "const": false }, + "workflow_trigger_allowed": { "const": false }, + "host_or_k8s_write_allowed": { "const": false }, + "secret_read_allowed": { "const": false }, + "raw_session_or_sqlite_read_allowed": { "const": false } + } + } + } +} diff --git a/ops/runner/test_cd_controlled_runtime_profile.py b/ops/runner/test_cd_controlled_runtime_profile.py index 60871f08..30ac34dd 100644 --- a/ops/runner/test_cd_controlled_runtime_profile.py +++ b/ops/runner/test_cd_controlled_runtime_profile.py @@ -18,6 +18,14 @@ def test_web_changes_stay_on_controlled_runtime_profile() -> None: assert "UI-only changes are verified by the" in text +def test_product_manifest_changes_stay_on_controlled_runtime_profile() -> None: + text = _workflow_text() + assert "product.awoooi.yaml)" in text + assert "docs/schemas/product_awoooi_manifest_v1.schema.json)" in text + assert "apps/api/src/services/product_awoooi_manifest_standard.py)" in text + assert "tests/test_product_awoooi_manifest_standard_api.py" in text + + def test_controlled_runtime_skips_b5_before_docker_socket_use() -> None: text = _workflow_text() b5_start = text.index("- name: Integration Tests (B5") diff --git a/product.awoooi.yaml b/product.awoooi.yaml new file mode 100644 index 00000000..3704b79e --- /dev/null +++ b/product.awoooi.yaml @@ -0,0 +1,29 @@ +schema_version: product_awoooi_manifest_v1 +product: + id: awoooi + display_name: AWOOOI / AiOps + owner: wooo + lifecycle: production + priority: P0 +source_control: + authority: gitea + repo: wooo/awoooi + development_branch: dev + production_branch: main + github_status: stopped_retired_do_not_use +runtime: + production_url: https://awoooi.wooo.work + delivery_workbench_api: /api/v1/agents/delivery-closure-workbench +contracts: + data_contract_ref: docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json + security_contract_ref: docs/security/SECURITY-SUPPLY-CHAIN-PROGRESS.md + runtime_inventory_ref: docs/evaluations/runtime_surface_inventory_2026-06-05.json +operation_boundaries: + read_only_manifest_allowed: true + github_api_allowed: false + repo_creation_allowed: false + refs_sync_allowed: false + workflow_trigger_allowed: false + host_or_k8s_write_allowed: false + secret_read_allowed: false + raw_session_or_sqlite_read_allowed: false