feat(AUDIT-WP-0008): add T02 synthetic load driver
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02991-be07-7bb3-8b6d-e9701b5621de
This commit is contained in:
parent
d2bba6646d
commit
8c8bcf49ae
3 changed files with 420 additions and 0 deletions
39
docs/recovery-synthetic-load-driver.md
Normal file
39
docs/recovery-synthetic-load-driver.md
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# T02 synthetic-load driver
|
||||||
|
|
||||||
|
`scripts/t02_synthetic_load_driver.py` implements the hash-bound
|
||||||
|
`railiance.synthetic-load-driver` v1 contract for
|
||||||
|
`RAILIANCE-WP-0024-T02-SYNTHETIC-01`. It is a candidate execution component,
|
||||||
|
not authorization to revoke a database lease or create a temporary identity.
|
||||||
|
|
||||||
|
The attended operator supplies these non-secret settings to the driver process:
|
||||||
|
|
||||||
|
- `AUDIT_T02_BASE_URL`: the approved direct audit-core URL;
|
||||||
|
- `AUDIT_T02_TENANT`, `AUDIT_T02_SOURCE`, and `AUDIT_T02_FIXTURE_ID`: the exact
|
||||||
|
approved synthetic identity scope and immutable fixture;
|
||||||
|
- `AUDIT_T02_OCCURRED_AT`: the fixed RFC3339 event time used by every phase;
|
||||||
|
- optionally `AUDIT_T02_MAX_ATTEMPTS` (1–30, default 6) and
|
||||||
|
`AUDIT_T02_RETRY_INTERVAL_SECONDS` (0–10, default 1).
|
||||||
|
|
||||||
|
`AUDIT_T02_TOKEN_FILE` is different: it names the approved private mounted
|
||||||
|
bearer file. The driver requires a regular file with no group/world permission,
|
||||||
|
reads one bounded line inside its own process, and never returns the bearer,
|
||||||
|
request body, response body, database credential, Secret data, or OpenBao
|
||||||
|
output. Do not put the bearer in an environment variable or parent shell.
|
||||||
|
|
||||||
|
The four invocations are:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/t02_synthetic_load_driver.py baseline --contract-id RAILIANCE-WP-0024-T02-SYNTHETIC-01
|
||||||
|
scripts/t02_synthetic_load_driver.py expect-unavailable --contract-id RAILIANCE-WP-0024-T02-SYNTHETIC-01
|
||||||
|
scripts/t02_synthetic_load_driver.py expect-recovered --contract-id RAILIANCE-WP-0024-T02-SYNTHETIC-01
|
||||||
|
scripts/t02_synthetic_load_driver.py cleanup --contract-id RAILIANCE-WP-0024-T02-SYNTHETIC-01
|
||||||
|
```
|
||||||
|
|
||||||
|
`baseline` commits or reconciles one immutable synthetic event.
|
||||||
|
`expect-unavailable` accepts only HTTP 503 as the required retryable evidence
|
||||||
|
and never retries HTTP 400, 401, 403, or 409. `expect-recovered` retries bounded
|
||||||
|
transport/500/503 failures until the same event is accepted or reconciled as a
|
||||||
|
duplicate. `cleanup` owns no server-side deletion: it stops no persistent
|
||||||
|
process and reports its ephemeral driver runtime clean, while the accepted
|
||||||
|
audit fixture remains as evidence. Temporary sender identity cleanup is a
|
||||||
|
separate attended `railiance-platform` custody action.
|
||||||
252
scripts/t02_synthetic_load_driver.py
Executable file
252
scripts/t02_synthetic_load_driver.py
Executable file
|
|
@ -0,0 +1,252 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Value-safe synthetic sender for the attended T02 lease-recovery exercise.
|
||||||
|
|
||||||
|
The driver implements the revision-pinned ``railiance.synthetic-load-driver``
|
||||||
|
interface. It reads a short-lived bearer only from a mounted file, sends one
|
||||||
|
immutable synthetic event, and emits only the exact evidence shape required by
|
||||||
|
the selected phase. It never prints request or response bodies.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import stat
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import urllib.error
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Callable, Mapping
|
||||||
|
|
||||||
|
|
||||||
|
CONTRACT_ID = "RAILIANCE-WP-0024-T02-SYNTHETIC-01"
|
||||||
|
PHASES = ("baseline", "expect-unavailable", "expect-recovered", "cleanup")
|
||||||
|
TERMINAL_HTTP = {400, 401, 403, 409}
|
||||||
|
RETRYABLE_HTTP = {0, 500, 503}
|
||||||
|
MAX_RESPONSE_BYTES = 64 * 1024
|
||||||
|
|
||||||
|
|
||||||
|
class DriverError(RuntimeError):
|
||||||
|
"""A value-safe, operator-facing driver failure."""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Config:
|
||||||
|
base_url: str
|
||||||
|
token_file: Path
|
||||||
|
tenant: str
|
||||||
|
source: str
|
||||||
|
fixture_id: str
|
||||||
|
occurred_at: str
|
||||||
|
max_attempts: int
|
||||||
|
retry_interval: float
|
||||||
|
|
||||||
|
|
||||||
|
def _required(env: Mapping[str, str], name: str) -> str:
|
||||||
|
value = env.get(name, "").strip()
|
||||||
|
if not value:
|
||||||
|
raise DriverError(f"required configuration is missing: {name}")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _bounded_int(env: Mapping[str, str], name: str, default: int) -> int:
|
||||||
|
try:
|
||||||
|
value = int(env.get(name, str(default)))
|
||||||
|
except ValueError as exc:
|
||||||
|
raise DriverError(f"invalid numeric configuration: {name}") from exc
|
||||||
|
if not 1 <= value <= 30:
|
||||||
|
raise DriverError(f"configuration is outside its safe bound: {name}")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _bounded_float(env: Mapping[str, str], name: str, default: float) -> float:
|
||||||
|
try:
|
||||||
|
value = float(env.get(name, str(default)))
|
||||||
|
except ValueError as exc:
|
||||||
|
raise DriverError(f"invalid numeric configuration: {name}") from exc
|
||||||
|
if not 0 <= value <= 10:
|
||||||
|
raise DriverError(f"configuration is outside its safe bound: {name}")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(env: Mapping[str, str]) -> Config:
|
||||||
|
base_url = _required(env, "AUDIT_T02_BASE_URL").rstrip("/")
|
||||||
|
parsed = urllib.parse.urlsplit(base_url)
|
||||||
|
if (
|
||||||
|
parsed.scheme not in {"http", "https"}
|
||||||
|
or not parsed.netloc
|
||||||
|
or parsed.username is not None
|
||||||
|
or parsed.password is not None
|
||||||
|
or parsed.query
|
||||||
|
or parsed.fragment
|
||||||
|
):
|
||||||
|
raise DriverError("AUDIT_T02_BASE_URL must be a credential-free HTTP(S) URL")
|
||||||
|
token_file = Path(_required(env, "AUDIT_T02_TOKEN_FILE"))
|
||||||
|
return Config(
|
||||||
|
base_url=base_url,
|
||||||
|
token_file=token_file,
|
||||||
|
tenant=_required(env, "AUDIT_T02_TENANT"),
|
||||||
|
source=_required(env, "AUDIT_T02_SOURCE"),
|
||||||
|
fixture_id=_required(env, "AUDIT_T02_FIXTURE_ID"),
|
||||||
|
occurred_at=_required(env, "AUDIT_T02_OCCURRED_AT"),
|
||||||
|
max_attempts=_bounded_int(env, "AUDIT_T02_MAX_ATTEMPTS", 6),
|
||||||
|
retry_interval=_bounded_float(env, "AUDIT_T02_RETRY_INTERVAL_SECONDS", 1.0),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load_cleanup_fixture(env: Mapping[str, str]) -> str:
|
||||||
|
"""Cleanup owns no credential or server-side object; only the id is needed."""
|
||||||
|
return _required(env, "AUDIT_T02_FIXTURE_ID")
|
||||||
|
|
||||||
|
|
||||||
|
def read_bearer(path: Path) -> str:
|
||||||
|
try:
|
||||||
|
details = path.stat()
|
||||||
|
except OSError as exc:
|
||||||
|
raise DriverError("approved mounted bearer file is unavailable") from exc
|
||||||
|
mode = stat.S_IMODE(details.st_mode)
|
||||||
|
if not stat.S_ISREG(details.st_mode) or mode & 0o077:
|
||||||
|
raise DriverError("approved mounted bearer file must be private and regular")
|
||||||
|
try:
|
||||||
|
raw = path.read_text(encoding="utf-8")
|
||||||
|
except OSError as exc:
|
||||||
|
raise DriverError("approved mounted bearer file cannot be read") from exc
|
||||||
|
lines = raw.splitlines()
|
||||||
|
bearer = lines[0].strip() if len(lines) == 1 else ""
|
||||||
|
if not bearer or len(bearer) > 4096:
|
||||||
|
raise DriverError("approved mounted bearer file has an invalid shape")
|
||||||
|
return bearer
|
||||||
|
|
||||||
|
|
||||||
|
def event_payload(config: Config) -> dict[str, object]:
|
||||||
|
return {
|
||||||
|
"id": config.fixture_id,
|
||||||
|
"type": "audit.recovery.synthetic",
|
||||||
|
"source": config.source,
|
||||||
|
"subject": config.fixture_id,
|
||||||
|
"tenant": config.tenant,
|
||||||
|
"correlation_id": f"{config.fixture_id}:t02",
|
||||||
|
"occurred_at": config.occurred_at,
|
||||||
|
"data": {"exercise": "RAILIANCE-WP-0024-T02", "synthetic": True},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def request_event(config: Config) -> tuple[int, str | None]:
|
||||||
|
bearer = read_bearer(config.token_file)
|
||||||
|
payload = json.dumps(event_payload(config), separators=(",", ":")).encode("utf-8")
|
||||||
|
request = urllib.request.Request(
|
||||||
|
f"{config.base_url}/v1/events", data=payload, method="POST"
|
||||||
|
)
|
||||||
|
request.add_header("Authorization", f"Bearer {bearer}")
|
||||||
|
request.add_header("Content-Type", "application/json")
|
||||||
|
request.add_header("Idempotency-Key", config.fixture_id)
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(request, timeout=10) as response:
|
||||||
|
status = response.status
|
||||||
|
body = response.read(MAX_RESPONSE_BYTES + 1)
|
||||||
|
except urllib.error.HTTPError as exc:
|
||||||
|
exc.close()
|
||||||
|
return exc.code, None
|
||||||
|
except (OSError, TimeoutError, urllib.error.URLError):
|
||||||
|
return 0, None
|
||||||
|
if len(body) > MAX_RESPONSE_BYTES:
|
||||||
|
raise DriverError("target response exceeded the safe size bound")
|
||||||
|
try:
|
||||||
|
value = json.loads(body or b"{}")
|
||||||
|
except json.JSONDecodeError as exc:
|
||||||
|
raise DriverError("target returned invalid JSON") from exc
|
||||||
|
application_status = value.get("status") if isinstance(value, dict) else None
|
||||||
|
return status, application_status if isinstance(application_status, str) else None
|
||||||
|
|
||||||
|
|
||||||
|
def _attempt(
|
||||||
|
config: Config,
|
||||||
|
phase: str,
|
||||||
|
requester: Callable[[Config], tuple[int, str | None]],
|
||||||
|
sleeper: Callable[[float], None],
|
||||||
|
) -> dict[str, object]:
|
||||||
|
last_status = 0
|
||||||
|
for attempt in range(1, config.max_attempts + 1):
|
||||||
|
http_status, application_status = requester(config)
|
||||||
|
last_status = http_status
|
||||||
|
if http_status in TERMINAL_HTTP:
|
||||||
|
raise DriverError(f"{phase} received terminal HTTP status {http_status}")
|
||||||
|
|
||||||
|
if phase == "expect-unavailable" and http_status == 503:
|
||||||
|
return {
|
||||||
|
"contract_id": CONTRACT_ID,
|
||||||
|
"fixture_id": config.fixture_id,
|
||||||
|
"status": "retryable_unavailable",
|
||||||
|
"http_status": 503,
|
||||||
|
"attempts": attempt,
|
||||||
|
"secret_values_observed": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
if http_status in {200, 202}:
|
||||||
|
if application_status not in {"accepted", "duplicate"}:
|
||||||
|
raise DriverError(f"{phase} returned an unexpected application status")
|
||||||
|
if phase == "expect-unavailable":
|
||||||
|
raise DriverError("expect-unavailable observed a successful accept")
|
||||||
|
if phase == "baseline":
|
||||||
|
return {
|
||||||
|
"contract_id": CONTRACT_ID,
|
||||||
|
"fixture_id": config.fixture_id,
|
||||||
|
"status": "ready",
|
||||||
|
"secret_values_observed": False,
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"contract_id": CONTRACT_ID,
|
||||||
|
"fixture_id": config.fixture_id,
|
||||||
|
"status": application_status,
|
||||||
|
"http_status": http_status,
|
||||||
|
"attempts": attempt,
|
||||||
|
"secret_values_observed": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
if http_status not in RETRYABLE_HTTP:
|
||||||
|
raise DriverError(f"{phase} received unexpected HTTP status {http_status}")
|
||||||
|
if attempt < config.max_attempts:
|
||||||
|
sleeper(config.retry_interval)
|
||||||
|
raise DriverError(f"{phase} exhausted its bounded attempts at HTTP status {last_status}")
|
||||||
|
|
||||||
|
|
||||||
|
def run_phase(
|
||||||
|
phase: str,
|
||||||
|
contract_id: str,
|
||||||
|
*,
|
||||||
|
env: Mapping[str, str] = os.environ,
|
||||||
|
requester: Callable[[Config], tuple[int, str | None]] = request_event,
|
||||||
|
sleeper: Callable[[float], None] = time.sleep,
|
||||||
|
) -> dict[str, object]:
|
||||||
|
if contract_id != CONTRACT_ID:
|
||||||
|
raise DriverError("requested contract id is not approved")
|
||||||
|
if phase == "cleanup":
|
||||||
|
return {
|
||||||
|
"contract_id": CONTRACT_ID,
|
||||||
|
"fixture_id": load_cleanup_fixture(env),
|
||||||
|
"status": "clean",
|
||||||
|
"secret_values_observed": False,
|
||||||
|
}
|
||||||
|
return _attempt(load_config(env), phase, requester, sleeper)
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str] | None = None) -> int:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("phase", choices=PHASES)
|
||||||
|
parser.add_argument("--contract-id", required=True)
|
||||||
|
args = parser.parse_args(argv)
|
||||||
|
try:
|
||||||
|
result = run_phase(args.phase, args.contract_id)
|
||||||
|
except DriverError as exc:
|
||||||
|
print(f"synthetic-load driver refused: {exc}", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
print(json.dumps(result, sort_keys=True, separators=(",", ":")))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
129
tests/test_t02_synthetic_load_driver.py
Normal file
129
tests/test_t02_synthetic_load_driver.py
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
import importlib.util
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
SCRIPT = Path(__file__).parents[1] / "scripts" / "t02_synthetic_load_driver.py"
|
||||||
|
SPEC = importlib.util.spec_from_file_location("t02_synthetic_load_driver", SCRIPT)
|
||||||
|
assert SPEC and SPEC.loader
|
||||||
|
DRIVER = importlib.util.module_from_spec(SPEC)
|
||||||
|
sys.modules[SPEC.name] = DRIVER
|
||||||
|
SPEC.loader.exec_module(DRIVER)
|
||||||
|
|
||||||
|
|
||||||
|
def environment(tmp_path):
|
||||||
|
token = tmp_path / "token"
|
||||||
|
token.write_text("opaque-test-value\n", encoding="utf-8")
|
||||||
|
token.chmod(0o600)
|
||||||
|
return {
|
||||||
|
"AUDIT_T02_BASE_URL": "http://audit-core.test:8080",
|
||||||
|
"AUDIT_T02_TOKEN_FILE": str(token),
|
||||||
|
"AUDIT_T02_TENANT": "tenant:trial:recovery-20260822",
|
||||||
|
"AUDIT_T02_SOURCE": "audit-core-recovery",
|
||||||
|
"AUDIT_T02_FIXTURE_ID": "audit-t02-recovery-20260822",
|
||||||
|
"AUDIT_T02_OCCURRED_AT": "2026-08-22T18:30:00Z",
|
||||||
|
"AUDIT_T02_MAX_ATTEMPTS": "4",
|
||||||
|
"AUDIT_T02_RETRY_INTERVAL_SECONDS": "0",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_baseline_returns_exact_value_safe_shape(tmp_path):
|
||||||
|
result = DRIVER.run_phase(
|
||||||
|
"baseline",
|
||||||
|
DRIVER.CONTRACT_ID,
|
||||||
|
env=environment(tmp_path),
|
||||||
|
requester=lambda config: (202, "accepted"),
|
||||||
|
)
|
||||||
|
assert result == {
|
||||||
|
"contract_id": DRIVER.CONTRACT_ID,
|
||||||
|
"fixture_id": "audit-t02-recovery-20260822",
|
||||||
|
"status": "ready",
|
||||||
|
"secret_values_observed": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_unavailable_retries_only_retryable_statuses(tmp_path):
|
||||||
|
replies = iter([(500, None), (503, None)])
|
||||||
|
result = DRIVER.run_phase(
|
||||||
|
"expect-unavailable",
|
||||||
|
DRIVER.CONTRACT_ID,
|
||||||
|
env=environment(tmp_path),
|
||||||
|
requester=lambda config: next(replies),
|
||||||
|
)
|
||||||
|
assert result["status"] == "retryable_unavailable"
|
||||||
|
assert result["http_status"] == 503
|
||||||
|
assert result["attempts"] == 2
|
||||||
|
assert set(result) == {
|
||||||
|
"contract_id", "fixture_id", "status", "http_status", "attempts",
|
||||||
|
"secret_values_observed",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("status", [400, 401, 403, 409])
|
||||||
|
def test_terminal_status_is_never_retried(tmp_path, status):
|
||||||
|
attempts = 0
|
||||||
|
|
||||||
|
def terminal(config):
|
||||||
|
nonlocal attempts
|
||||||
|
attempts += 1
|
||||||
|
return status, None
|
||||||
|
|
||||||
|
with pytest.raises(DRIVER.DriverError, match="terminal HTTP status"):
|
||||||
|
DRIVER.run_phase(
|
||||||
|
"expect-unavailable",
|
||||||
|
DRIVER.CONTRACT_ID,
|
||||||
|
env=environment(tmp_path),
|
||||||
|
requester=terminal,
|
||||||
|
)
|
||||||
|
assert attempts == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_recovered_reports_duplicate_after_retry(tmp_path):
|
||||||
|
replies = iter([(503, None), (200, "duplicate")])
|
||||||
|
result = DRIVER.run_phase(
|
||||||
|
"expect-recovered",
|
||||||
|
DRIVER.CONTRACT_ID,
|
||||||
|
env=environment(tmp_path),
|
||||||
|
requester=lambda config: next(replies),
|
||||||
|
)
|
||||||
|
assert result["status"] == "duplicate"
|
||||||
|
assert result["http_status"] == 200
|
||||||
|
assert result["attempts"] == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_cleanup_needs_no_bearer_and_retains_immutable_fixture(tmp_path):
|
||||||
|
env = {"AUDIT_T02_FIXTURE_ID": "audit-t02-recovery-20260822"}
|
||||||
|
result = DRIVER.run_phase("cleanup", DRIVER.CONTRACT_ID, env=env)
|
||||||
|
assert result == {
|
||||||
|
"contract_id": DRIVER.CONTRACT_ID,
|
||||||
|
"fixture_id": "audit-t02-recovery-20260822",
|
||||||
|
"status": "clean",
|
||||||
|
"secret_values_observed": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_private_mounted_bearer_is_required(tmp_path):
|
||||||
|
env = environment(tmp_path)
|
||||||
|
Path(env["AUDIT_T02_TOKEN_FILE"]).chmod(0o644)
|
||||||
|
config = DRIVER.load_config(env)
|
||||||
|
with pytest.raises(DRIVER.DriverError, match="private and regular"):
|
||||||
|
DRIVER.request_event(config)
|
||||||
|
|
||||||
|
|
||||||
|
def test_payload_is_synthetic_and_never_contains_bearer(tmp_path):
|
||||||
|
env = environment(tmp_path)
|
||||||
|
config = DRIVER.load_config(env)
|
||||||
|
payload = DRIVER.event_payload(config)
|
||||||
|
assert payload["data"] == {
|
||||||
|
"exercise": "RAILIANCE-WP-0024-T02",
|
||||||
|
"synthetic": True,
|
||||||
|
}
|
||||||
|
assert "opaque-test-value" not in repr(payload)
|
||||||
|
|
||||||
|
|
||||||
|
def test_wrong_contract_is_refused_without_reading_token(tmp_path):
|
||||||
|
env = {"AUDIT_T02_FIXTURE_ID": "audit-t02-recovery-20260822"}
|
||||||
|
with pytest.raises(DRIVER.DriverError, match="not approved"):
|
||||||
|
DRIVER.run_phase("cleanup", "some-other-contract", env=env)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue