Spaces:
Running
Running
Add typed OpenAPI, MELT, and runtime contracts
#3
by betterwithage - opened
- Dockerfile +1 -1
- README.md +11 -0
- killinchu_public_contracts.py +186 -0
- serve.py +20 -0
- szl_be_hardening.py +27 -0
- tests/test_public_contracts.py +56 -0
Dockerfile
CHANGED
|
@@ -112,6 +112,7 @@ COPY cathedral.html knowledge.json operator_shell_v4.py serve.py szl_evidence_re
|
|
| 112 |
# falls back and /spaces + /api/<ns>/v1/spaces/health 404. Byte-identical a11oy+killinchu.
|
| 113 |
COPY szl_spaces_proxy.py szl_spaces_surface.py ./
|
| 114 |
COPY szl_live_wires.py live_wires.html live_wires_3d.js szl_rosie_companion.py killinchu_szl_pqc_sign.py szl_rekor.py killinchu_osint.py szl_be_hardening.py szl_unay.py szl_khipu_lmdb.py szl_khipu_replicate.py szl_unay_routes.py szl_warhacker_aliases.py killinchu_genius.py killinchu_warhacker_demos.py killinchu_v3.py szl_brain.py szl_rag.py szl_formulas.py szl_understudy.py ./
|
|
|
|
| 115 |
COPY szl_killinchu_cookbook.py szl_uds_hardening.py killinchu_fusion.py szl_v4_fleet.py szl_ken.py killinchu_frontier_patch.py killinchu_drone_routes.py killinchu_parity.py killinchu_cannonico.py killinchu_elite_console.py killinchu_experience.py _vendor_blobs.py killinchu_fleet_vessels.py killinchu_maritime_risk.py killinchu_maritime_intel.py killinchu_maritime_view.py killinchu_live_feeds.py killinchu_feeds_realdata.py killinchu_asw.py killinchu_anatomy.py killinchu_health_twin.py fleet_vessels_data.json killinchu_beyond.py szl_khipu_consensus.py killinchu_mesh.py killinchu_mesh_view.py killinchu_formula_endpoints.py killinchu_edge_formulas.py killinchu_active_flux.py killinchu_platform_dynamics.py ./
|
| 116 |
COPY killinchu_edge_console.py szl_agentic_loop.py szl_anatomy_routes.py szl_formula_wiring.py a11oy_code_engine.py killinchu_ops_control.py szl_llm_registry.py szl_alloy_models.py a11oy_agent_loop.py a11oy_org_rag.py a11oy_mcp_client.py killinchu_mined_ops.py killinchu_resweep_ops.py killinchu_wave910.py killinchu_posture_topology.py szl_connectors_serve.py szl_connector_mcp.py szl_scaling.py szl_allodial.py szl_entanglement.py szl_neuroplasticity.py szl_chain_of_title.py ./
|
| 117 |
# Wave 15 frontier organs (Dynamic Linear Attention, Context-Ready Transformer, OPERA perplexity-reward).
|
|
@@ -701,4 +702,3 @@ CMD ["python", "serve.py"]
|
|
| 701 |
|
| 702 |
# Build cache-bust 2026-06-07T05:00Z (MINED ops squad): COPY killinchu_mined_ops.py
|
| 703 |
# into /app so serve.py can import+register the 4 mined operational/efficiency surfaces.
|
| 704 |
-
|
|
|
|
| 112 |
# falls back and /spaces + /api/<ns>/v1/spaces/health 404. Byte-identical a11oy+killinchu.
|
| 113 |
COPY szl_spaces_proxy.py szl_spaces_surface.py ./
|
| 114 |
COPY szl_live_wires.py live_wires.html live_wires_3d.js szl_rosie_companion.py killinchu_szl_pqc_sign.py szl_rekor.py killinchu_osint.py szl_be_hardening.py szl_unay.py szl_khipu_lmdb.py szl_khipu_replicate.py szl_unay_routes.py szl_warhacker_aliases.py killinchu_genius.py killinchu_warhacker_demos.py killinchu_v3.py szl_brain.py szl_rag.py szl_formulas.py szl_understudy.py ./
|
| 115 |
+
COPY killinchu_public_contracts.py ./
|
| 116 |
COPY szl_killinchu_cookbook.py szl_uds_hardening.py killinchu_fusion.py szl_v4_fleet.py szl_ken.py killinchu_frontier_patch.py killinchu_drone_routes.py killinchu_parity.py killinchu_cannonico.py killinchu_elite_console.py killinchu_experience.py _vendor_blobs.py killinchu_fleet_vessels.py killinchu_maritime_risk.py killinchu_maritime_intel.py killinchu_maritime_view.py killinchu_live_feeds.py killinchu_feeds_realdata.py killinchu_asw.py killinchu_anatomy.py killinchu_health_twin.py fleet_vessels_data.json killinchu_beyond.py szl_khipu_consensus.py killinchu_mesh.py killinchu_mesh_view.py killinchu_formula_endpoints.py killinchu_edge_formulas.py killinchu_active_flux.py killinchu_platform_dynamics.py ./
|
| 117 |
COPY killinchu_edge_console.py szl_agentic_loop.py szl_anatomy_routes.py szl_formula_wiring.py a11oy_code_engine.py killinchu_ops_control.py szl_llm_registry.py szl_alloy_models.py a11oy_agent_loop.py a11oy_org_rag.py a11oy_mcp_client.py killinchu_mined_ops.py killinchu_resweep_ops.py killinchu_wave910.py killinchu_posture_topology.py szl_connectors_serve.py szl_connector_mcp.py szl_scaling.py szl_allodial.py szl_entanglement.py szl_neuroplasticity.py szl_chain_of_title.py ./
|
| 118 |
# Wave 15 frontier organs (Dynamic Linear Attention, Context-Ready Transformer, OPERA perplexity-reward).
|
|
|
|
| 702 |
|
| 703 |
# Build cache-bust 2026-06-07T05:00Z (MINED ops squad): COPY killinchu_mined_ops.py
|
| 704 |
# into /app so serve.py can import+register the 4 mined operational/efficiency surfaces.
|
|
|
README.md
CHANGED
|
@@ -467,4 +467,15 @@ One sovereign substrate, many organs — every decision carries a signed, checka
|
|
| 467 |
|
| 468 |
<sub>Doctrine v11 · Λ = Conjecture 1, never green · honest by design · public data only.</sub>
|
| 469 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 470 |
Signed-off-by: Stephen P. Lutar Jr. <stephenlutar2@gmail.com>
|
|
|
|
| 467 |
|
| 468 |
<sub>Doctrine v11 · Λ = Conjecture 1, never green · honest by design · public data only.</sub>
|
| 469 |
|
| 470 |
+
## Public evidence contracts
|
| 471 |
+
|
| 472 |
+
- `GET /health` - typed read-only health contract; reachability is not readiness.
|
| 473 |
+
- `GET /api/killinchu/v1/status` - runtime, doctrine, and contract discovery.
|
| 474 |
+
- `GET /api/killinchu/v1/melt/summary` - measured in-process MELT summary with explicit external-trace limits.
|
| 475 |
+
- `GET /openapi.json`, `/api/killinchu/openapi.json`, or `/api/killinchu/v1/openapi.json` - the same generated API description, with bad legacy annotations omitted rather than crashing discovery.
|
| 476 |
+
|
| 477 |
+
The MELT contract exposes aggregate metric family and sample counts, not private
|
| 478 |
+
log content. It does not claim an external collector, complete distributed
|
| 479 |
+
traces, model quality, mission readiness, or actuator authority.
|
| 480 |
+
|
| 481 |
Signed-off-by: Stephen P. Lutar Jr. <stephenlutar2@gmail.com>
|
killinchu_public_contracts.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Read-only public contracts for Killinchu runtime and MELT observability.
|
| 2 |
+
|
| 3 |
+
These endpoints expose measured in-process telemetry and route presence. They
|
| 4 |
+
do not imply model quality, external collector export, operational readiness,
|
| 5 |
+
or authority to actuate a defense system.
|
| 6 |
+
"""
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
import os
|
| 10 |
+
from datetime import datetime, timezone
|
| 11 |
+
from typing import Any
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def _now() -> str:
|
| 15 |
+
return datetime.now(timezone.utc).isoformat()
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def _has_route(app: Any, path: str, method: str = "GET") -> bool:
|
| 19 |
+
for route in getattr(getattr(app, "router", None), "routes", []):
|
| 20 |
+
methods = getattr(route, "methods", set()) or set()
|
| 21 |
+
if getattr(route, "path", None) == path and method in methods:
|
| 22 |
+
return True
|
| 23 |
+
return False
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def _metrics_snapshot(app: Any) -> dict[str, Any]:
|
| 27 |
+
try:
|
| 28 |
+
import szl_metrics_prom
|
| 29 |
+
|
| 30 |
+
text = szl_metrics_prom.render(app)
|
| 31 |
+
families = sorted(
|
| 32 |
+
{
|
| 33 |
+
line.split()[2]
|
| 34 |
+
for line in text.splitlines()
|
| 35 |
+
if line.startswith("# HELP ") and len(line.split()) >= 3
|
| 36 |
+
}
|
| 37 |
+
)
|
| 38 |
+
samples = [
|
| 39 |
+
line
|
| 40 |
+
for line in text.splitlines()
|
| 41 |
+
if line and not line.startswith("#")
|
| 42 |
+
]
|
| 43 |
+
return {
|
| 44 |
+
"state": "LIVE_IN_PROCESS",
|
| 45 |
+
"metric_family_count": len(families),
|
| 46 |
+
"sample_count": len(samples),
|
| 47 |
+
"metric_families": families,
|
| 48 |
+
"source": "/metrics",
|
| 49 |
+
}
|
| 50 |
+
except Exception as exc:
|
| 51 |
+
return {
|
| 52 |
+
"state": "UNAVAILABLE",
|
| 53 |
+
"metric_family_count": 0,
|
| 54 |
+
"sample_count": 0,
|
| 55 |
+
"metric_families": [],
|
| 56 |
+
"source": "/metrics",
|
| 57 |
+
"error_type": type(exc).__name__,
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def melt_summary(app: Any, ns: str = "killinchu") -> dict[str, Any]:
|
| 62 |
+
metrics = _metrics_snapshot(app)
|
| 63 |
+
metrics_route = _has_route(app, "/metrics")
|
| 64 |
+
mesh_route = _has_route(app, f"/api/{ns}/v1/mesh/state")
|
| 65 |
+
live = metrics_route and metrics["state"] == "LIVE_IN_PROCESS"
|
| 66 |
+
return {
|
| 67 |
+
"schema": "szl.killinchu.melt-summary/v1",
|
| 68 |
+
"observed_at": _now(),
|
| 69 |
+
"transport_state": "REACHABLE",
|
| 70 |
+
"evidence_state": "LIVE" if live else "UNAVAILABLE",
|
| 71 |
+
"authority_state": "READ_ONLY",
|
| 72 |
+
"scope": "MELT runtime observability",
|
| 73 |
+
"signals": {
|
| 74 |
+
"metrics": metrics,
|
| 75 |
+
"events": {
|
| 76 |
+
"state": "IN_PROCESS",
|
| 77 |
+
"basis": "request and receipt events remain within the running process unless a durable subsystem records them",
|
| 78 |
+
},
|
| 79 |
+
"logs": {
|
| 80 |
+
"state": "IN_PROCESS",
|
| 81 |
+
"basis": "structured application logs are emitted by the runtime; no public log corpus is exposed here",
|
| 82 |
+
},
|
| 83 |
+
"traces": {
|
| 84 |
+
"state": "EXPORT_UNAVAILABLE",
|
| 85 |
+
"basis": "OTLP export is not enabled in this public Space build",
|
| 86 |
+
},
|
| 87 |
+
},
|
| 88 |
+
"contracts": {
|
| 89 |
+
"metrics_route_registered": metrics_route,
|
| 90 |
+
"mesh_state_route_registered": mesh_route,
|
| 91 |
+
"metrics_endpoint": "/metrics",
|
| 92 |
+
"mesh_endpoint": f"/api/{ns}/v1/mesh/state",
|
| 93 |
+
},
|
| 94 |
+
"limits": [
|
| 95 |
+
"LIVE means measured in this process, not an uptime SLA.",
|
| 96 |
+
"No external collector, distributed trace completeness, model quality, or business outcome is claimed.",
|
| 97 |
+
"This endpoint is read-only and performs no effector action.",
|
| 98 |
+
],
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def runtime_status(app: Any, ns: str = "killinchu") -> dict[str, Any]:
|
| 103 |
+
route_count = len(getattr(getattr(app, "router", None), "routes", []))
|
| 104 |
+
return {
|
| 105 |
+
"schema": "szl.killinchu.runtime-status/v1",
|
| 106 |
+
"observed_at": _now(),
|
| 107 |
+
"service": ns,
|
| 108 |
+
"transport_state": "REACHABLE",
|
| 109 |
+
"evidence_state": "COMPUTED",
|
| 110 |
+
"verification_state": "STRUCTURAL_ONLY",
|
| 111 |
+
"authority_state": "READ_ONLY",
|
| 112 |
+
"doctrine": {
|
| 113 |
+
"version": "v11",
|
| 114 |
+
"locked_core": "749/14/163 @ c7c0ba17",
|
| 115 |
+
"lambda": "Conjecture 1",
|
| 116 |
+
},
|
| 117 |
+
"runtime": {
|
| 118 |
+
"route_count": route_count,
|
| 119 |
+
"git_sha": os.getenv("SZL_GIT_SHA", "unknown"),
|
| 120 |
+
"build_time": os.getenv("SZL_BUILD_TIME", "unknown"),
|
| 121 |
+
},
|
| 122 |
+
"contracts": {
|
| 123 |
+
"experience_manifest": f"/api/{ns}/v1/experience/manifest",
|
| 124 |
+
"openapi": f"/api/{ns}/v1/openapi.json",
|
| 125 |
+
"melt": f"/api/{ns}/v1/melt/summary",
|
| 126 |
+
"metrics": "/metrics",
|
| 127 |
+
"readiness": f"/api/{ns}/readyz",
|
| 128 |
+
},
|
| 129 |
+
"limits": [
|
| 130 |
+
"Reachability and registered routes do not establish data freshness, model quality, or mission readiness.",
|
| 131 |
+
"Defense effectors remain simulated and human-controlled.",
|
| 132 |
+
],
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def register(app: Any, ns: str = "killinchu") -> dict[str, Any]:
|
| 137 |
+
from starlette.responses import JSONResponse
|
| 138 |
+
from starlette.routing import Route
|
| 139 |
+
|
| 140 |
+
async def _health(_request):
|
| 141 |
+
payload = runtime_status(app, ns)
|
| 142 |
+
payload["schema"] = "szl.killinchu.health/v1"
|
| 143 |
+
return JSONResponse(payload, headers={"Cache-Control": "no-store"})
|
| 144 |
+
|
| 145 |
+
async def _status(_request):
|
| 146 |
+
return JSONResponse(runtime_status(app, ns), headers={"Cache-Control": "no-store"})
|
| 147 |
+
|
| 148 |
+
async def _melt(_request):
|
| 149 |
+
return JSONResponse(melt_summary(app, ns), headers={"Cache-Control": "no-store"})
|
| 150 |
+
|
| 151 |
+
routes = [
|
| 152 |
+
Route("/health", _health, methods=["GET"], name="killinchu_public_health"),
|
| 153 |
+
Route(
|
| 154 |
+
f"/api/{ns}/v1/status",
|
| 155 |
+
_status,
|
| 156 |
+
methods=["GET"],
|
| 157 |
+
name="killinchu_public_status",
|
| 158 |
+
),
|
| 159 |
+
Route(
|
| 160 |
+
f"/api/{ns}/v1/melt/summary",
|
| 161 |
+
_melt,
|
| 162 |
+
methods=["GET"],
|
| 163 |
+
name="killinchu_melt_summary",
|
| 164 |
+
),
|
| 165 |
+
]
|
| 166 |
+
existing = {getattr(route, "name", None) for route in app.router.routes}
|
| 167 |
+
new = [route for route in routes if route.name not in existing]
|
| 168 |
+
app.router.routes[0:0] = new
|
| 169 |
+
return {
|
| 170 |
+
"schema": "szl.killinchu.public-contract-registration/v1",
|
| 171 |
+
"routes": [route.path for route in new],
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
if __name__ == "__main__":
|
| 176 |
+
from fastapi import FastAPI
|
| 177 |
+
|
| 178 |
+
candidate = FastAPI()
|
| 179 |
+
candidate.add_api_route("/metrics", lambda: "", methods=["GET"])
|
| 180 |
+
candidate.add_api_route(
|
| 181 |
+
"/api/killinchu/v1/mesh/state", lambda: {}, methods=["GET"]
|
| 182 |
+
)
|
| 183 |
+
result = register(candidate)
|
| 184 |
+
assert len(result["routes"]) == 3
|
| 185 |
+
assert runtime_status(candidate)["transport_state"] == "REACHABLE"
|
| 186 |
+
print("SELFTEST OK")
|
serve.py
CHANGED
|
@@ -5583,6 +5583,26 @@ except Exception as _kc_wave_e: # pragma: no cover — never break SPA/other or
|
|
| 5583 |
# ============================================================================
|
| 5584 |
|
| 5585 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5586 |
if __name__ == "__main__":
|
| 5587 |
import uvicorn
|
| 5588 |
port = int(os.environ.get("PORT", "7860"))
|
|
|
|
| 5583 |
# ============================================================================
|
| 5584 |
|
| 5585 |
|
| 5586 |
+
# Public discovery and MELT contracts. Registered last and front-inserted so
|
| 5587 |
+
# they cannot be shadowed by the SPA history route. All endpoints are GET-only.
|
| 5588 |
+
try:
|
| 5589 |
+
import killinchu_public_contracts as _killinchu_public_contracts
|
| 5590 |
+
|
| 5591 |
+
_killinchu_public_contracts_status = _killinchu_public_contracts.register(
|
| 5592 |
+
app, ns="killinchu"
|
| 5593 |
+
)
|
| 5594 |
+
print(
|
| 5595 |
+
f"[killinchu] public contracts registered: {_killinchu_public_contracts_status}",
|
| 5596 |
+
file=sys.stderr,
|
| 5597 |
+
)
|
| 5598 |
+
except Exception as _killinchu_public_contracts_e: # pragma: no cover
|
| 5599 |
+
print(
|
| 5600 |
+
f"[killinchu] public contracts NOT registered: {_killinchu_public_contracts_e!r}; "
|
| 5601 |
+
"SPA + existing API unaffected",
|
| 5602 |
+
file=sys.stderr,
|
| 5603 |
+
)
|
| 5604 |
+
|
| 5605 |
+
|
| 5606 |
if __name__ == "__main__":
|
| 5607 |
import uvicorn
|
| 5608 |
port = int(os.environ.get("PORT", "7860"))
|
szl_be_hardening.py
CHANGED
|
@@ -640,6 +640,33 @@ def harden(app: Any, organ: str, ns: Optional[str] = None,
|
|
| 640 |
status_code=500,
|
| 641 |
)
|
| 642 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 643 |
report["registered"].append(f"openapi:/api/{organ}/openapi.json")
|
| 644 |
|
| 645 |
# ---- 9: honest footer -------------------------------------------------
|
|
|
|
| 640 |
status_code=500,
|
| 641 |
)
|
| 642 |
|
| 643 |
+
# FastAPI's default /openapi.json can fail when one legacy route carries an
|
| 644 |
+
# annotation that Pydantic cannot resolve. The organ endpoint above already
|
| 645 |
+
# has a bounded, route-by-route fallback. Front-insert aliases so all public
|
| 646 |
+
# discovery paths use that generator instead of exposing a known 500.
|
| 647 |
+
try:
|
| 648 |
+
from starlette.routing import Route
|
| 649 |
+
|
| 650 |
+
async def _openapi_alias(_request):
|
| 651 |
+
value = await _organ_openapi()
|
| 652 |
+
return JSONResponse(value) if isinstance(value, dict) else value
|
| 653 |
+
|
| 654 |
+
aliases = [
|
| 655 |
+
Route("/openapi.json", _openapi_alias, methods=["GET"], name=f"{organ}_root_openapi"),
|
| 656 |
+
Route(
|
| 657 |
+
f"/api/{organ}/v1/openapi.json",
|
| 658 |
+
_openapi_alias,
|
| 659 |
+
methods=["GET"],
|
| 660 |
+
name=f"{organ}_v1_openapi",
|
| 661 |
+
),
|
| 662 |
+
]
|
| 663 |
+
app.router.routes[0:0] = aliases
|
| 664 |
+
report["registered"].extend(
|
| 665 |
+
["openapi:/openapi.json", f"openapi:/api/{organ}/v1/openapi.json"]
|
| 666 |
+
)
|
| 667 |
+
except Exception as exc:
|
| 668 |
+
logger.error(f"openapi_aliases_unavailable: {exc!r}")
|
| 669 |
+
|
| 670 |
report["registered"].append(f"openapi:/api/{organ}/openapi.json")
|
| 671 |
|
| 672 |
# ---- 9: honest footer -------------------------------------------------
|
tests/test_public_contracts.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import unittest
|
| 2 |
+
|
| 3 |
+
from fastapi import FastAPI
|
| 4 |
+
from fastapi.testclient import TestClient
|
| 5 |
+
|
| 6 |
+
import killinchu_public_contracts
|
| 7 |
+
import szl_be_hardening
|
| 8 |
+
import szl_metrics_prom
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class PublicContractTests(unittest.TestCase):
|
| 12 |
+
def build_app(self):
|
| 13 |
+
app = FastAPI(title="contract-test", version="1")
|
| 14 |
+
szl_metrics_prom.register(app, ns="killinchu")
|
| 15 |
+
|
| 16 |
+
@app.get("/api/killinchu/v1/mesh/state")
|
| 17 |
+
async def mesh_state():
|
| 18 |
+
return {"state": "test"}
|
| 19 |
+
|
| 20 |
+
szl_be_hardening.harden(app, organ="killinchu", khipu_path=":memory:")
|
| 21 |
+
killinchu_public_contracts.register(app, ns="killinchu")
|
| 22 |
+
return app
|
| 23 |
+
|
| 24 |
+
def test_health_status_and_melt_are_typed(self):
|
| 25 |
+
client = TestClient(self.build_app())
|
| 26 |
+
health = client.get("/health")
|
| 27 |
+
self.assertEqual(health.status_code, 200)
|
| 28 |
+
self.assertEqual(health.json()["transport_state"], "REACHABLE")
|
| 29 |
+
self.assertEqual(health.json()["authority_state"], "READ_ONLY")
|
| 30 |
+
|
| 31 |
+
status = client.get("/api/killinchu/v1/status")
|
| 32 |
+
self.assertEqual(status.status_code, 200)
|
| 33 |
+
self.assertEqual(status.json()["schema"], "szl.killinchu.runtime-status/v1")
|
| 34 |
+
|
| 35 |
+
melt = client.get("/api/killinchu/v1/melt/summary")
|
| 36 |
+
self.assertEqual(melt.status_code, 200)
|
| 37 |
+
self.assertEqual(melt.json()["evidence_state"], "LIVE")
|
| 38 |
+
self.assertTrue(melt.json()["contracts"]["metrics_route_registered"])
|
| 39 |
+
self.assertTrue(melt.json()["contracts"]["mesh_state_route_registered"])
|
| 40 |
+
self.assertEqual(melt.json()["signals"]["traces"]["state"], "EXPORT_UNAVAILABLE")
|
| 41 |
+
|
| 42 |
+
def test_all_openapi_discovery_paths_avoid_legacy_500(self):
|
| 43 |
+
client = TestClient(self.build_app())
|
| 44 |
+
for path in (
|
| 45 |
+
"/openapi.json",
|
| 46 |
+
"/api/killinchu/openapi.json",
|
| 47 |
+
"/api/killinchu/v1/openapi.json",
|
| 48 |
+
):
|
| 49 |
+
with self.subTest(path=path):
|
| 50 |
+
response = client.get(path)
|
| 51 |
+
self.assertEqual(response.status_code, 200)
|
| 52 |
+
self.assertEqual(response.json()["openapi"], "3.1.0")
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
if __name__ == "__main__":
|
| 56 |
+
unittest.main()
|