feat: prepare data-only Anthropic native delivery lane
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0726e-5232-73f2-aaca-2c05ceb62efb
This commit is contained in:
parent
ebdff586fe
commit
6a0daae437
7 changed files with 202 additions and 4 deletions
60
catalog/glas-claude-agent-dev-anthropic.yaml
Normal file
60
catalog/glas-claude-agent-dev-anthropic.yaml
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
id: glas-claude-agent-dev-anthropic
|
||||||
|
kind: kv
|
||||||
|
org: coulomb
|
||||||
|
repo: sand-boxer
|
||||||
|
stage: prod
|
||||||
|
description: Proposed native exec-env delivery for CCR-2026-0016. KV custody exists;
|
||||||
|
no runtime grant or activation yet.
|
||||||
|
mount: platform
|
||||||
|
path: workloads/glas-harness/claude-agent-dev
|
||||||
|
mount_management: existing
|
||||||
|
fields:
|
||||||
|
- ANTHROPIC_API_KEY
|
||||||
|
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
|
||||||
|
workload_delivery: []
|
||||||
|
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
|
||||||
|
approval:
|
||||||
|
model: ccr
|
||||||
|
decision_ref: CCR-2026-0016
|
||||||
|
notes: Custody only has been completed. Native apply and exec require durable exact-action
|
||||||
|
authorization, engine consume, scoped backend authority and verified delivery
|
||||||
|
state. This entry is not authorization.
|
||||||
|
verification:
|
||||||
|
positive: Exact scoped AppRole reads only ANTHROPIC_API_KEY into the approved child;
|
||||||
|
owner binding and redaction pass.
|
||||||
|
negative: Wrong owner profile/project/actor, direct caller fetch, sibling KV, metadata,
|
||||||
|
listing and writes denied.
|
||||||
|
risk:
|
||||||
|
classification: high
|
||||||
|
notes: API spend; provider expiry 2027-01-31T21:00:00Z is not enforced by Bao token
|
||||||
|
TTL. Workspace scope and budget unverified.
|
||||||
|
rotation:
|
||||||
|
owner: railiance-platform + sand-boxer
|
||||||
|
expectation: Provider replacement, versioned CAS custody, stop old runs, verify
|
||||||
|
replacement then revoke predecessor at Anthropic and prove denial.
|
||||||
|
ttl: provider-defined
|
||||||
|
deactivation:
|
||||||
|
owner: railiance-platform + sand-boxer
|
||||||
|
expectation: Disable lane, stop affected runs, revoke Bao sessions and provider
|
||||||
|
key. Preserve custody history.
|
||||||
|
audit:
|
||||||
|
evidence: CCR id, actor, exact path, field name, provider key identifier if non-secret,
|
||||||
|
timestamps, and pass/fail only
|
||||||
|
|
@ -120,3 +120,10 @@ Before accepting an existing production lane:
|
||||||
that approval cannot be resolved.
|
that approval cannot be resolved.
|
||||||
8. Preserve the interim route until native positive and negative verification
|
8. Preserve the interim route until native positive and negative verification
|
||||||
passes without exposing a value.
|
passes without exposing a value.
|
||||||
|
|
||||||
|
### Optional data-only KV read policy
|
||||||
|
|
||||||
|
`delivery_auth.metadata_read` is a boolean, defaulting to `true` for existing
|
||||||
|
lanes. Set `false` when the consumer needs only the KV data GET; generated
|
||||||
|
policy omits the metadata endpoint. Owner metadata verification then needs
|
||||||
|
separate operator authority, not broader consumer access.
|
||||||
|
|
|
||||||
53
docs/glas-claude-delivery.md
Normal file
53
docs/glas-claude-delivery.md
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Glas Claude exec delivery
|
||||||
|
|
||||||
|
Proposed native lane `glas-claude-agent-dev-anthropic`, provenance
|
||||||
|
railiance-platform CCR-2026-0016; implementation/activation record SECRETS-WP-0009.
|
||||||
|
KV custody is already confirmed at version 2. Do not provision or rotate it as
|
||||||
|
part of native read-lane adoption.
|
||||||
|
|
||||||
|
The generated plan checks existing mount `platform`, creates policy and AppRole
|
||||||
|
`se-prod-glas-claude-agent-dev-anthropic`, and grants read only on
|
||||||
|
`platform/data/workloads/glas-harness/claude-agent-dev`. Field ANTHROPIC_API_KEY
|
||||||
|
is selected by the exec adapter; KV policies scope entries, not fields.
|
||||||
|
`delivery_auth.metadata_read: false` excludes the metadata endpoint; existing
|
||||||
|
lanes retain their previous metadata access by default. Token TTL 5m, maximum
|
||||||
|
15m, SecretID TTL 5m and single use, token use budget 8. No wildcard, listing,
|
||||||
|
workload writes, mount mutation, provider creation or default-policy change is
|
||||||
|
included in this plan. Verify effective token identity policies at activation.
|
||||||
|
|
||||||
|
Sand-boxer's owner-configured credential route binds profile, project, actor and
|
||||||
|
nonempty run id before invoking secrets-engine's exec-env interface. The
|
||||||
|
provider injects the key into a private host helper that directly forwards it to
|
||||||
|
the namespace broker. The broker injects only ANTHROPIC_API_KEY into the command
|
||||||
|
and redacts exact values before truncating output. No OpenBao token crosses
|
||||||
|
into the sandbox; no key is returned through Glas's API. Values are available
|
||||||
|
to the trusted workload and descendants; encoding/exfiltration by hostile
|
||||||
|
workload code is not prevented by an output redactor. Existing sandbox, egress,
|
||||||
|
artifact verification and profile admission boundaries remain required.
|
||||||
|
|
||||||
|
A synthetic provider proves the transport only. It does not stand in for native
|
||||||
|
approval, OpenBao access, provider authentication or production readiness.
|
||||||
|
|
||||||
|
## Activation requirements
|
||||||
|
|
||||||
|
The current engine's production stance refuses before opening the backend:
|
||||||
|
`production action 'exec' requires a durable access-engine decision record;
|
||||||
|
live production remains disabled`. This refusal was exercised with the proposed
|
||||||
|
catalog and service-jwt selection. No real value was requested.
|
||||||
|
|
||||||
|
Activation depends on SECRETS-WP-0007-T04 (exact production actions) and
|
||||||
|
SECRETS-WP-0008-T02/T06 (decision consumption and service authority). Require
|
||||||
|
canonical ActionAuthorization for each protected action, successful consume,
|
||||||
|
and exact scoped backend authority. This draft cannot authorize itself; an
|
||||||
|
operator browser token or unsafe-demo flag is not a runtime substitute.
|
||||||
|
|
||||||
|
Once those services exist: obtain the reviewed apply authorization, apply this
|
||||||
|
exact policy/AppRole with scoped authority, verify positive read and denied
|
||||||
|
metadata/sibling/write access without exposing values, and record delivery-ready
|
||||||
|
state. Bind approved exec authorization and named engine service authentication
|
||||||
|
to the sand-boxer owner route. Prove actual provider authentication and a bounded
|
||||||
|
Glas task, then activate routing and only the validated profile.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
@ -284,6 +284,8 @@ def validate_entry(data: dict[str, Any], *, source: str = "<memory>") -> Catalog
|
||||||
delivery_auth = data["delivery_auth"]
|
delivery_auth = data["delivery_auth"]
|
||||||
if not isinstance(delivery_auth, dict):
|
if not isinstance(delivery_auth, dict):
|
||||||
raise CatalogError(f"{source}: delivery_auth must be a mapping")
|
raise CatalogError(f"{source}: delivery_auth must be a mapping")
|
||||||
|
if "metadata_read" in delivery_auth and not isinstance(delivery_auth["metadata_read"], bool):
|
||||||
|
raise CatalogError(f"{source}: delivery_auth.metadata_read must be boolean")
|
||||||
auth_method = delivery_auth.get("method", "approle")
|
auth_method = delivery_auth.get("method", "approle")
|
||||||
auth_management = delivery_auth.get("management", "engine")
|
auth_management = delivery_auth.get("management", "engine")
|
||||||
if auth_method not in VALID_DELIVERY_AUTH_METHODS:
|
if auth_method not in VALID_DELIVERY_AUTH_METHODS:
|
||||||
|
|
|
||||||
|
|
@ -175,10 +175,10 @@ def lane_policy_paths(entry: CatalogEntry) -> dict[str, list[str]]:
|
||||||
"""The minimal KV v2 paths + capabilities a consumer policy needs for a lane."""
|
"""The minimal KV v2 paths + capabilities a consumer policy needs for a lane."""
|
||||||
data_path = f"{entry.mount}/data/{entry.path}"
|
data_path = f"{entry.mount}/data/{entry.path}"
|
||||||
meta_path = f"{entry.mount}/metadata/{entry.path}"
|
meta_path = f"{entry.mount}/metadata/{entry.path}"
|
||||||
return {
|
paths = {data_path: ["read"]}
|
||||||
data_path: ["read"],
|
if entry.delivery_auth.get("metadata_read", True):
|
||||||
meta_path: ["read"],
|
paths[meta_path] = ["read"]
|
||||||
}
|
return paths
|
||||||
|
|
||||||
|
|
||||||
def render_policy_hcl(policy_name: str, paths: dict[str, list[str]]) -> str:
|
def render_policy_hcl(policy_name: str, paths: dict[str, list[str]]) -> str:
|
||||||
|
|
|
||||||
|
|
@ -114,3 +114,20 @@ def test_every_admitted_lane_renders_existing_mount_check_and_exact_policy():
|
||||||
assert f'path "{entry.kv_data_path}"' in plan.policy_hcl
|
assert f'path "{entry.kv_data_path}"' in plan.policy_hcl
|
||||||
assert "*" not in entry.kv_data_path
|
assert "*" not in entry.kv_data_path
|
||||||
assert plan.role_name.startswith("se-prod-")
|
assert plan.role_name.startswith("se-prod-")
|
||||||
|
|
||||||
|
|
||||||
|
def test_metadata_read_can_be_excluded_without_changing_default():
|
||||||
|
from secrets_engine.roles import lane_policy_paths
|
||||||
|
default = _entry(stage="test", path="test/team/thing")
|
||||||
|
assert "secret/metadata/test/team/thing" in lane_policy_paths(default)
|
||||||
|
narrow = _entry(stage="test", path="test/team/thing", delivery_auth={
|
||||||
|
"method":"approle", "management":"engine", "metadata_read":False})
|
||||||
|
assert lane_policy_paths(narrow) == {"secret/data/test/team/thing":["read"]}
|
||||||
|
assert "metadata/" not in build_plan(narrow, "test").policy_hcl
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("invalid", ["false", None, 0, [], {}])
|
||||||
|
def test_metadata_read_setting_requires_boolean(invalid):
|
||||||
|
from secrets_engine.errors import CatalogError
|
||||||
|
with pytest.raises(CatalogError, match="metadata_read must be boolean"):
|
||||||
|
_entry(delivery_auth={"metadata_read":invalid})
|
||||||
|
|
|
||||||
59
workplans/SECRETS-WP-0009-glas-claude-native-delivery.md
Normal file
59
workplans/SECRETS-WP-0009-glas-claude-native-delivery.md
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
---
|
||||||
|
id: SECRETS-WP-0009
|
||||||
|
type: workplan
|
||||||
|
title: "Activate native Claude credential delivery for Glas"
|
||||||
|
domain: infotech
|
||||||
|
repo: secrets-engine
|
||||||
|
status: blocked
|
||||||
|
owner: codex
|
||||||
|
created: "2026-09-05"
|
||||||
|
updated: "2026-09-05"
|
||||||
|
---
|
||||||
|
|
||||||
|
Demand: GLAS-WP-0012-T02 / SAND-WP-0015-T04, custody CCR-2026-0016.
|
||||||
|
The user authorized continuing credential delivery after storing the key in Bao.
|
||||||
|
This record tracks native read-lane adoption, not a new provider key or rotation.
|
||||||
|
|
||||||
|
## Define the exact native read lane
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: SECRETS-WP-0009-T01
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
Added catalog glas-claude-agent-dev-anthropic; plan checks existing platform
|
||||||
|
mount and proposes one data-only read policy/AppRole. Token TTL5m/max15m,
|
||||||
|
single-use SecretID5m, token use budget8. See docs/glas-claude-delivery.md.
|
||||||
|
No metadata, sibling, listing or write capability is proposed.
|
||||||
|
|
||||||
|
## Support data-only delivery policies
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: SECRETS-WP-0009-T02
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
Added boolean delivery_auth.metadata_read with compatible default true and
|
||||||
|
explicit false for this lane. Validation refuses non-booleans; generated plan
|
||||||
|
omits metadata permissions when disabled. Full owner suite passes. Sand-boxer
|
||||||
|
synthetic exec-env transport proof passed; no real secret was read.
|
||||||
|
|
||||||
|
## Activate the approved native lane and verify real owner delivery
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: SECRETS-WP-0009-T03
|
||||||
|
status: wait
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
Depends on SECRETS-WP-0007-T04 and SECRETS-WP-0008-T02/T06: canonical production
|
||||||
|
authorization, successful consume and scoped service authority must exist.
|
||||||
|
Current production exec refuses before OpenBao because durable access-engine
|
||||||
|
decision records are not served. Do not bypass this with unsafe-demo or a human
|
||||||
|
operator runtime token. Then apply the exact read policy/AppRole, prove positive
|
||||||
|
and negative access, register delivery-ready evidence, bind owner exec with
|
||||||
|
service authentication, and return verified pins/evidence to SAND-WP-0015 and
|
||||||
|
GLAS-WP-0012. Provider scope/budget and expiry remain explicit acceptance inputs.
|
||||||
|
Keep the catalog route inactive until real verification passes.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue