diff --git a/catalog/glas-claude-agent-dev-anthropic.yaml b/catalog/glas-claude-agent-dev-anthropic.yaml index 42e454e..f214b0e 100644 --- a/catalog/glas-claude-agent-dev-anthropic.yaml +++ b/catalog/glas-claude-agent-dev-anthropic.yaml @@ -4,7 +4,8 @@ org: coulomb repo: sand-boxer stage: prod description: Catalog-bound metered owner with Activity Core worker-token companion. - KV custody exists; native runtime approval and activation remain SECRETS-WP-0009-T03. + The 2026-09-27 corrected owner pin requires reviewed host installation; the old + 200k/32k owner is not admitted by this catalog. Native activation remains SECRETS-WP-0009-T03. mount: platform path: workloads/glas-harness/claude-agent-dev mount_management: existing @@ -50,7 +51,7 @@ delivery_config: sha256: e50d468e8b0adfb05733f5b87b3cff34829c4a8c1aea50c865aa8bdfe4bb150f private: false /home/tegwick/hfact/owner-metered/owner.json: - sha256: 0e263f8299a42f63caf3595ff3eb7adc10354673f7b5125e0811def3bea7c274 + sha256: e0d3fb84649fdca302eccd9415f3cc2beaee84bf07bd83205cdffbe93e5573bc private: true /home/tegwick/hfact/owner-metered/spend-policy.json: sha256: f31c585916de1ea8bd8e48c72421803dfde1015e6201704b9320f77d2c545d9c diff --git a/docs/evidence/2026-09-27-installed-policy-refusal.json b/docs/evidence/2026-09-27-installed-policy-refusal.json new file mode 100644 index 0000000..cdc4801 --- /dev/null +++ b/docs/evidence/2026-09-27-installed-policy-refusal.json @@ -0,0 +1,694 @@ +{ + "schema": "metered-activation-review/v1", + "status": "review-only", + "dispatch_enabled": false, + "owner_digest": "46ab4f3fab1c5996ee61c96061b90518d625ffb3fa0966c9bbf7550f422b884b", + "policy_review": { + "passed": false, + "failures": [ + "input_reservation_below_provider_context", + "output_limit_below_observed_cli_request", + "observed_cli_beta_not_admitted" + ], + "maximum_request_microusd": 1120000, + "maximum_request_eur_at_configured_fx": "0.9744", + "full_output_requests_that_fit": 5, + "provider_facts_ref": "https://platform.claude.com/docs/en/models/sonnet-5/whats-new-sonnet-5", + "authority_granted": false, + "remaining_acceptance": [ + "FX upper bound and validity", + "live CLI/beta compatibility", + "native delivery and runtime admission" + ] + }, + "plans": [ + { + "catalog_id": "glas-claude-agent-dev-anthropic", + "stage": "prod", + "decision_id": "", + "policy_name": "se-prod-glas-claude-agent-dev-anthropic", + "role_name": "se-prod-glas-claude-agent-dev-anthropic", + "actions": [ + { + "kind": "kv-mount-check", + "target": "platform", + "detail": { + "type": "kv-v2", + "management": "existing", + "mutation": "none" + } + }, + { + "kind": "policy", + "target": "se-prod-glas-claude-agent-dev-anthropic", + "detail": { + "paths": "platform/data/workloads/glas-harness/claude-agent-dev" + } + }, + { + "kind": "approle", + "target": "se-prod-glas-claude-agent-dev-anthropic", + "detail": { + "token_policies": "se-prod-glas-claude-agent-dev-anthropic", + "auth": "approle", + "token_ttl": "5m", + "token_max_ttl": "15m", + "token_num_uses": 8, + "secret_id_ttl": "5m", + "secret_id_num_uses": 1 + } + } + ], + "policy_hcl": "# Generated by secrets-engine for policy \"se-prod-glas-claude-agent-dev-anthropic\"\n\npath \"platform/data/workloads/glas-harness/claude-agent-dev\" {\n capabilities = [\"read\"]\n}\n" + }, + { + "catalog_id": "activity-core-metered-worker-token", + "stage": "prod", + "decision_id": "", + "policy_name": "se-prod-activity-core-metered-worker-token", + "role_name": "se-prod-activity-core-metered-worker-token", + "actions": [ + { + "kind": "kv-mount-check", + "target": "platform", + "detail": { + "type": "kv-v2", + "management": "existing", + "mutation": "none" + } + }, + { + "kind": "policy", + "target": "se-prod-activity-core-metered-worker-token", + "detail": { + "paths": "platform/data/workloads/activity-core/ops-run-workers/rein-aharness-metered-railiance01" + } + }, + { + "kind": "approle", + "target": "se-prod-activity-core-metered-worker-token", + "detail": { + "token_policies": "se-prod-activity-core-metered-worker-token", + "auth": "approle", + "token_ttl": "5m", + "token_max_ttl": "15m", + "token_num_uses": 8, + "secret_id_ttl": "5m", + "secret_id_num_uses": 1 + } + } + ], + "policy_hcl": "# Generated by secrets-engine for policy \"se-prod-activity-core-metered-worker-token\"\n\npath \"platform/data/workloads/activity-core/ops-run-workers/rein-aharness-metered-railiance01\" {\n capabilities = [\"read\"]\n}\n" + } + ], + "actions": [ + { + "catalog": "glas-claude-agent-dev-anthropic", + "action": "apply", + "request": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service" + }, + "action": "apply", + "resource": { + "id": "catalog:glas-claude-agent-dev-anthropic", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "attributes": { + "stage": "prod", + "fields": [], + "policy_targets": [ + "se-prod-glas-claude-agent-dev-anthropic" + ], + "auth_targets": [ + "se-prod-glas-claude-agent-dev-anthropic" + ] + } + }, + "context": { + "purpose": "Owner-admitted glas-harness agt run through the reviewed local profile; no caller-facing key fetch", + "catalog_target": { + "kind": "kv", + "org": "coulomb", + "repo": "sand-boxer", + "mount": "platform", + "path": "workloads/glas-harness/claude-agent-dev", + "fields": [ + "ANTHROPIC_API_KEY" + ], + "mount_management": "existing", + "consumers": [ + { + "name": "sand-boxer-glas-agent-dev", + "auth": "approle", + "claim": "catalog:glas-claude-agent-dev-anthropic", + "purpose": "Owner-admitted glas-harness agt run through the reviewed local profile; no caller-facing key fetch" + } + ], + "delivery_modes": [ + "exec-env", + "read-check" + ], + "delivery_auth": { + "method": "approle", + "management": "engine", + "policy_name": "se-prod-glas-claude-agent-dev-anthropic", + "role_name": "se-prod-glas-claude-agent-dev-anthropic", + "metadata_read": false, + "token_ttl": "5m", + "token_max_ttl": "15m", + "secret_id_ttl": "5m", + "secret_id_num_uses": 1, + "token_num_uses": 8 + }, + "delivery_config": { + "exec_owner": { + "status": "configured", + "owner": "rein-aharness MessagesOwner (metered-once) with sand-boxer runtime boundary", + "command": [ + "/home/tegwick/.helixforge-factory/runtimes/b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969/bin/python3", + "-I", + "-B", + "-m", + "rein_aharness.cli", + "metered-once", + "--owner-config", + "/home/tegwick/hfact/owner-metered/owner.json" + ], + "cwd": "/home/tegwick/hfact/owner-metered", + "environment": { + "PATH": "/usr/bin:/bin", + "LANG": "C.UTF-8", + "HOME": "/home/tegwick", + "ACTIVITY_CORE_URL": "http://127.0.0.1:8010", + "AGENT_HARNESS_WORKER_ID": "rein-aharness-metered@railiance01", + "AGENT_HARNESS_OPS_LABELS": "hfact-metered", + "AGENT_HARNESS_OPS_LABELS_MODE": "all", + "AGENT_HARNESS_EXECUTION_PROJECT": "prj-helixforge-factory", + "AGENT_HARNESS_REQUIRE_SPEND_ADMISSION": "1", + "AGENT_HARNESS_REQUIRE_REQUEST_ADMISSION": "1", + "AGENT_HARNESS_SPEND_POLICY": "/home/tegwick/hfact/owner-metered/spend-policy.json", + "AGENT_HARNESS_SPEND_LEDGER": "/home/tegwick/hfact/owner-metered/spend.sqlite3", + "AGENT_HARNESS_REPO_MAP": "{\"hfact-glas-proof\":\"/home/tegwick/hfact/targets/hfact-glas-proof\"}" + }, + "files": { + "/home/tegwick/.helixforge-factory/runtimes/b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969/bin/python3": { + "sha256": "e50d468e8b0adfb05733f5b87b3cff34829c4a8c1aea50c865aa8bdfe4bb150f", + "private": false + }, + "/home/tegwick/hfact/owner-metered/owner.json": { + "sha256": "0e263f8299a42f63caf3595ff3eb7adc10354673f7b5125e0811def3bea7c274", + "private": true + }, + "/home/tegwick/hfact/owner-metered/spend-policy.json": { + "sha256": "f31c585916de1ea8bd8e48c72421803dfde1015e6201704b9320f77d2c545d9c", + "private": true + } + }, + "companions": [ + { + "catalog": "activity-core-metered-worker-token", + "field": "token", + "env": "ACTIVITY_CORE_WORKER_TOKEN" + } + ] + } + }, + "auth_capability": {}, + "workload_delivery": [] + }, + "human_control": true + } + } + }, + { + "catalog": "glas-claude-agent-dev-anthropic", + "action": "verify", + "request": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service" + }, + "action": "verify", + "resource": { + "id": "catalog:glas-claude-agent-dev-anthropic", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "attributes": { + "stage": "prod", + "fields": [ + "ANTHROPIC_API_KEY" + ], + "policy_targets": [ + "se-prod-glas-claude-agent-dev-anthropic" + ], + "auth_targets": [ + "se-prod-glas-claude-agent-dev-anthropic" + ] + } + }, + "context": { + "purpose": "Owner-admitted glas-harness agt run through the reviewed local profile; no caller-facing key fetch", + "catalog_target": { + "kind": "kv", + "org": "coulomb", + "repo": "sand-boxer", + "mount": "platform", + "path": "workloads/glas-harness/claude-agent-dev", + "fields": [ + "ANTHROPIC_API_KEY" + ], + "mount_management": "existing", + "consumers": [ + { + "name": "sand-boxer-glas-agent-dev", + "auth": "approle", + "claim": "catalog:glas-claude-agent-dev-anthropic", + "purpose": "Owner-admitted glas-harness agt run through the reviewed local profile; no caller-facing key fetch" + } + ], + "delivery_modes": [ + "exec-env", + "read-check" + ], + "delivery_auth": { + "method": "approle", + "management": "engine", + "policy_name": "se-prod-glas-claude-agent-dev-anthropic", + "role_name": "se-prod-glas-claude-agent-dev-anthropic", + "metadata_read": false, + "token_ttl": "5m", + "token_max_ttl": "15m", + "secret_id_ttl": "5m", + "secret_id_num_uses": 1, + "token_num_uses": 8 + }, + "delivery_config": { + "exec_owner": { + "status": "configured", + "owner": "rein-aharness MessagesOwner (metered-once) with sand-boxer runtime boundary", + "command": [ + "/home/tegwick/.helixforge-factory/runtimes/b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969/bin/python3", + "-I", + "-B", + "-m", + "rein_aharness.cli", + "metered-once", + "--owner-config", + "/home/tegwick/hfact/owner-metered/owner.json" + ], + "cwd": "/home/tegwick/hfact/owner-metered", + "environment": { + "PATH": "/usr/bin:/bin", + "LANG": "C.UTF-8", + "HOME": "/home/tegwick", + "ACTIVITY_CORE_URL": "http://127.0.0.1:8010", + "AGENT_HARNESS_WORKER_ID": "rein-aharness-metered@railiance01", + "AGENT_HARNESS_OPS_LABELS": "hfact-metered", + "AGENT_HARNESS_OPS_LABELS_MODE": "all", + "AGENT_HARNESS_EXECUTION_PROJECT": "prj-helixforge-factory", + "AGENT_HARNESS_REQUIRE_SPEND_ADMISSION": "1", + "AGENT_HARNESS_REQUIRE_REQUEST_ADMISSION": "1", + "AGENT_HARNESS_SPEND_POLICY": "/home/tegwick/hfact/owner-metered/spend-policy.json", + "AGENT_HARNESS_SPEND_LEDGER": "/home/tegwick/hfact/owner-metered/spend.sqlite3", + "AGENT_HARNESS_REPO_MAP": "{\"hfact-glas-proof\":\"/home/tegwick/hfact/targets/hfact-glas-proof\"}" + }, + "files": { + "/home/tegwick/.helixforge-factory/runtimes/b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969/bin/python3": { + "sha256": "e50d468e8b0adfb05733f5b87b3cff34829c4a8c1aea50c865aa8bdfe4bb150f", + "private": false + }, + "/home/tegwick/hfact/owner-metered/owner.json": { + "sha256": "0e263f8299a42f63caf3595ff3eb7adc10354673f7b5125e0811def3bea7c274", + "private": true + }, + "/home/tegwick/hfact/owner-metered/spend-policy.json": { + "sha256": "f31c585916de1ea8bd8e48c72421803dfde1015e6201704b9320f77d2c545d9c", + "private": true + } + }, + "companions": [ + { + "catalog": "activity-core-metered-worker-token", + "field": "token", + "env": "ACTIVITY_CORE_WORKER_TOKEN" + } + ] + } + }, + "auth_capability": {}, + "workload_delivery": [] + }, + "human_control": true + } + } + }, + { + "catalog": "glas-claude-agent-dev-anthropic", + "action": "exec", + "request": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service" + }, + "action": "exec", + "resource": { + "id": "catalog:glas-claude-agent-dev-anthropic", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "attributes": { + "stage": "prod", + "fields": [ + "ANTHROPIC_API_KEY" + ], + "policy_targets": [ + "se-prod-glas-claude-agent-dev-anthropic" + ], + "auth_targets": [ + "se-prod-glas-claude-agent-dev-anthropic" + ] + } + }, + "context": { + "purpose": "Owner-admitted glas-harness agt run through the reviewed local profile; no caller-facing key fetch", + "catalog_target": { + "kind": "kv", + "org": "coulomb", + "repo": "sand-boxer", + "mount": "platform", + "path": "workloads/glas-harness/claude-agent-dev", + "fields": [ + "ANTHROPIC_API_KEY" + ], + "mount_management": "existing", + "consumers": [ + { + "name": "sand-boxer-glas-agent-dev", + "auth": "approle", + "claim": "catalog:glas-claude-agent-dev-anthropic", + "purpose": "Owner-admitted glas-harness agt run through the reviewed local profile; no caller-facing key fetch" + } + ], + "delivery_modes": [ + "exec-env", + "read-check" + ], + "delivery_auth": { + "method": "approle", + "management": "engine", + "policy_name": "se-prod-glas-claude-agent-dev-anthropic", + "role_name": "se-prod-glas-claude-agent-dev-anthropic", + "metadata_read": false, + "token_ttl": "5m", + "token_max_ttl": "15m", + "secret_id_ttl": "5m", + "secret_id_num_uses": 1, + "token_num_uses": 8 + }, + "delivery_config": { + "exec_owner": { + "status": "configured", + "owner": "rein-aharness MessagesOwner (metered-once) with sand-boxer runtime boundary", + "command": [ + "/home/tegwick/.helixforge-factory/runtimes/b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969/bin/python3", + "-I", + "-B", + "-m", + "rein_aharness.cli", + "metered-once", + "--owner-config", + "/home/tegwick/hfact/owner-metered/owner.json" + ], + "cwd": "/home/tegwick/hfact/owner-metered", + "environment": { + "PATH": "/usr/bin:/bin", + "LANG": "C.UTF-8", + "HOME": "/home/tegwick", + "ACTIVITY_CORE_URL": "http://127.0.0.1:8010", + "AGENT_HARNESS_WORKER_ID": "rein-aharness-metered@railiance01", + "AGENT_HARNESS_OPS_LABELS": "hfact-metered", + "AGENT_HARNESS_OPS_LABELS_MODE": "all", + "AGENT_HARNESS_EXECUTION_PROJECT": "prj-helixforge-factory", + "AGENT_HARNESS_REQUIRE_SPEND_ADMISSION": "1", + "AGENT_HARNESS_REQUIRE_REQUEST_ADMISSION": "1", + "AGENT_HARNESS_SPEND_POLICY": "/home/tegwick/hfact/owner-metered/spend-policy.json", + "AGENT_HARNESS_SPEND_LEDGER": "/home/tegwick/hfact/owner-metered/spend.sqlite3", + "AGENT_HARNESS_REPO_MAP": "{\"hfact-glas-proof\":\"/home/tegwick/hfact/targets/hfact-glas-proof\"}" + }, + "files": { + "/home/tegwick/.helixforge-factory/runtimes/b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969/bin/python3": { + "sha256": "e50d468e8b0adfb05733f5b87b3cff34829c4a8c1aea50c865aa8bdfe4bb150f", + "private": false + }, + "/home/tegwick/hfact/owner-metered/owner.json": { + "sha256": "0e263f8299a42f63caf3595ff3eb7adc10354673f7b5125e0811def3bea7c274", + "private": true + }, + "/home/tegwick/hfact/owner-metered/spend-policy.json": { + "sha256": "f31c585916de1ea8bd8e48c72421803dfde1015e6201704b9320f77d2c545d9c", + "private": true + } + }, + "companions": [ + { + "catalog": "activity-core-metered-worker-token", + "field": "token", + "env": "ACTIVITY_CORE_WORKER_TOKEN" + } + ] + } + }, + "auth_capability": {}, + "workload_delivery": [] + }, + "human_control": true, + "exec_owner_sha256": "46ab4f3fab1c5996ee61c96061b90518d625ffb3fa0966c9bbf7550f422b884b" + } + } + }, + { + "catalog": "activity-core-metered-worker-token", + "action": "apply", + "request": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service" + }, + "action": "apply", + "resource": { + "id": "catalog:activity-core-metered-worker-token", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "attributes": { + "stage": "prod", + "fields": [], + "policy_targets": [ + "se-prod-activity-core-metered-worker-token" + ], + "auth_targets": [ + "se-prod-activity-core-metered-worker-token" + ] + } + }, + "context": { + "purpose": "Claim the admitted hfact-metered ops_run as rein-aharness-metered@railiance01 inside the catalog-bound Glas metered owner", + "catalog_target": { + "kind": "kv", + "org": "coulomb", + "repo": "activity-core", + "mount": "platform", + "path": "workloads/activity-core/ops-run-workers/rein-aharness-metered-railiance01", + "fields": [ + "token" + ], + "mount_management": "existing", + "consumers": [ + { + "name": "rein-aharness-metered-railiance01", + "auth": "approle", + "claim": "catalog:activity-core-metered-worker-token", + "purpose": "Claim the admitted hfact-metered ops_run as rein-aharness-metered@railiance01 inside the catalog-bound Glas metered owner" + } + ], + "delivery_modes": [ + "exec-env" + ], + "delivery_auth": { + "method": "approle", + "management": "engine", + "policy_name": "se-prod-activity-core-metered-worker-token", + "role_name": "se-prod-activity-core-metered-worker-token", + "metadata_read": false, + "token_ttl": "5m", + "token_max_ttl": "15m", + "secret_id_ttl": "5m", + "secret_id_num_uses": 1, + "token_num_uses": 8 + }, + "delivery_config": { + "companion_of": [ + "glas-claude-agent-dev-anthropic" + ] + }, + "auth_capability": {}, + "workload_delivery": [] + } + } + } + }, + { + "catalog": "activity-core-metered-worker-token", + "action": "verify", + "request": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service" + }, + "action": "verify", + "resource": { + "id": "catalog:activity-core-metered-worker-token", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "attributes": { + "stage": "prod", + "fields": [ + "token" + ], + "policy_targets": [ + "se-prod-activity-core-metered-worker-token" + ], + "auth_targets": [ + "se-prod-activity-core-metered-worker-token" + ] + } + }, + "context": { + "purpose": "Claim the admitted hfact-metered ops_run as rein-aharness-metered@railiance01 inside the catalog-bound Glas metered owner", + "catalog_target": { + "kind": "kv", + "org": "coulomb", + "repo": "activity-core", + "mount": "platform", + "path": "workloads/activity-core/ops-run-workers/rein-aharness-metered-railiance01", + "fields": [ + "token" + ], + "mount_management": "existing", + "consumers": [ + { + "name": "rein-aharness-metered-railiance01", + "auth": "approle", + "claim": "catalog:activity-core-metered-worker-token", + "purpose": "Claim the admitted hfact-metered ops_run as rein-aharness-metered@railiance01 inside the catalog-bound Glas metered owner" + } + ], + "delivery_modes": [ + "exec-env" + ], + "delivery_auth": { + "method": "approle", + "management": "engine", + "policy_name": "se-prod-activity-core-metered-worker-token", + "role_name": "se-prod-activity-core-metered-worker-token", + "metadata_read": false, + "token_ttl": "5m", + "token_max_ttl": "15m", + "secret_id_ttl": "5m", + "secret_id_num_uses": 1, + "token_num_uses": 8 + }, + "delivery_config": { + "companion_of": [ + "glas-claude-agent-dev-anthropic" + ] + }, + "auth_capability": {}, + "workload_delivery": [] + } + } + } + }, + { + "catalog": "activity-core-metered-worker-token", + "action": "exec", + "request": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service" + }, + "action": "exec", + "resource": { + "id": "catalog:activity-core-metered-worker-token", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "attributes": { + "stage": "prod", + "fields": [ + "token" + ], + "policy_targets": [ + "se-prod-activity-core-metered-worker-token" + ], + "auth_targets": [ + "se-prod-activity-core-metered-worker-token" + ] + } + }, + "context": { + "purpose": "Claim the admitted hfact-metered ops_run as rein-aharness-metered@railiance01 inside the catalog-bound Glas metered owner", + "catalog_target": { + "kind": "kv", + "org": "coulomb", + "repo": "activity-core", + "mount": "platform", + "path": "workloads/activity-core/ops-run-workers/rein-aharness-metered-railiance01", + "fields": [ + "token" + ], + "mount_management": "existing", + "consumers": [ + { + "name": "rein-aharness-metered-railiance01", + "auth": "approle", + "claim": "catalog:activity-core-metered-worker-token", + "purpose": "Claim the admitted hfact-metered ops_run as rein-aharness-metered@railiance01 inside the catalog-bound Glas metered owner" + } + ], + "delivery_modes": [ + "exec-env" + ], + "delivery_auth": { + "method": "approle", + "management": "engine", + "policy_name": "se-prod-activity-core-metered-worker-token", + "role_name": "se-prod-activity-core-metered-worker-token", + "metadata_read": false, + "token_ttl": "5m", + "token_max_ttl": "15m", + "secret_id_ttl": "5m", + "secret_id_num_uses": 1, + "token_num_uses": 8 + }, + "delivery_config": { + "companion_of": [ + "glas-claude-agent-dev-anthropic" + ] + }, + "auth_capability": {}, + "workload_delivery": [] + } + } + } + } + ], + "native_evaluator_digests": "not obtained; local hashes are not evaluator digests", + "approval_ids": [], + "host_pins_verified": false +} diff --git a/docs/evidence/2026-09-27-metered-host-preflight.json b/docs/evidence/2026-09-27-metered-host-preflight.json new file mode 100644 index 0000000..8bffeef --- /dev/null +++ b/docs/evidence/2026-09-27-metered-host-preflight.json @@ -0,0 +1,23 @@ +{ + "scope": "read-only host metadata; no credential or queue access", + "host": "railiance01", + "files": { + "owner.json": { + "sha256": "0e263f8299a42f63caf3595ff3eb7adc10354673f7b5125e0811def3bea7c274", + "mode": "0o600" + }, + "spend-policy.json": { + "sha256": "f31c585916de1ea8bd8e48c72421803dfde1015e6201704b9320f77d2c545d9c", + "mode": "0o600" + } + }, + "ledger_rows": { + "reservations": 0, + "request_routes": 0, + "request_reservations": 0 + }, + "secrets_engine_revision": "f7c12bedc8149bbf484065fb58411a9fac706798", + "target_head": "679d23e0517707ba63e25399b5262f0d2f37315d", + "target_clean": true, + "activity_core_forward": "active" +} diff --git a/docs/evidence/2026-09-27-sonnet5-runtime-proof.json b/docs/evidence/2026-09-27-sonnet5-runtime-proof.json new file mode 100644 index 0000000..d03f76f --- /dev/null +++ b/docs/evidence/2026-09-27-sonnet5-runtime-proof.json @@ -0,0 +1,242 @@ +{ + "scope": "standalone installed packages and real CLI/bwrap; synthetic key/provider/empty queue", + "ok": true, + "runtime_sha256": "b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969", + "proof_script_sha256": "a3cde37c6b1075caaa092248569d9b7a3e37bde467648686d70ac77ef86c89d8", + "claude_sha256": "19842705e989393fce936804df6d2ab034860e24b8f8880357981d87ffd83fac", + "imports": { + "rein_aharness": "lib/python3.12/site-packages/rein_aharness/__init__.py", + "llm_connect": "lib/python3.12/site-packages/llm_connect/__init__.py", + "glas_harness": "lib/python3.12/site-packages/glas_harness/__init__.py", + "sandboxer": "lib/python3.12/site-packages/sandboxer/__init__.py" + }, + "packaged_definitions": true, + "positive": { + "native_threshold_usd": 1.0, + "probe": { + "runtime_readonly": true, + "python_prefix": "/opt/sandboxer/runtime", + "cli_version": "2.1.266 (Claude Code)", + "private_absent": true, + "source_absent": true, + "proxy_absent": true, + "interfaces": [ + "lo" + ] + }, + "provider_requests": 1, + "cli_exit_code": 0, + "cli_is_error": false, + "cli_estimated_usd": 0.00030000000000000003, + "request_reservations": [ + { + "receipt": "5519ba2f-fe1f-4928-8afb-fb765acc1bdc", + "run_id": "fixture-run", + "policy_sha256": "c72c3a7c69e5224c63beb60f699bbc0c28d93d4b300604cae4208e761a1b4825", + "lease_id": "d40ebad662643b376f406fb07bf37671cb99497cc23755e3e8f97cda53b37a29", + "liability_microusd": 4640000, + "state": "charged", + "observed_microusd": 500, + "created_at": "2026-09-27T14:55:51.865416+00:00" + } + ], + "route_revoked": true, + "workspace_removed": true, + "bootstrap": { + "ok": true, + "claimed": false, + "empty": true, + "run_id": "", + "ops_state": null + }, + "profile_ref": "harness.agent-dev-local@1.1.1", + "model": "claude-sonnet-5", + "request_shapes": [ + { + "model": "claude-sonnet-5", + "max_tokens": 64000, + "thinking_type": "adaptive", + "manual_thinking_budget": false, + "sampling_parameters": {}, + "betas": [ + "claude-code-20250219", + "interleaved-thinking-2025-05-14", + "thinking-token-count-2026-05-13", + "context-management-2025-06-27", + "prompt-caching-scope-2026-01-05", + "mid-conversation-system-2026-04-07", + "effort-2025-11-24" + ] + } + ], + "policy_context_tokens": 1000000, + "admission_requests": [ + { + "model": "claude-sonnet-5", + "max_tokens": 64000, + "thinking_type": "disabled", + "top_level_fields": [ + "max_tokens", + "messages", + "metadata", + "model", + "output_config", + "stream", + "system", + "thinking", + "tools" + ], + "betas": [ + "claude-code-20250219", + "interleaved-thinking-2025-05-14", + "thinking-token-count-2026-05-13", + "context-management-2025-06-27", + "prompt-caching-scope-2026-01-05", + "mid-conversation-system-2026-04-07", + "effort-2025-11-24", + "structured-outputs-2025-12-15" + ], + "message_roles": [ + "user" + ], + "output_config_keys": [ + "effort", + "format" + ], + "output_effort": "high", + "refusal": "beta feature not admitted" + }, + { + "model": "claude-sonnet-5", + "max_tokens": 64000, + "thinking_type": "adaptive", + "top_level_fields": [ + "context_management", + "max_tokens", + "messages", + "metadata", + "model", + "output_config", + "stream", + "system", + "thinking", + "tools" + ], + "betas": [ + "claude-code-20250219", + "interleaved-thinking-2025-05-14", + "thinking-token-count-2026-05-13", + "context-management-2025-06-27", + "prompt-caching-scope-2026-01-05", + "mid-conversation-system-2026-04-07", + "effort-2025-11-24" + ], + "message_roles": [ + "user" + ], + "output_config_keys": [ + "effort" + ], + "output_effort": "high" + } + ] + }, + "refused": { + "native_threshold_usd": 0.01, + "probe": { + "runtime_readonly": true, + "python_prefix": "/opt/sandboxer/runtime", + "cli_version": "2.1.266 (Claude Code)", + "private_absent": true, + "source_absent": true, + "proxy_absent": true, + "interfaces": [ + "lo" + ] + }, + "provider_requests": 0, + "cli_exit_code": 1, + "cli_is_error": true, + "cli_estimated_usd": 0, + "request_reservations": [], + "route_revoked": true, + "workspace_removed": true, + "bootstrap": null, + "profile_ref": "harness.agent-dev-local@1.1.1", + "model": "claude-sonnet-5", + "request_shapes": [], + "policy_context_tokens": 1000000, + "admission_requests": [ + { + "model": "claude-sonnet-5", + "max_tokens": 64000, + "thinking_type": "disabled", + "top_level_fields": [ + "max_tokens", + "messages", + "metadata", + "model", + "output_config", + "stream", + "system", + "thinking", + "tools" + ], + "betas": [ + "claude-code-20250219", + "interleaved-thinking-2025-05-14", + "thinking-token-count-2026-05-13", + "context-management-2025-06-27", + "prompt-caching-scope-2026-01-05", + "mid-conversation-system-2026-04-07", + "effort-2025-11-24", + "structured-outputs-2025-12-15" + ], + "message_roles": [ + "user" + ], + "output_config_keys": [ + "effort", + "format" + ], + "output_effort": "high", + "refusal": "beta feature not admitted" + }, + { + "model": "claude-sonnet-5", + "max_tokens": 64000, + "thinking_type": "adaptive", + "top_level_fields": [ + "context_management", + "max_tokens", + "messages", + "metadata", + "model", + "output_config", + "stream", + "system", + "thinking", + "tools" + ], + "betas": [ + "claude-code-20250219", + "interleaved-thinking-2025-05-14", + "thinking-token-count-2026-05-13", + "context-management-2025-06-27", + "prompt-caching-scope-2026-01-05", + "mid-conversation-system-2026-04-07", + "effort-2025-11-24" + ], + "message_roles": [ + "user" + ], + "output_config_keys": [ + "effort" + ], + "output_effort": "high" + } + ] + }, + "artifact_unchanged": true, + "live_factory_attempts": 0 +} diff --git a/docs/glas-claude-delivery.md b/docs/glas-claude-delivery.md index 30275f2..e28c131 100644 --- a/docs/glas-claude-delivery.md +++ b/docs/glas-claude-delivery.md @@ -62,3 +62,13 @@ not runtime authorization. No production activation was performed in this review Rotation: store replacement with CAS, stop old runs, verify replacement, revoke predecessor at Anthropic and prove denial. Bao session expiration does not revoke the provider key. Compromise disables the provider key and affected runs first. + + +## Current configuration correction — 2026-09-27 + +The earlier pin-only check is superseded for activation by the +[exact-model review packet](proposals/glas-metered-20260927/README.md). The source +catalog pins its corrected 1M-context/64k-output owner and observed beta list; +the host's older file will fail that pin. Exact Sonnet 5 synthetic target proof +passes, but host source/config installation, budget/FX acceptance and native +action approvals remain open. No new approval may name the old owner digest. diff --git a/docs/proposals/glas-metered-20260927/README.md b/docs/proposals/glas-metered-20260927/README.md new file mode 100644 index 0000000..ae1b2a6 --- /dev/null +++ b/docs/proposals/glas-metered-20260927/README.md @@ -0,0 +1,106 @@ +# Metered owner activation candidate — 2026-09-27 + +This is a reviewed source candidate, not native authorization. No provider key +was read, no approval created or consumed, and no paid/production queue attempt +was made. The live owner still has the old file; the source catalog now pins the +corrected file and will refuse that old file. + +## Concrete correction + +`owner.json` retains the installed runtime, spend-policy digest and authority +reference. Only its Messages policy changes: + +- input liability context: 200000 → 1000000 (Sonnet 5 has no 200k variant); +- output ceiling: 32000 → 64000 (observed from the pinned actual CLI); +- allow `mid-conversation-system-2026-04-07`, observed on the primary request; +- tariff reference: dated 2026-09-27 review of first-party global standard pricing. + +The optional CLI title-generation request remains refused: its structured-output +beta/format are not admitted. The primary request succeeds in the synthetic +proof. Mid-conversation system-role messages remain refused by the narrow +transport; this first-response proof does not establish a complete tool loop. +Do not broaden that transport as an implied consequence of allowing the header. + +The input upper rate remains 4 micro-USD/token (covers one-hour cache creation), +output 10. Maximum full-output request hold: USD 4.64. At the configured 0.87 +EUR/USD treatment that is EUR 4.0368. The existing USD 5.74 run reservation can +fit **one** such request; even a second minimum-output request cannot fit after +it. Actual low usage does not refund the full conservative charge. +Two full-output requests would require USD 9.28 (EUR 8.0736 at that configured +conversion); increasing the existing EUR 5 cap requires a new spend-policy/grant, +ledger and recipient pins. It is not part of this correction. The preserved +EUR 20 daily/EUR 500 total and validity through January 2027 are installed proof +policy values, not acceptance of the old factory proposal or a guaranteed future +FX/tariff ceiling. Recheck FX, tariff validity and the execution window before use. + +Sources: [model limits](https://platform.claude.com/docs/en/models/sonnet-5/whats-new-sonnet-5) +and [pricing](https://platform.claude.com/docs/en/about-claude/pricing), inspected +2026-09-27. `provider-facts.json` records these inputs and the observed CLI shape. + +Validation: the complete Secrets Engine suite passed **498 tests**. The exact +final proof script hash is recorded in the target receipt. + +## Evidence and exact requests + +- `activation-review.json`: six unapproved exact CheckRequests, apply/verify/exec + for each of the provider and worker-token lanes, plus their two native plans. +- `../../evidence/2026-09-27-installed-policy-refusal.json`: the installed policy + fails context, output and beta compatibility checks. +- `../../evidence/2026-09-27-sonnet5-runtime-proof.json`: actual Railiance CLI + 2.1.266/profile 1.1.1/model Sonnet 5, positive fake stream, zero-forward capacity + refusal, private-state exclusion, read-only unchanged artifact and teardown. +- `../../evidence/2026-09-27-metered-host-preflight.json`: old mode-0600 owner pin, + unchanged spend pin, all three ledger tables empty, clean target and active + Activity Core forward. These are observations, not permanent preconditions. + +The six requests are built by the same `_expected_request` code as the PEP. +They have no issued approval IDs or evaluator-origin digests. Do not use a local +hash as a native evaluator digest. The provider lane requires human control; +the companion retains its separately accepted ordinary lane approval. + +Reproduce the offline review from the secrets-engine root: + +```sh +uv run python tools/prepare_metered_activation.py \ + --catalog-dir catalog \ + --owner-snapshot docs/proposals/glas-metered-20260927/owner.json \ + --spend-snapshot docs/proposals/glas-metered-20260927/spend-policy.snapshot.json \ + --provider-facts docs/proposals/glas-metered-20260927/provider-facts.json \ + --output /tmp/new-metered-activation-review.json +``` + +The output must not already exist. This command performs no network access, +credential retrieval, host mutation, queue claim or approval consumption. + +## Remaining native sequence + +1. Approve the corrected host owner file and exact source release. Replace only + `/home/tegwick/hfact/owner-metered/owner.json`, preserving mode 0600. Require + the old SHA-256 `0e263f8299a42f63caf3595ff3eb7adc10354673f7b5125e0811def3bea7c274`, + unchanged spend-policy pin and empty parent/request/route tables before the + replacement. Refuse drift; do not reset or recreate the existing ledger. +2. Update the host Secrets Engine from `f7c12bed` to the reviewed source including + the companion-only guards and corrected catalog pin. No worker restart or + profile promotion is required for this file-only correction. Validate the + exact recipient path/pins and run its backend-free `metered-once --check`. +3. Choose and accept the next proof's budget and scope. The existing EUR 5 cap + permits one bounded model response but cannot establish a completed native + tool session. Do not queue the one-commit task expecting two calls to fit. +4. Use the current owner routing catalog and scoped attended requester lane + `secrets-engine-requester-login` to obtain evaluator bindings and create fresh + exact approvals. Use the admitted human review surface for the provider + lane. Do not seed human entries, reuse OpenRouter IDs, or reinterpret CCR + custody as action approval. Refresh Railiance Clock admission at execution. +5. Under the scoped approval-client reader and separately attended backend lane, + apply only the two data-read policies/AppRoles (5m TTL, max 15m, single-use + SecretIDs, eight token uses). Verify exact read and unrelated identity, + sibling path, metadata/list/write denials and revocation separately per lane. + Keep existing KV values and the standing claim-loop credential untouched. +6. Invoke only the pinned one-cycle owner after both lanes pass their own + claim/PDP/consume/readiness gates. Record natural claim/heartbeat/close, + request holds, cleanup, actual outcome and revocation. Retain failures. + +Native configuration, requester login, real human entries and paid execution +are not performed by this packet. Required attended authentication cannot be +substituted by an agent action. The complete factory G0 and useful delivery +remain HFACT T01/T05; this packet is the narrower disposable Glas proof. diff --git a/docs/proposals/glas-metered-20260927/activation-review.json b/docs/proposals/glas-metered-20260927/activation-review.json new file mode 100644 index 0000000..c52193a --- /dev/null +++ b/docs/proposals/glas-metered-20260927/activation-review.json @@ -0,0 +1,690 @@ +{ + "schema": "metered-activation-review/v1", + "status": "review-only", + "dispatch_enabled": false, + "owner_digest": "97ee637a8e022922bbf6264b57a55f68d05d4372dabd20e3cd74bed14db57cd2", + "policy_review": { + "passed": true, + "failures": [], + "maximum_request_microusd": 4640000, + "maximum_request_eur_at_configured_fx": "4.0368", + "full_output_requests_that_fit": 1, + "provider_facts_ref": "https://platform.claude.com/docs/en/models/sonnet-5/whats-new-sonnet-5", + "authority_granted": false, + "remaining_acceptance": [ + "FX upper bound and validity", + "live CLI/beta compatibility", + "native delivery and runtime admission" + ] + }, + "plans": [ + { + "catalog_id": "glas-claude-agent-dev-anthropic", + "stage": "prod", + "decision_id": "", + "policy_name": "se-prod-glas-claude-agent-dev-anthropic", + "role_name": "se-prod-glas-claude-agent-dev-anthropic", + "actions": [ + { + "kind": "kv-mount-check", + "target": "platform", + "detail": { + "type": "kv-v2", + "management": "existing", + "mutation": "none" + } + }, + { + "kind": "policy", + "target": "se-prod-glas-claude-agent-dev-anthropic", + "detail": { + "paths": "platform/data/workloads/glas-harness/claude-agent-dev" + } + }, + { + "kind": "approle", + "target": "se-prod-glas-claude-agent-dev-anthropic", + "detail": { + "token_policies": "se-prod-glas-claude-agent-dev-anthropic", + "auth": "approle", + "token_ttl": "5m", + "token_max_ttl": "15m", + "token_num_uses": 8, + "secret_id_ttl": "5m", + "secret_id_num_uses": 1 + } + } + ], + "policy_hcl": "# Generated by secrets-engine for policy \"se-prod-glas-claude-agent-dev-anthropic\"\n\npath \"platform/data/workloads/glas-harness/claude-agent-dev\" {\n capabilities = [\"read\"]\n}\n" + }, + { + "catalog_id": "activity-core-metered-worker-token", + "stage": "prod", + "decision_id": "", + "policy_name": "se-prod-activity-core-metered-worker-token", + "role_name": "se-prod-activity-core-metered-worker-token", + "actions": [ + { + "kind": "kv-mount-check", + "target": "platform", + "detail": { + "type": "kv-v2", + "management": "existing", + "mutation": "none" + } + }, + { + "kind": "policy", + "target": "se-prod-activity-core-metered-worker-token", + "detail": { + "paths": "platform/data/workloads/activity-core/ops-run-workers/rein-aharness-metered-railiance01" + } + }, + { + "kind": "approle", + "target": "se-prod-activity-core-metered-worker-token", + "detail": { + "token_policies": "se-prod-activity-core-metered-worker-token", + "auth": "approle", + "token_ttl": "5m", + "token_max_ttl": "15m", + "token_num_uses": 8, + "secret_id_ttl": "5m", + "secret_id_num_uses": 1 + } + } + ], + "policy_hcl": "# Generated by secrets-engine for policy \"se-prod-activity-core-metered-worker-token\"\n\npath \"platform/data/workloads/activity-core/ops-run-workers/rein-aharness-metered-railiance01\" {\n capabilities = [\"read\"]\n}\n" + } + ], + "actions": [ + { + "catalog": "glas-claude-agent-dev-anthropic", + "action": "apply", + "request": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service" + }, + "action": "apply", + "resource": { + "id": "catalog:glas-claude-agent-dev-anthropic", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "attributes": { + "stage": "prod", + "fields": [], + "policy_targets": [ + "se-prod-glas-claude-agent-dev-anthropic" + ], + "auth_targets": [ + "se-prod-glas-claude-agent-dev-anthropic" + ] + } + }, + "context": { + "purpose": "Owner-admitted glas-harness agt run through the reviewed local profile; no caller-facing key fetch", + "catalog_target": { + "kind": "kv", + "org": "coulomb", + "repo": "sand-boxer", + "mount": "platform", + "path": "workloads/glas-harness/claude-agent-dev", + "fields": [ + "ANTHROPIC_API_KEY" + ], + "mount_management": "existing", + "consumers": [ + { + "name": "sand-boxer-glas-agent-dev", + "auth": "approle", + "claim": "catalog:glas-claude-agent-dev-anthropic", + "purpose": "Owner-admitted glas-harness agt run through the reviewed local profile; no caller-facing key fetch" + } + ], + "delivery_modes": [ + "exec-env", + "read-check" + ], + "delivery_auth": { + "method": "approle", + "management": "engine", + "policy_name": "se-prod-glas-claude-agent-dev-anthropic", + "role_name": "se-prod-glas-claude-agent-dev-anthropic", + "metadata_read": false, + "token_ttl": "5m", + "token_max_ttl": "15m", + "secret_id_ttl": "5m", + "secret_id_num_uses": 1, + "token_num_uses": 8 + }, + "delivery_config": { + "exec_owner": { + "status": "configured", + "owner": "rein-aharness MessagesOwner (metered-once) with sand-boxer runtime boundary", + "command": [ + "/home/tegwick/.helixforge-factory/runtimes/b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969/bin/python3", + "-I", + "-B", + "-m", + "rein_aharness.cli", + "metered-once", + "--owner-config", + "/home/tegwick/hfact/owner-metered/owner.json" + ], + "cwd": "/home/tegwick/hfact/owner-metered", + "environment": { + "PATH": "/usr/bin:/bin", + "LANG": "C.UTF-8", + "HOME": "/home/tegwick", + "ACTIVITY_CORE_URL": "http://127.0.0.1:8010", + "AGENT_HARNESS_WORKER_ID": "rein-aharness-metered@railiance01", + "AGENT_HARNESS_OPS_LABELS": "hfact-metered", + "AGENT_HARNESS_OPS_LABELS_MODE": "all", + "AGENT_HARNESS_EXECUTION_PROJECT": "prj-helixforge-factory", + "AGENT_HARNESS_REQUIRE_SPEND_ADMISSION": "1", + "AGENT_HARNESS_REQUIRE_REQUEST_ADMISSION": "1", + "AGENT_HARNESS_SPEND_POLICY": "/home/tegwick/hfact/owner-metered/spend-policy.json", + "AGENT_HARNESS_SPEND_LEDGER": "/home/tegwick/hfact/owner-metered/spend.sqlite3", + "AGENT_HARNESS_REPO_MAP": "{\"hfact-glas-proof\":\"/home/tegwick/hfact/targets/hfact-glas-proof\"}" + }, + "files": { + "/home/tegwick/.helixforge-factory/runtimes/b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969/bin/python3": { + "sha256": "e50d468e8b0adfb05733f5b87b3cff34829c4a8c1aea50c865aa8bdfe4bb150f", + "private": false + }, + "/home/tegwick/hfact/owner-metered/owner.json": { + "sha256": "e0d3fb84649fdca302eccd9415f3cc2beaee84bf07bd83205cdffbe93e5573bc", + "private": true + }, + "/home/tegwick/hfact/owner-metered/spend-policy.json": { + "sha256": "f31c585916de1ea8bd8e48c72421803dfde1015e6201704b9320f77d2c545d9c", + "private": true + } + }, + "companions": [ + { + "catalog": "activity-core-metered-worker-token", + "field": "token", + "env": "ACTIVITY_CORE_WORKER_TOKEN" + } + ] + } + }, + "auth_capability": {}, + "workload_delivery": [] + }, + "human_control": true + } + } + }, + { + "catalog": "glas-claude-agent-dev-anthropic", + "action": "verify", + "request": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service" + }, + "action": "verify", + "resource": { + "id": "catalog:glas-claude-agent-dev-anthropic", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "attributes": { + "stage": "prod", + "fields": [ + "ANTHROPIC_API_KEY" + ], + "policy_targets": [ + "se-prod-glas-claude-agent-dev-anthropic" + ], + "auth_targets": [ + "se-prod-glas-claude-agent-dev-anthropic" + ] + } + }, + "context": { + "purpose": "Owner-admitted glas-harness agt run through the reviewed local profile; no caller-facing key fetch", + "catalog_target": { + "kind": "kv", + "org": "coulomb", + "repo": "sand-boxer", + "mount": "platform", + "path": "workloads/glas-harness/claude-agent-dev", + "fields": [ + "ANTHROPIC_API_KEY" + ], + "mount_management": "existing", + "consumers": [ + { + "name": "sand-boxer-glas-agent-dev", + "auth": "approle", + "claim": "catalog:glas-claude-agent-dev-anthropic", + "purpose": "Owner-admitted glas-harness agt run through the reviewed local profile; no caller-facing key fetch" + } + ], + "delivery_modes": [ + "exec-env", + "read-check" + ], + "delivery_auth": { + "method": "approle", + "management": "engine", + "policy_name": "se-prod-glas-claude-agent-dev-anthropic", + "role_name": "se-prod-glas-claude-agent-dev-anthropic", + "metadata_read": false, + "token_ttl": "5m", + "token_max_ttl": "15m", + "secret_id_ttl": "5m", + "secret_id_num_uses": 1, + "token_num_uses": 8 + }, + "delivery_config": { + "exec_owner": { + "status": "configured", + "owner": "rein-aharness MessagesOwner (metered-once) with sand-boxer runtime boundary", + "command": [ + "/home/tegwick/.helixforge-factory/runtimes/b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969/bin/python3", + "-I", + "-B", + "-m", + "rein_aharness.cli", + "metered-once", + "--owner-config", + "/home/tegwick/hfact/owner-metered/owner.json" + ], + "cwd": "/home/tegwick/hfact/owner-metered", + "environment": { + "PATH": "/usr/bin:/bin", + "LANG": "C.UTF-8", + "HOME": "/home/tegwick", + "ACTIVITY_CORE_URL": "http://127.0.0.1:8010", + "AGENT_HARNESS_WORKER_ID": "rein-aharness-metered@railiance01", + "AGENT_HARNESS_OPS_LABELS": "hfact-metered", + "AGENT_HARNESS_OPS_LABELS_MODE": "all", + "AGENT_HARNESS_EXECUTION_PROJECT": "prj-helixforge-factory", + "AGENT_HARNESS_REQUIRE_SPEND_ADMISSION": "1", + "AGENT_HARNESS_REQUIRE_REQUEST_ADMISSION": "1", + "AGENT_HARNESS_SPEND_POLICY": "/home/tegwick/hfact/owner-metered/spend-policy.json", + "AGENT_HARNESS_SPEND_LEDGER": "/home/tegwick/hfact/owner-metered/spend.sqlite3", + "AGENT_HARNESS_REPO_MAP": "{\"hfact-glas-proof\":\"/home/tegwick/hfact/targets/hfact-glas-proof\"}" + }, + "files": { + "/home/tegwick/.helixforge-factory/runtimes/b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969/bin/python3": { + "sha256": "e50d468e8b0adfb05733f5b87b3cff34829c4a8c1aea50c865aa8bdfe4bb150f", + "private": false + }, + "/home/tegwick/hfact/owner-metered/owner.json": { + "sha256": "e0d3fb84649fdca302eccd9415f3cc2beaee84bf07bd83205cdffbe93e5573bc", + "private": true + }, + "/home/tegwick/hfact/owner-metered/spend-policy.json": { + "sha256": "f31c585916de1ea8bd8e48c72421803dfde1015e6201704b9320f77d2c545d9c", + "private": true + } + }, + "companions": [ + { + "catalog": "activity-core-metered-worker-token", + "field": "token", + "env": "ACTIVITY_CORE_WORKER_TOKEN" + } + ] + } + }, + "auth_capability": {}, + "workload_delivery": [] + }, + "human_control": true + } + } + }, + { + "catalog": "glas-claude-agent-dev-anthropic", + "action": "exec", + "request": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service" + }, + "action": "exec", + "resource": { + "id": "catalog:glas-claude-agent-dev-anthropic", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "attributes": { + "stage": "prod", + "fields": [ + "ANTHROPIC_API_KEY" + ], + "policy_targets": [ + "se-prod-glas-claude-agent-dev-anthropic" + ], + "auth_targets": [ + "se-prod-glas-claude-agent-dev-anthropic" + ] + } + }, + "context": { + "purpose": "Owner-admitted glas-harness agt run through the reviewed local profile; no caller-facing key fetch", + "catalog_target": { + "kind": "kv", + "org": "coulomb", + "repo": "sand-boxer", + "mount": "platform", + "path": "workloads/glas-harness/claude-agent-dev", + "fields": [ + "ANTHROPIC_API_KEY" + ], + "mount_management": "existing", + "consumers": [ + { + "name": "sand-boxer-glas-agent-dev", + "auth": "approle", + "claim": "catalog:glas-claude-agent-dev-anthropic", + "purpose": "Owner-admitted glas-harness agt run through the reviewed local profile; no caller-facing key fetch" + } + ], + "delivery_modes": [ + "exec-env", + "read-check" + ], + "delivery_auth": { + "method": "approle", + "management": "engine", + "policy_name": "se-prod-glas-claude-agent-dev-anthropic", + "role_name": "se-prod-glas-claude-agent-dev-anthropic", + "metadata_read": false, + "token_ttl": "5m", + "token_max_ttl": "15m", + "secret_id_ttl": "5m", + "secret_id_num_uses": 1, + "token_num_uses": 8 + }, + "delivery_config": { + "exec_owner": { + "status": "configured", + "owner": "rein-aharness MessagesOwner (metered-once) with sand-boxer runtime boundary", + "command": [ + "/home/tegwick/.helixforge-factory/runtimes/b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969/bin/python3", + "-I", + "-B", + "-m", + "rein_aharness.cli", + "metered-once", + "--owner-config", + "/home/tegwick/hfact/owner-metered/owner.json" + ], + "cwd": "/home/tegwick/hfact/owner-metered", + "environment": { + "PATH": "/usr/bin:/bin", + "LANG": "C.UTF-8", + "HOME": "/home/tegwick", + "ACTIVITY_CORE_URL": "http://127.0.0.1:8010", + "AGENT_HARNESS_WORKER_ID": "rein-aharness-metered@railiance01", + "AGENT_HARNESS_OPS_LABELS": "hfact-metered", + "AGENT_HARNESS_OPS_LABELS_MODE": "all", + "AGENT_HARNESS_EXECUTION_PROJECT": "prj-helixforge-factory", + "AGENT_HARNESS_REQUIRE_SPEND_ADMISSION": "1", + "AGENT_HARNESS_REQUIRE_REQUEST_ADMISSION": "1", + "AGENT_HARNESS_SPEND_POLICY": "/home/tegwick/hfact/owner-metered/spend-policy.json", + "AGENT_HARNESS_SPEND_LEDGER": "/home/tegwick/hfact/owner-metered/spend.sqlite3", + "AGENT_HARNESS_REPO_MAP": "{\"hfact-glas-proof\":\"/home/tegwick/hfact/targets/hfact-glas-proof\"}" + }, + "files": { + "/home/tegwick/.helixforge-factory/runtimes/b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969/bin/python3": { + "sha256": "e50d468e8b0adfb05733f5b87b3cff34829c4a8c1aea50c865aa8bdfe4bb150f", + "private": false + }, + "/home/tegwick/hfact/owner-metered/owner.json": { + "sha256": "e0d3fb84649fdca302eccd9415f3cc2beaee84bf07bd83205cdffbe93e5573bc", + "private": true + }, + "/home/tegwick/hfact/owner-metered/spend-policy.json": { + "sha256": "f31c585916de1ea8bd8e48c72421803dfde1015e6201704b9320f77d2c545d9c", + "private": true + } + }, + "companions": [ + { + "catalog": "activity-core-metered-worker-token", + "field": "token", + "env": "ACTIVITY_CORE_WORKER_TOKEN" + } + ] + } + }, + "auth_capability": {}, + "workload_delivery": [] + }, + "human_control": true, + "exec_owner_sha256": "97ee637a8e022922bbf6264b57a55f68d05d4372dabd20e3cd74bed14db57cd2" + } + } + }, + { + "catalog": "activity-core-metered-worker-token", + "action": "apply", + "request": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service" + }, + "action": "apply", + "resource": { + "id": "catalog:activity-core-metered-worker-token", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "attributes": { + "stage": "prod", + "fields": [], + "policy_targets": [ + "se-prod-activity-core-metered-worker-token" + ], + "auth_targets": [ + "se-prod-activity-core-metered-worker-token" + ] + } + }, + "context": { + "purpose": "Claim the admitted hfact-metered ops_run as rein-aharness-metered@railiance01 inside the catalog-bound Glas metered owner", + "catalog_target": { + "kind": "kv", + "org": "coulomb", + "repo": "activity-core", + "mount": "platform", + "path": "workloads/activity-core/ops-run-workers/rein-aharness-metered-railiance01", + "fields": [ + "token" + ], + "mount_management": "existing", + "consumers": [ + { + "name": "rein-aharness-metered-railiance01", + "auth": "approle", + "claim": "catalog:activity-core-metered-worker-token", + "purpose": "Claim the admitted hfact-metered ops_run as rein-aharness-metered@railiance01 inside the catalog-bound Glas metered owner" + } + ], + "delivery_modes": [ + "exec-env" + ], + "delivery_auth": { + "method": "approle", + "management": "engine", + "policy_name": "se-prod-activity-core-metered-worker-token", + "role_name": "se-prod-activity-core-metered-worker-token", + "metadata_read": false, + "token_ttl": "5m", + "token_max_ttl": "15m", + "secret_id_ttl": "5m", + "secret_id_num_uses": 1, + "token_num_uses": 8 + }, + "delivery_config": { + "companion_of": [ + "glas-claude-agent-dev-anthropic" + ] + }, + "auth_capability": {}, + "workload_delivery": [] + } + } + } + }, + { + "catalog": "activity-core-metered-worker-token", + "action": "verify", + "request": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service" + }, + "action": "verify", + "resource": { + "id": "catalog:activity-core-metered-worker-token", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "attributes": { + "stage": "prod", + "fields": [ + "token" + ], + "policy_targets": [ + "se-prod-activity-core-metered-worker-token" + ], + "auth_targets": [ + "se-prod-activity-core-metered-worker-token" + ] + } + }, + "context": { + "purpose": "Claim the admitted hfact-metered ops_run as rein-aharness-metered@railiance01 inside the catalog-bound Glas metered owner", + "catalog_target": { + "kind": "kv", + "org": "coulomb", + "repo": "activity-core", + "mount": "platform", + "path": "workloads/activity-core/ops-run-workers/rein-aharness-metered-railiance01", + "fields": [ + "token" + ], + "mount_management": "existing", + "consumers": [ + { + "name": "rein-aharness-metered-railiance01", + "auth": "approle", + "claim": "catalog:activity-core-metered-worker-token", + "purpose": "Claim the admitted hfact-metered ops_run as rein-aharness-metered@railiance01 inside the catalog-bound Glas metered owner" + } + ], + "delivery_modes": [ + "exec-env" + ], + "delivery_auth": { + "method": "approle", + "management": "engine", + "policy_name": "se-prod-activity-core-metered-worker-token", + "role_name": "se-prod-activity-core-metered-worker-token", + "metadata_read": false, + "token_ttl": "5m", + "token_max_ttl": "15m", + "secret_id_ttl": "5m", + "secret_id_num_uses": 1, + "token_num_uses": 8 + }, + "delivery_config": { + "companion_of": [ + "glas-claude-agent-dev-anthropic" + ] + }, + "auth_capability": {}, + "workload_delivery": [] + } + } + } + }, + { + "catalog": "activity-core-metered-worker-token", + "action": "exec", + "request": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service" + }, + "action": "exec", + "resource": { + "id": "catalog:activity-core-metered-worker-token", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "attributes": { + "stage": "prod", + "fields": [ + "token" + ], + "policy_targets": [ + "se-prod-activity-core-metered-worker-token" + ], + "auth_targets": [ + "se-prod-activity-core-metered-worker-token" + ] + } + }, + "context": { + "purpose": "Claim the admitted hfact-metered ops_run as rein-aharness-metered@railiance01 inside the catalog-bound Glas metered owner", + "catalog_target": { + "kind": "kv", + "org": "coulomb", + "repo": "activity-core", + "mount": "platform", + "path": "workloads/activity-core/ops-run-workers/rein-aharness-metered-railiance01", + "fields": [ + "token" + ], + "mount_management": "existing", + "consumers": [ + { + "name": "rein-aharness-metered-railiance01", + "auth": "approle", + "claim": "catalog:activity-core-metered-worker-token", + "purpose": "Claim the admitted hfact-metered ops_run as rein-aharness-metered@railiance01 inside the catalog-bound Glas metered owner" + } + ], + "delivery_modes": [ + "exec-env" + ], + "delivery_auth": { + "method": "approle", + "management": "engine", + "policy_name": "se-prod-activity-core-metered-worker-token", + "role_name": "se-prod-activity-core-metered-worker-token", + "metadata_read": false, + "token_ttl": "5m", + "token_max_ttl": "15m", + "secret_id_ttl": "5m", + "secret_id_num_uses": 1, + "token_num_uses": 8 + }, + "delivery_config": { + "companion_of": [ + "glas-claude-agent-dev-anthropic" + ] + }, + "auth_capability": {}, + "workload_delivery": [] + } + } + } + } + ], + "native_evaluator_digests": "not obtained; local hashes are not evaluator digests", + "approval_ids": [], + "host_pins_verified": false +} diff --git a/docs/proposals/glas-metered-20260927/owner.json b/docs/proposals/glas-metered-20260927/owner.json new file mode 100644 index 0000000..6ed816f --- /dev/null +++ b/docs/proposals/glas-metered-20260927/owner.json @@ -0,0 +1,28 @@ +{ + "version": "1", + "authority_ref": "approval-engine:ba5ce2d8-8b6d-40be-af89-2e8c147029a3", + "spend_policy_sha256": "a8ab37294edc48ee601a15134c116d30a7935d455c9df655e208ebcf7d0a1132", + "messages_policy": { + "tariff_ref": "anthropic-list-2026-09-27:claude-sonnet-5:global-standard:input-bound-1h-cache-write", + "model": "claude-sonnet-5", + "context_tokens": 1000000, + "max_output_tokens": 64000, + "input_microusd_per_token": 4, + "output_microusd_per_token": 10, + "allowed_betas": [ + "claude-code-20250219", + "interleaved-thinking-2025-05-14", + "thinking-token-count-2026-05-13", + "context-management-2025-06-27", + "prompt-caching-scope-2026-01-05", + "mid-conversation-system-2026-04-07", + "effort-2025-11-24" + ], + "max_body_bytes": 2000000, + "timeout_seconds": 120 + }, + "runtime": { + "path": "/home/tegwick/.helixforge-factory/runtimes/b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969", + "sha256": "b6e4e8a429393d68831c996a65c0489664205df969ac9882e581983ec2da4969" + } +} diff --git a/docs/proposals/glas-metered-20260927/provider-facts.json b/docs/proposals/glas-metered-20260927/provider-facts.json new file mode 100644 index 0000000..8005088 --- /dev/null +++ b/docs/proposals/glas-metered-20260927/provider-facts.json @@ -0,0 +1,22 @@ +{ + "model": "claude-sonnet-5", + "context_tokens": 1000000, + "max_output_tokens": 128000, + "input_microusd_per_token": 4, + "output_microusd_per_token": 10, + "observed_on": "2026-09-27", + "source": "https://platform.claude.com/docs/en/models/sonnet-5/whats-new-sonnet-5", + "pricing_source": "https://platform.claude.com/docs/en/about-claude/pricing", + "scope": "First-party global standard Messages; input bound covers one-hour cache creation. No inference_geo, service_tier, speed, server tools or other extra-fee request fields admitted by transport. Snapshot for review, not authority or a future tariff guarantee.", + "observed_cli_max_tokens": 64000, + "required_request_betas": [ + "claude-code-20250219", + "interleaved-thinking-2025-05-14", + "thinking-token-count-2026-05-13", + "context-management-2025-06-27", + "prompt-caching-scope-2026-01-05", + "mid-conversation-system-2026-04-07", + "effort-2025-11-24" + ], + "cli_shape_evidence": "2026-09-27-sonnet5-runtime-proof.json" +} diff --git a/docs/proposals/glas-metered-20260927/spend-policy.snapshot.json b/docs/proposals/glas-metered-20260927/spend-policy.snapshot.json new file mode 100644 index 0000000..3ad5392 --- /dev/null +++ b/docs/proposals/glas-metered-20260927/spend-policy.snapshot.json @@ -0,0 +1,23 @@ +{ + "version": "1", + "envelope_id": "hfact-glas-anthropic-2026-09", + "authority_ref": "approval-engine:ba5ce2d8-8b6d-40be-af89-2e8c147029a3", + "valid_from": "2026-09-23T16:36:52+00:00", + "expires_at": "2027-01-31T23:59:59+01:00", + "timezone": "Europe/Berlin", + "worker_id": "rein-aharness-metered@railiance01", + "activity_definition_id": "5bae5505-77f1-5ba3-8dfa-2e10ed15531e", + "target_repo": "/home/tegwick/hfact/targets/hfact-glas-proof", + "project": "prj-helixforge-factory", + "profile_ref": "harness.agent-dev-local@1.1.1", + "profile_sha256": "10e423fe256d3a1b3a64b9380ce422dd9e3eba3a432ae35ac28680562683ca65", + "descriptor_sha256": "4f151744d8fcc18a58e4b571a6bff32c8688028841c225da3de41d49b29a3a9b", + "repository_grant_id": "656911f7dff83e871434b415f0cee685", + "max_budget_usd": "5.00", + "max_liability_usd": "5.74", + "max_turns": 30, + "eur_per_usd": "0.87", + "per_run_eur": "5", + "daily_eur": "20", + "total_eur": "500" +} \ No newline at end of file diff --git a/tests/test_metered_activation_review.py b/tests/test_metered_activation_review.py new file mode 100644 index 0000000..c3ed2b5 --- /dev/null +++ b/tests/test_metered_activation_review.py @@ -0,0 +1,94 @@ +import copy +import importlib.util +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[1] +spec = importlib.util.spec_from_file_location('metered_review', ROOT / 'tools/prepare_metered_activation.py') +review = importlib.util.module_from_spec(spec) +spec.loader.exec_module(review) + +PROVIDER = dict(model='claude-sonnet-5', context_tokens=1_000_000, + max_output_tokens=128_000, input_microusd_per_token=4, + output_microusd_per_token=10, source='fixture:provider-facts') +OWNER = {'messages_policy': dict(PROVIDER, max_output_tokens=32_000)} +SPEND = dict(max_liability_usd='5.74', eur_per_usd='0.87', per_run_eur='5') + + +def test_installed_200k_context_is_not_a_hard_input_bound(): + owner = copy.deepcopy(OWNER) + owner['messages_policy']['context_tokens'] = 200_000 + result = review.review_policy(owner, SPEND, PROVIDER) + assert result['maximum_request_microusd'] == 1_120_000 + assert result['failures'] == ['input_reservation_below_provider_context'] + assert not result['passed'] + + +def test_full_context_fits_once_and_does_not_grant_authority(): + result = review.review_policy(OWNER, SPEND, PROVIDER) + assert result['passed'] + assert result['maximum_request_microusd'] == 4_320_000 + assert result['maximum_request_eur_at_configured_fx'] == '3.7584' + assert result['full_output_requests_that_fit'] == 1 + assert result['authority_granted'] is False + + +@pytest.mark.parametrize('key,value,reason', [ + ('model', 'other', 'model_mismatch'), + ('input_microusd_per_token', 2, 'understated_input_microusd_per_token'), + ('output_microusd_per_token', 9, 'understated_output_microusd_per_token'), + ('max_output_tokens', 128001, 'output_exceeds_provider_limit'), +]) +def test_invalid_provider_compatibility_refuses(key, value, reason): + owner = copy.deepcopy(OWNER) + owner['messages_policy'][key] = value + assert reason in review.review_policy(owner, SPEND, PROVIDER)['failures'] + + +def test_no_capacity_and_invalid_fx_refuse(): + assert 'no_full_output_request_fits' in review.review_policy( + OWNER, dict(SPEND, max_liability_usd='1'), PROVIDER)['failures'] + assert 'parent_exceeds_eur_cap' in review.review_policy( + OWNER, dict(SPEND, eur_per_usd='1'), PROVIDER)['failures'] + with pytest.raises(ValueError): + review.review_policy(OWNER, dict(SPEND, eur_per_usd='NaN'), PROVIDER) + + +def test_packet_has_six_distinct_unapproved_actions_and_unchanged_human_controls(): + packet = review.build_packet(ROOT / 'catalog', 'glas-claude-agent-dev-anthropic', + OWNER, SPEND, PROVIDER) + actions = packet['actions'] + assert len(actions) == 6 + assert len({(a['catalog'], a['action']) for a in actions}) == 6 + for row in actions: + request = row['request'] + assert request['resource']['id'] == 'catalog:' + row['catalog'] + assert request['action'] == row['action'] + assert bool(request['context'].get('human_control')) == (row['catalog'] == 'glas-claude-agent-dev-anthropic') + assert 'approval_id' not in request['context'] + assert packet['approval_ids'] == [] + assert not packet['dispatch_enabled'] + assert not packet['host_pins_verified'] + + +def test_snapshot_must_match_the_recipient_pin(tmp_path): + snapshot = tmp_path / 'owner.json' + snapshot.write_text('{}') + with pytest.raises(ValueError, match='snapshot_file_pin_mismatch'): + review.load_pinned_snapshot(snapshot, '0' * 64) + + +def test_actual_cli_output_and_beta_must_fit_policy(): + facts = dict(PROVIDER, observed_cli_max_tokens=64000, + required_request_betas=['mid-conversation-system-2026-04-07']) + result = review.review_policy(OWNER, SPEND, facts) + assert result['failures'] == ['output_limit_below_observed_cli_request', + 'observed_cli_beta_not_admitted'] + owner = copy.deepcopy(OWNER) + owner['messages_policy'].update(max_output_tokens=64000, + allowed_betas=['mid-conversation-system-2026-04-07']) + result = review.review_policy(owner, SPEND, facts) + assert result['passed'] + assert result['maximum_request_microusd'] == 4640000 + assert result['maximum_request_eur_at_configured_fx'] == '4.0368' diff --git a/tools/prepare_metered_activation.py b/tools/prepare_metered_activation.py new file mode 100644 index 0000000..0a6f7d9 --- /dev/null +++ b/tools/prepare_metered_activation.py @@ -0,0 +1,142 @@ +"""Offline review packet for a metered owner; never requests approval or credentials. + +Run with the owning secrets-engine environment. The provider facts input is a +reviewed snapshot, not authority, a live price feed, or a token estimate. +""" +from __future__ import annotations + +import argparse +from dataclasses import asdict +from decimal import Decimal +import hashlib +import json +from pathlib import Path +from types import SimpleNamespace + +from secrets_engine.approval_consume import _expected_request +from secrets_engine.catalog import get_entry +from secrets_engine.exec_owner import owner_binding, owner_digest, resolve_companions +from secrets_engine.plan import build_plan + + +def review_policy(owner: dict, spend: dict, provider: dict) -> dict: + policy = owner['messages_policy'] + failures = [] + for key in ('context_tokens', 'max_output_tokens', 'input_microusd_per_token', + 'output_microusd_per_token'): + if type(policy.get(key)) is not int or policy[key] <= 0: + raise ValueError('invalid_policy_bound') + for key in ('context_tokens', 'max_output_tokens', 'input_microusd_per_token', + 'output_microusd_per_token'): + if type(provider.get(key)) is not int or provider[key] <= 0: + raise ValueError('invalid_provider_bound') + if policy['model'] != provider['model']: + failures.append('model_mismatch') + # No tokenizer or count endpoint is used by MessagesPolicy. A smaller + # configured context does not constrain what the upstream model accepts. + if policy['context_tokens'] < provider['context_tokens']: + failures.append('input_reservation_below_provider_context') + if policy['max_output_tokens'] > provider['max_output_tokens']: + failures.append('output_exceeds_provider_limit') + observed_output = provider.get('observed_cli_max_tokens') + if observed_output is not None: + if type(observed_output) is not int or observed_output <= 0: + raise ValueError('invalid_observed_output_bound') + if policy['max_output_tokens'] < observed_output: + failures.append('output_limit_below_observed_cli_request') + required_betas = provider.get('required_request_betas', []) + if set(required_betas) - set(policy.get('allowed_betas', [])): + failures.append('observed_cli_beta_not_admitted') + for key in ('input_microusd_per_token', 'output_microusd_per_token'): + if policy[key] < provider[key]: + failures.append('understated_' + key) + liability = (policy['context_tokens'] * policy['input_microusd_per_token'] + + policy['max_output_tokens'] * policy['output_microusd_per_token']) + capacity = Decimal(spend['max_liability_usd']) * 1_000_000 + fx = Decimal(spend['eur_per_usd']) + cap = Decimal(spend['per_run_eur']) + if not all(x.is_finite() and x > 0 for x in (capacity, fx, cap)): + raise ValueError('invalid_spend_bound') + if capacity * fx > cap * 1_000_000: + failures.append('parent_exceeds_eur_cap') + if liability > capacity: + failures.append('no_full_output_request_fits') + return { + 'passed': not failures, 'failures': failures, + 'maximum_request_microusd': liability, + 'maximum_request_eur_at_configured_fx': str(Decimal(liability) * fx / 1_000_000), + 'full_output_requests_that_fit': int(capacity // liability), + 'provider_facts_ref': provider['source'], + 'authority_granted': False, + 'remaining_acceptance': ['FX upper bound and validity', 'live CLI/beta compatibility', + 'native delivery and runtime admission'], + } + + +def build_packet(catalog_dir: Path, primary_id: str, owner: dict, spend: dict, + provider: dict) -> dict: + primary = get_entry(catalog_dir, primary_id) + binding = owner_binding(primary) + if binding is None or binding.get('status') != 'configured': + raise ValueError('configured_owner_required') + # This is a source/offline review: host file validation is a separate, + # explicit backend-free step. Do not pretend local paths validate a host. + lanes = [(primary, primary.fields[0])] + lanes.extend((lane, field) for lane, field, _ in resolve_companions( + primary, lambda cid: get_entry(catalog_dir, cid))) + cfg = SimpleNamespace(authorization_subject_id='secrets-engine', + authorization_subject_type='service') + actions = [] + for lane, field in lanes: + for action in ('apply', 'verify', 'exec'): + request = _expected_request( + cfg, lane, action, + fields=() if action == 'apply' else tuple(lane.fields) if action == 'verify' else (field,), + policy_targets=(lane.policy_name,), auth_targets=(lane.role_name,)) + actions.append({'catalog': lane.id, 'action': action, 'request': request}) + return { + 'schema': 'metered-activation-review/v1', 'status': 'review-only', + 'dispatch_enabled': False, 'owner_digest': owner_digest(primary), + 'policy_review': review_policy(owner, spend, provider), + 'plans': [asdict(build_plan(lane, lane.stage)) for lane, _ in lanes], + 'actions': actions, + 'native_evaluator_digests': 'not obtained; local hashes are not evaluator digests', + 'approval_ids': [], 'host_pins_verified': False, + } + + +def load_pinned_snapshot(path: Path, expected: str) -> dict: + raw = path.read_bytes() + if hashlib.sha256(raw).hexdigest() != expected: + raise ValueError('snapshot_file_pin_mismatch') + return json.loads(raw) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--catalog-dir', type=Path, required=True) + parser.add_argument('--primary', default='glas-claude-agent-dev-anthropic') + parser.add_argument('--owner-snapshot', type=Path, required=True) + parser.add_argument('--spend-snapshot', type=Path, required=True) + parser.add_argument('--provider-facts', type=Path, required=True) + parser.add_argument('--output', type=Path, required=True) + args = parser.parse_args() + binding = owner_binding(get_entry(args.catalog_dir, args.primary)) + if binding is None or binding.get('status') != 'configured': + raise ValueError('configured_owner_required') + command = binding['command'] + owner_path = command[command.index('--owner-config') + 1] + spend_path = binding['environment']['AGENT_HARNESS_SPEND_POLICY'] + owner = load_pinned_snapshot(args.owner_snapshot, binding['files'][owner_path]['sha256']) + spend = load_pinned_snapshot(args.spend_snapshot, binding['files'][spend_path]['sha256']) + packet = build_packet(args.catalog_dir, args.primary, owner, spend, + json.loads(args.provider_facts.read_text())) + # Refuse overwrite: each revision remains independently reviewable. + with args.output.open('x') as out: + json.dump(packet, out, indent=2) + out.write('\n') + return 0 if packet['policy_review']['passed'] else 2 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/workplans/SECRETS-WP-0009-glas-claude-native-delivery.md b/workplans/SECRETS-WP-0009-glas-claude-native-delivery.md index 9cc649a..4516522 100644 --- a/workplans/SECRETS-WP-0009-glas-claude-native-delivery.md +++ b/workplans/SECRETS-WP-0009-glas-claude-native-delivery.md @@ -49,7 +49,7 @@ synthetic exec-env transport proof passed; no real secret was read. ```task id: SECRETS-WP-0009-T03 status: wait -blocking_reason: "Configured owner and worker companion passed backend-free recipient/pin checks on 2026-09-27 (SECRETS-WP-0011 complete). Remaining: exact per-action/per-lane approvals, unrelated negative identity, scoped attended apply/verify, bounded real owner delivery and revocation. Recheck pins and spend validity at execution." +blocking_reason: "Exact-profile review found installed context/output/beta mismatches. Corrected owner candidate and source catalog pin prepared with six unapproved action requests; exact Sonnet 5 synthetic target proof passes. Await reviewed host config/source installation, proof budget and FX/tariff acceptance, then attended per-lane approvals/apply/verify/delivery/revocation." priority: high state_hub_task_id: "f8069c8a-ad6b-5d0b-9a36-c2326699437d" ``` @@ -519,3 +519,36 @@ T03 retains all native activation and delivery acceptance. Revalidate the owner and spend envelope in the attended execution window, use approvals bound to the new owner digest and each lane, apply/verify both native lanes, and prove actual bounded owner delivery and cleanup. Configuration does not authorize those actions. + +### 2026-09-27 exact-model admission correction + +The earlier backend-free pin check verified byte identity, not provider liability +or actual CLI compatibility. Cross-owner follow-up found the installed owner +reserved only 200k input tokens although Sonnet 5 accepts 1M, capped output at +32k although the CLI requests 64k, and omitted a primary-request beta. The prior +runtime proof silently selected profile 1.0.0; rein now requires exact profile +and expected model and passed the corrected synthetic proof on Railiance. + +`tools/prepare_metered_activation.py` now builds the six exact per-lane/action +CheckRequests through the PEP's own request builder, renders both plans and +checks the pinned non-secret config snapshots against reviewed provider facts +and observed CLI shape. It makes no network or authorization call. The installed +policy fails; the corrected candidate passes. Native evaluator digests and +approval IDs are deliberately absent. See `docs/proposals/glas-metered-20260927/`. + +The source catalog now pins corrected owner SHA-256 +`e0d3fb84649fdca302eccd9415f3cc2beaee84bf07bd83205cdffbe93e5573bc`. +The host still has `0e263f82…`, so current source refuses it until reviewed +replacement. The unchanged spend-policy pin is `f31c5859…`; read-only host +inspection found zero rows in parent, request and route tables and a clean target. +Host Secrets Engine remains f7c12bed and needs current companion guards before +activation. The provider/worker custody and standing claim loop are untouched. + +The corrected maximum hold is USD 4.64; only one fits the existing USD 5.74 +allowance. Do not promise tool-loop completion under that cap. T03 retains +host installation, scope/budget/FX/tariff acceptance and the existing native +attended approvals, verification, delivery and revocation. No secret read, +approval creation/consume, native apply or paid run occurred. + +Validation for this correction: 498 tests passed in the full Secrets Engine suite; +the finalized exact-profile Railiance proof passed and records its script SHA-256.