Spaces:
Sleeping
Sleeping
File size: 11,256 Bytes
f74bce1 347a4d6 f74bce1 347a4d6 14a02b9 74ee21a 347a4d6 14a02b9 74ee21a 347a4d6 f74bce1 fc9f64b 347a4d6 f74bce1 f54fafa f74bce1 74ee21a 14a02b9 f74bce1 347a4d6 74ee21a 347a4d6 74ee21a 347a4d6 f74bce1 347a4d6 f74bce1 347a4d6 f74bce1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | #!/usr/bin/env python3
"""Build a redacted live credential handoff report."""
from __future__ import annotations
import argparse
import json
import os
from pathlib import Path
from typing import Any
from proofframe.config import normalize_genblaze_provider
ROOT = Path(__file__).resolve().parents[1]
DEFAULT_JSON = ROOT / "docs" / "assets" / "live-credential-handoff.json"
DEFAULT_MD = ROOT / "docs" / "assets" / "live-credential-handoff.md"
PLACEHOLDERS = {"", "placeholder", "change-me", "changeme", "todo", "tbd", "none", "null", "..."}
REQUIRED_GROUPS = [
{
"id": "storage_backend_mode",
"label": "ProofFrame storage backend mode",
"accepted_names": ["PROOFFRAME_STORAGE_BACKEND"],
"expected": "b2",
"remediation": "Set PROOFFRAME_STORAGE_BACKEND=b2.",
},
{
"id": "generation_backend_mode",
"label": "ProofFrame generation backend mode",
"accepted_names": ["PROOFFRAME_GENERATION_BACKEND"],
"expected": "genblaze",
"remediation": "Set PROOFFRAME_GENERATION_BACKEND=genblaze.",
},
{
"id": "b2_endpoint",
"label": "Backblaze B2 S3 endpoint",
"accepted_names": ["B2_ENDPOINT_URL", "B2_S3_ENDPOINT_URL"],
"remediation": "Set B2_ENDPOINT_URL from the Backblaze bucket S3 endpoint.",
},
{
"id": "b2_bucket",
"label": "Backblaze B2 bucket",
"accepted_names": ["B2_BUCKET"],
"remediation": "Set B2_BUCKET to the dedicated demo bucket name.",
},
{
"id": "b2_key_id",
"label": "Backblaze B2 key id",
"accepted_names": ["B2_KEY_ID"],
"remediation": "Set B2_KEY_ID for a least-privilege application key.",
},
{
"id": "b2_application_key",
"label": "Backblaze B2 application key",
"accepted_names": ["B2_APPLICATION_KEY", "B2_APP_KEY"],
"remediation": "Set B2_APPLICATION_KEY or B2_APP_KEY.",
},
{
"id": "genblaze_provider_key",
"label": "Genblaze provider API key",
"accepted_names_by_provider": {
"gmicloud": ["GENBLAZE_API_KEY", "GMI_API_KEY"],
"openai": ["OPENAI_API_KEY"],
"local": [],
},
"remediation_by_provider": {
"gmicloud": "Set GENBLAZE_API_KEY or GMI_API_KEY.",
"openai": "Set OPENAI_API_KEY.",
"local": "GENBLAZE_PROVIDER=local does not require a provider API key.",
},
},
{
"id": "genblaze_image_model",
"label": "Genblaze image model",
"accepted_names": ["GENBLAZE_IMAGE_MODEL"],
"remediation": "Set GENBLAZE_IMAGE_MODEL to the verified image model.",
},
]
OPTIONAL_GROUPS = [
{
"id": "b2_public_base_url",
"label": "B2 public base URL",
"accepted_names": ["B2_PUBLIC_BASE_URL"],
},
{
"id": "b2_region",
"label": "B2 region for Genblaze sink",
"accepted_names": ["B2_REGION"],
},
{
"id": "genblaze_provider",
"label": "Genblaze provider",
"accepted_names": ["GENBLAZE_PROVIDER"],
},
{
"id": "genblaze_aspect_ratio",
"label": "Genblaze aspect ratio",
"accepted_names": ["GENBLAZE_ASPECT_RATIO"],
},
{
"id": "genblaze_timeout",
"label": "Genblaze timeout seconds",
"accepted_names": ["GENBLAZE_TIMEOUT_SECONDS"],
},
]
NEXT_COMMANDS = [
"python scripts/final_env_wizard.py --prefill-non-secret --output .env.final.local",
"python scripts/final_env_wizard.py --output .env.final.local --missing-only --force",
"python scripts/live_env_handoff.py --env-file .env.final.local",
"python scripts/run_final_live_proof.py --env-file .env.final.local --preflight-only",
"python scripts/run_final_live_proof.py --env-file .env.final.local --genblaze-provider local --genblaze-image-model local-svg-v1 --preflight-only",
"python scripts/run_final_live_proof.py --env-file .env.final.local --genblaze-provider openai --genblaze-image-model gpt-image-1 --preflight-only",
"python scripts/run_final_live_proof.py --env-file .env.final.local --evidence-out docs/assets/final-live-proof-evidence.json",
'python scripts/devpost_packet.py --post-live --video-url "$PROOFFRAME_PUBLIC_VIDEO_URL"',
"python scripts/secret_scan.py",
"python scripts/claim_lint.py",
"python scripts/submission_audit.py --strict-final",
]
def normalize_value(value: str | None) -> str:
if value is None:
return ""
return value.strip().strip('"').strip("'")
def has_real_value(value: str | None) -> bool:
normalized = normalize_value(value)
if normalized.lower() in PLACEHOLDERS:
return False
if normalized.startswith("<") and normalized.endswith(">"):
return False
return bool(normalized)
def parse_env_file(path: Path) -> dict[str, str]:
values: dict[str, str] = {}
for raw_line in path.read_text(encoding="utf-8").splitlines():
line = raw_line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.strip()
if key.startswith("export "):
key = key.removeprefix("export ").strip()
if key:
values[key] = normalize_value(value)
return values
def env_source(env_file: Path | None) -> tuple[dict[str, str], str]:
values = {key: value for key, value in os.environ.items()}
source = "process environment"
if env_file:
file_values = parse_env_file(env_file)
values.update(file_values)
source = str(env_file)
return values, source
def evaluate_group(group: dict[str, Any], values: dict[str, str]) -> dict[str, Any]:
provider = normalize_genblaze_provider(values.get("GENBLAZE_PROVIDER", "gmicloud"))
accepted_names = group.get("accepted_names")
remediation = group.get("remediation", "")
if "accepted_names_by_provider" in group:
accepted_by_provider = group["accepted_names_by_provider"]
accepted_names = accepted_by_provider.get(
provider,
sorted({name for names in accepted_by_provider.values() for name in names}),
)
remediation = group.get("remediation_by_provider", {}).get(
provider,
"Set GENBLAZE_PROVIDER to gmicloud, openai, or local and provide its required setup.",
)
if provider == "local" and not accepted_names:
return {
"id": group["id"],
"label": group["label"],
"ok": True,
"accepted_names": accepted_names,
"present_names": [],
"expected": group.get("expected"),
"remediation": "",
}
present_names = [name for name in accepted_names if has_real_value(values.get(name))]
expected = group.get("expected")
expected_ok = True
if expected is not None:
expected_ok = any(normalize_value(values.get(name)).lower() == expected for name in present_names)
ok = bool(present_names) and expected_ok
return {
"id": group["id"],
"label": group["label"],
"ok": ok,
"accepted_names": accepted_names,
"present_names": present_names,
"expected": expected,
"remediation": "" if ok else remediation,
}
def build_report(env_file: Path | None = None) -> dict[str, Any]:
values, source = env_source(env_file)
required = [evaluate_group(group, values) for group in REQUIRED_GROUPS]
optional = [evaluate_group(group, values) for group in OPTIONAL_GROUPS]
missing = [item for item in required if not item["ok"]]
return {
"schema": "proofframe.live_credential_handoff.v1",
"ok": not missing,
"mode": "live_env_ready" if not missing else "missing_live_env",
"source": source,
"required": required,
"optional": optional,
"missing_ids": [item["id"] for item in missing],
"next_commands": NEXT_COMMANDS,
"secret_policy": (
"This report records only variable names and presence checks. It never prints, hashes, "
"stores, or commits credential values."
),
}
def render_markdown(report: dict[str, Any]) -> str:
lines = [
"# ProofFrame Live Credential Handoff",
"",
f"Mode: `{report['mode']}`",
f"Ready for live proof: `{str(report['ok']).lower()}`",
f"Source: `{report['source']}`",
"",
report["secret_policy"],
"",
"## Required Values",
"",
]
for item in report["required"]:
marker = "OK" if item["ok"] else "MISSING"
names = ", ".join(f"`{name}`" for name in item["accepted_names"])
present = ", ".join(f"`{name}`" for name in item["present_names"]) or "none"
expected = f"; expected `{item['expected']}`" if item["expected"] else ""
lines.append(f"- {marker} {item['label']}: {names}; present {present}{expected}.")
if item["remediation"]:
lines.append(f" Remediation: {item['remediation']}")
lines.extend(["", "## Optional Values", ""])
for item in report["optional"]:
marker = "SET" if item["present_names"] else "UNSET"
names = ", ".join(f"`{name}`" for name in item["accepted_names"])
present = ", ".join(f"`{name}`" for name in item["present_names"]) or "none"
lines.append(f"- {marker} {item['label']}: {names}; present {present}.")
lines.extend(["", "## Next Commands", ""])
lines.extend(f"```bash\n{command}\n```" for command in report["next_commands"])
return "\n".join(lines) + "\n"
def write_outputs(report: dict[str, Any], json_path: Path, markdown_path: Path) -> None:
json_path.parent.mkdir(parents=True, exist_ok=True)
markdown_path.parent.mkdir(parents=True, exist_ok=True)
json_path.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
markdown_path.write_text(render_markdown(report), encoding="utf-8")
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Build a redacted ProofFrame live credential handoff report."
)
parser.add_argument("--env-file", type=Path, help="Optional local env file; values are not printed.")
parser.add_argument("--json-out", type=Path, default=DEFAULT_JSON)
parser.add_argument("--markdown-out", type=Path, default=DEFAULT_MD)
parser.add_argument(
"--strict",
action="store_true",
help="Exit nonzero when required live variables are missing.",
)
return parser
def main() -> None:
args = build_parser().parse_args()
report = build_report(args.env_file)
write_outputs(report, args.json_out, args.markdown_out)
print(
json.dumps(
{
"ok": report["ok"],
"mode": report["mode"],
"json": str(args.json_out),
"markdown": str(args.markdown_out),
"missing_ids": report["missing_ids"],
},
indent=2,
)
)
if args.strict and not report["ok"]:
raise SystemExit(2)
if __name__ == "__main__":
main()
|