feat(model-registry): add delegation-JWT auth and config/snapshot HTTP API

- BFF service token (constant-time, plaintext or SHA-256 hash) plus
  X-Evo-Actor delegation JWT verification (ES256/RS256, iss/aud, <=60s
  lifetime, required claims, thread binding) with atomic jti anti-replay
- Config API: GET/PUT /api/model-registry, credential rotation endpoint,
  GET /api/models selector; PUT runs the section 9.2 save-time checks
  inside the registry write transaction after credential writes
- Snapshot API: create/bind/delete routes delegating to SnapshotService
  with thread/deployment binding checks and 9.5 unified error payloads
- Platform security config loader (config.yaml fields), OpenAPI export
  (scripts/export_model_registry_schema.py -> model_registry/openapi.json)
- Mount new routes in langgraph_dev/http.py; retire the legacy
  GET /api/models and POST /api/runtime-snapshots handlers
- Declare PyJWT>=2.8 (previously transitive); extend the 9.5 error code
  table with the HTTP-layer codes (400/401/403/422/500)
This commit is contained in:
m4
2026-07-21 09:28:55 +08:00
parent c8c46eab16
commit dbb6b7abde
14 changed files with 4536 additions and 374 deletions
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""Export the model registry OpenAPI contract (design doc section 11 step 3).
Regenerates ``EvoScientist/model_registry/openapi.json`` from the same
Pydantic schemas the HTTP API validates against, so contract tests and the
WebUI type generation always track the backend::
.venv/bin/python scripts/export_model_registry_schema.py
"""
from __future__ import annotations
import json
from pathlib import Path
from EvoScientist.model_registry.http_api import build_openapi_document
OUTPUT_PATH = (
Path(__file__).resolve().parent.parent
/ "EvoScientist"
/ "model_registry"
/ "openapi.json"
)
def main() -> None:
document = build_openapi_document()
OUTPUT_PATH.write_text(
json.dumps(document, indent=2, sort_keys=True) + "\n",
encoding="utf-8",
)
print(f"Wrote {OUTPUT_PATH}")
if __name__ == "__main__":
main()