compute-guard: Hub conditions (90-day cap, named approver, CODEOWNERS, engine-port noise cut)

- non-structural exemptions need approved_by (windy-hub|windy-mind) and expire within 90 days;
  a longer amnesty simply does not apply and is reported (OVER-CAP). compute-door/guard-self: yearly.
- .github/CODEOWNERS on the allow-lists + guard.
- engine-port rule skips contracts/schemas/specs/openapi dirs and *.json (53 baseline hits, was 57).
- tests: findings carry kind+name never the value; shipped allow file obeys its own rules.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Kit OC5
2026-10-02 18:13:18 -04:00
parent 5fa1e652ae
commit 5347c11f69
4 changed files with 82 additions and 6 deletions

7
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,7 @@
# Changes to the guard allow-lists / exemptions need review by the account owner on GitHub, AND an
# approved_by (windy-hub | windy-mind) on every non-structural entry, which the guard enforces itself
# (scripts/compute_guard.py: load_allow). A lane never approves its own exemption (Hub 10-02).
/ci/compute-guard-allow.yml @sneakyfree
/ci/secret-guard-allow.yml @sneakyfree
/ci/ci-hygiene-allow.yml @sneakyfree
/scripts/compute_guard.py @sneakyfree

View File

@@ -78,16 +78,18 @@ def test_allow_list_needs_a_reason_per_entry(tmp_path):
def _entry(**kw):
base = dict(repo="x", paths=["*"], reason="r", exemption="owner-approved", expires="2099-01-01")
base = dict(repo="x", paths=["*"], reason="r", exemption="owner-approved", expires="2099-01-01",
approved_by="windy-hub")
base.update(kw)
lines = ["allow:", " - repo: x", " paths: ['*']", " reason: r"]
for k in ("exemption", "expires"):
for k in ("exemption", "expires", "approved_by"):
if base.get(k) is not None:
lines.append(f" {k}: {base[k]}")
return "\n".join(lines) + "\n"
def test_allow_entries_need_a_named_exemption_and_an_expiry(tmp_path):
from datetime import date
f = tmp_path / "a.yml"
f.write_text(_entry(exemption=None))
with pytest.raises(ValueError):
@@ -101,8 +103,8 @@ def test_allow_entries_need_a_named_exemption_and_an_expiry(tmp_path):
f.write_text(_entry(expires="someday"))
with pytest.raises(ValueError):
cg.load_allow(f)
f.write_text(_entry())
assert len(cg.load_allow(f)) == 1
f.write_text(_entry(expires="2026-12-01"))
assert len(cg.load_allow(f, today=date(2026, 10, 2))) == 1
def test_expired_exemption_stops_excusing_and_is_reported(tmp_path):
@@ -330,3 +332,39 @@ def test_ollama_in_added_pr_lines_only():
"+URL = 'http://veron:11434/api/chat'\n")
got = cg.parse_added("some-repo", diff, [])
assert [(f.kind, f.line) for f in got] == [("veron ollama", 2)]
def test_non_structural_exemptions_need_an_independent_approver_and_a_90_day_cap(tmp_path):
from datetime import date
f = tmp_path / "a.yml"
f.write_text(_entry(approved_by=None))
with pytest.raises(ValueError):
cg.load_allow(f, today=date(2026, 10, 2))
f.write_text(_entry(approved_by="windy-chat")) # a lane may not approve itself/another lane
with pytest.raises(ValueError):
cg.load_allow(f, today=date(2026, 10, 2))
f.write_text(_entry(expires="2026-12-31")) # exactly 90 days: fine
assert len(cg.load_allow(f, today=date(2026, 10, 2))) == 1
f.write_text(_entry(expires="2027-01-01")) # 91 days: does NOT apply, and is reported
assert cg.load_allow(f, today=date(2026, 10, 2)) == [] and cg.OVERCAP
f.write_text(_entry(exemption="compute-door", approved_by=None, expires="2027-10-02"))
assert len(cg.load_allow(f, today=date(2026, 10, 2))) == 1 # structural: yearly, no approver field
def test_shipped_allow_file_obeys_its_own_rules():
allow = cg.load_allow()
assert allow and not cg.EXPIRED and not cg.OVERCAP
for e in allow:
if e["exemption"] not in cg.STRUCTURAL:
assert e["approved_by"] in cg.APPROVERS
def test_findings_carry_kind_and_name_never_the_value_and_ports_skip_contracts():
for line, kind in [("ELEVENLABS_API_KEY=sk_live_SUPERSECRET123456789", "voice-ai key"),
('DEEPGRAM_API_KEY = "dg-VALUE-0123456789abcdef"', "voice-ai key")]:
hits = cg.scan_line("app/x.py", line)
assert [k for k, _ in hits] == [kind]
assert all("SUPERSECRET" not in m and "VALUE" not in m for _, m in hits)
assert cg.scan_line("engine/contracts/ops.mcp.v1.json", '"url": "http://h:8099/x"') == []
assert cg.scan_line("services/api/openapi/spec.json", '"url": "http://h:8099/x"') == []
assert [k for k, _ in cg.scan_line("deploy/docker-compose.yml", " - 8099:8099 # :8099")] == ["talk engine port"]

View File

@@ -4,7 +4,8 @@
# relative to the repo root. Owner of this file: Windy Git lane (13); changes
# go through the orchestrator. EVERY entry needs `exemption` (local-user-hardware | owner-approved |
# compute-door | guard-self) and `expires` (YYYY-MM-DD): nothing gets permanent amnesty (Mind 10-02);
# an expired entry stops excusing code on that date and shows in the guard report. Source of the first entries: COMPUTE_BYPASS_AUDIT.md.
# non-structural exemptions also need approved_by (windy-hub | windy-mind; a lane never approves its own)
# and expire within 90 days; an expired entry stops excusing code on that date and shows in the guard report. Source of the first entries: COMPUTE_BYPASS_AUDIT.md.
allow:
- repo: windy-mind
paths: ["*"]
@@ -19,18 +20,24 @@ allow:
Mind stays opt-in there, or every self-hosted user's inference lands on
Grant's bill (no-cloud-cost-liability rule; audit #7).
exemption: owner-approved
approved_by: windy-hub
approved_on: 2026-10-02
expires: 2026-12-31
- repo: windy-code
paths: ["extensions/windy-ai/*"]
reason: "User BYOK AI extension: the user's own provider keys; Mind is one opt-in provider (audit #8)."
exemption: owner-approved
approved_by: windy-hub
approved_on: 2026-10-02
expires: 2026-12-31
- repo: windy-connect
paths: ["*writers/*"]
reason: "Writes client configs that NAME the user's own provider env vars; makes no provider calls (audit #11)."
exemption: owner-approved
approved_by: windy-hub
approved_on: 2026-10-02
expires: 2026-12-31
- repo: windy-pro
@@ -41,12 +48,16 @@ allow:
the CSP line allows exactly those user-keyed hosts (audit #10). The
account-server is NOT covered: server-side calls go through Mind.
exemption: owner-approved
approved_by: windy-hub
approved_on: 2026-10-02
expires: 2026-12-31
- repo: windy-pro
paths: ["src/client/web/src/pages/panels/MindPanel.jsx"]
reason: "Validates the USER's own OpenRouter key for BYOK (audit #10); spends no house money."
exemption: owner-approved
approved_by: windy-hub
approved_on: 2026-10-02
expires: 2026-12-31
- repo: windy-pro
@@ -56,6 +67,8 @@ allow:
matches: ['openrouter\.ai/auth\?', 'openrouter\.ai/api/v1/auth/keys']
reason: "BYOK key acquisition via OpenRouter OAuth PKCE; no inference; successor of MindPanel allow (ADR-064)."
exemption: owner-approved
approved_by: windy-hub
approved_on: 2026-10-02
expires: 2026-12-31
- repo: windy-git

View File

@@ -31,7 +31,7 @@ import re
import subprocess
import sys
from dataclasses import dataclass
from datetime import date
from datetime import date, timedelta
from pathlib import Path
import yaml
@@ -73,6 +73,8 @@ PY_SDKS = r"anthropic|openai|groq|mistralai|cohere|google\.generativeai|google\.
JS_SDKS = (r"@anthropic-ai/sdk|openai|groq-sdk|@google/generative-ai|@google/genai|@mistralai/mistralai"
r"|cohere-ai|together-ai|@ai-sdk/(?:anthropic|openai|groq|google|mistral)")
# The engine-port rule is about CODE/CONFIG that calls the engine, not API contracts, schemas or specs.
PORT_SKIP = re.compile(r"(^|/)(contracts?|schemas?|specs?|openapi)/|\.json$", re.I)
WRANGLER = re.compile(r"(^|/)wrangler\.(toml|jsonc?)$")
WRANGLER_AI = re.compile(r'^\s*\[ai\]\s*$|^\s*"ai"\s*:\s*\{')
@@ -121,7 +123,11 @@ class Finding:
EXEMPTIONS = {"local-user-hardware", "owner-approved", "compute-door", "guard-self"}
STRUCTURAL = {"compute-door", "guard-self"} # the door itself and the guard's own files: yearly review
APPROVERS = {"windy-hub", "windy-mind"} # a lane never approves its own exemption (Hub 10-02)
MAX_DAYS = 90 # every other exemption: 90 days max, then re-approve
EXPIRED: list[dict] = [] # entries dropped as expired on the last load_allow (reported, never silent)
OVERCAP: list[dict] = [] # entries dropped because their expiry is further out than MAX_DAYS
def load_allow(path: Path = ALLOW_FILE, today: date | None = None, strict: bool = True) -> list[dict]:
@@ -133,6 +139,7 @@ def load_allow(path: Path = ALLOW_FILE, today: date | None = None, strict: bool
entries = data.get("allow") or []
active = []
EXPIRED.clear()
OVERCAP.clear()
for e in entries:
if not (e.get("repo") and e.get("paths") and str(e.get("reason", "")).strip()):
raise ValueError(f"allow entry needs repo, paths and a reason: {e}")
@@ -145,6 +152,12 @@ def load_allow(path: Path = ALLOW_FILE, today: date | None = None, strict: bool
exp = e["expires"] if isinstance(e.get("expires"), date) else date.fromisoformat(str(e.get("expires")))
except ValueError as err:
raise ValueError(f"allow entry needs expires: YYYY-MM-DD: {e.get('repo')} {e.get('paths')}") from err
if e["exemption"] not in STRUCTURAL:
if e.get("approved_by") not in APPROVERS:
raise ValueError(f"allow entry needs approved_by in {sorted(APPROVERS)}: {e.get('repo')} {e.get('paths')}")
if exp > today + timedelta(days=MAX_DAYS):
OVERCAP.append({**e, "expires": exp.isoformat()}) # a longer amnesty simply does not apply
continue
if exp < today:
EXPIRED.append({**e, "expires": exp.isoformat()})
else:
@@ -182,6 +195,8 @@ def scan_line(path: str, text: str) -> list[tuple[str, str]]:
continue
if kind == "workers ai binding" and not WRANGLER.search(path):
continue
if kind == "talk engine port" and PORT_SKIP.search(path):
continue
m = rx.search(text)
if m:
hits.append((kind, m.group(0).strip()[:60]))
@@ -358,6 +373,9 @@ def status_for(findings: list[Finding], whole_tree: bool,
def report(repos: list[str]) -> int:
allow = load_allow()
for e in OVERCAP:
print(f"## OVER-CAP exemption (> {MAX_DAYS} days, NOT applied): {e['repo']} {e['paths']} "
f"[{e['exemption']}] expires {e['expires']}")
for e in EXPIRED:
print(f"## EXPIRED exemption (no longer excuses anything): {e['repo']} {e['paths']} "
f"[{e['exemption']}] expired {e['expires']}")