diff --git a/harnesses/contexts/cp-write-token/v1/harness.yaml b/harnesses/contexts/cp-write-token/v1/harness.yaml new file mode 100644 index 0000000..da1d6ee --- /dev/null +++ b/harnesses/contexts/cp-write-token/v1/harness.yaml @@ -0,0 +1,25 @@ +kind: context +name: cp-write-token +version: 1 +description: "Per-step scoped CP write token (AU-55, Option C): points CP_WRITE_TOKEN_FILE at the wfcw_ bearer the dispatcher delivers into the script container only (spec/typed-workflows.md WF-COMP-31c, spec/authentication.md AU-55b)" +requires: [] +provides: [] + +# AU-55b delivery: the token does NOT exist at manifest-submit time, so it is +# NOT a secrets_required / ESO mount and NOT under /variables/. The dispatcher +# mints it at dispatch (mint_step_tokens, scope='cp_write') and delivers it +# into the script container ONLY: +# - Kubernetes: item `cp-write-token` of the per-task wfvars-- +# Secret, projected at the path below (mode 0440 + pod fsGroup, so the +# non-root agent uid can read it). +# - Docker: create -> put_archive -> start on the script container (never the +# wfvars-* data volume). +# This layer therefore carries env only: no init script, no secrets_required, +# no files. It must stay that way; a secrets_required entry here would make the +# dispatcher wait on an ESO secret that never exists. +# +# The token is only valid on the two AU-57 routes for the run's own item +# (PATCH / GET /v1/work-items/{id}). It is NOT the AU-51 internal bearer; +# cp-service-token/v1 is deliberately not layered alongside it. +env: + CP_WRITE_TOKEN_FILE: /run/agent/cp-write-token/token diff --git a/tests/invariants/test_cp_write_token_layer.py b/tests/invariants/test_cp_write_token_layer.py new file mode 100644 index 0000000..4ecbc77 --- /dev/null +++ b/tests/invariants/test_cp_write_token_layer.py @@ -0,0 +1,37 @@ +"""cp-write-token/v1 context: env-only layer (WF-COMP-31c, AU-55b). + +The per-step wfcw_ token is delivered by the dispatcher into the script +container only (Secret projection on K8s, create -> put_archive -> start on +Docker). The layer must therefore set the env contract and nothing else: no +ESO secret mount, no init script, no context files. +""" +from __future__ import annotations + +from pathlib import Path + +import yaml + +_ROOT = Path(__file__).resolve().parents[2] / "harnesses" + + +def _load(kind_dir: str, name: str) -> dict: + return yaml.safe_load((_ROOT / kind_dir / name / "v1" / "harness.yaml").read_text()) + + +def test_cp_write_token_layer_sets_env_contract(): + doc = _load("contexts", "cp-write-token") + assert doc["kind"] == "context" + assert doc["name"] == "cp-write-token" + assert doc["version"] == 1 + assert doc["env"] == {"CP_WRITE_TOKEN_FILE": "/run/agent/cp-write-token/token"} + + +def test_cp_write_token_layer_is_env_only(): + doc = _load("contexts", "cp-write-token") + assert not doc.get("secrets_required"), "token is not an ESO mount (AU-55b)" + assert not doc.get("scripts"), "no init script: the backend delivers the file" + assert not doc.get("context_files") + assert not doc.get("write_targets") + assert doc.get("requires") == [] + assert doc.get("provides") == [] + assert not (_ROOT / "contexts" / "cp-write-token" / "v1" / "init.sh").exists()