Spaces:
Running
Running
chore(sync): mirror backend .py + Dockerfile to Space (hf-sync-backend)
Browse filesAutomated backend sync from szl-holdings/a11oy main via hf-sync-backend.
Updated (differed from the Space): Dockerfile, a11oy_ayllu_wall.py, serve.py
Deleted (gone from the repo + Dockerfile COPY set): (none)
Keeps the Space-built backend (serve.py + the Dockerfile-COPY'd .py
modules) identical to GitHub main so the Space never rebuilds from a
stale backend, new endpoints don't 404 there, and orphaned modules
removed from the repo don't linger in the Space tree.
- Dockerfile +5 -0
- a11oy_ayllu_wall.py +342 -0
- serve.py +23 -0
Dockerfile
CHANGED
|
@@ -620,6 +620,11 @@ COPY a11oy_quant_signals_nav.py ./a11oy_quant_signals_nav.py
|
|
| 620 |
# wholesale above; this per-file image copies only the new Python registrar.
|
| 621 |
COPY a11oy_ecosystem_atlas.py ./a11oy_ecosystem_atlas.py
|
| 622 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 623 |
# git_sha wireup (FORGE-INSTRUCTION-gitsha-quiet-window): surface the deployed commit
|
| 624 |
# … (full rationale: docs/DOCKERFILE_NOTES.md §96)
|
| 625 |
ARG SZL_GIT_SHA=unknown
|
|
|
|
| 620 |
# wholesale above; this per-file image copies only the new Python registrar.
|
| 621 |
COPY a11oy_ecosystem_atlas.py ./a11oy_ecosystem_atlas.py
|
| 622 |
|
| 623 |
+
# AYLLU COUNCIL WALL (2026-07-21): /api/ayllu/wall + /ayllu/wall — server-side
|
| 624 |
+
# per-request DSSE re-verification of committed council decision receipts,
|
| 625 |
+
# fetched from the public GitHub repo. Fail-closed; key honesty in-band.
|
| 626 |
+
COPY a11oy_ayllu_wall.py ./a11oy_ayllu_wall.py
|
| 627 |
+
|
| 628 |
# git_sha wireup (FORGE-INSTRUCTION-gitsha-quiet-window): surface the deployed commit
|
| 629 |
# … (full rationale: docs/DOCKERFILE_NOTES.md §96)
|
| 630 |
ARG SZL_GIT_SHA=unknown
|
a11oy_ayllu_wall.py
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 2 |
+
# © 2026 Lutar, Stephen P. — SZL Holdings
|
| 3 |
+
"""
|
| 4 |
+
a11oy Ayllu council wall — GET /api/ayllu/wall (JSON) + GET /ayllu/wall (HTML)
|
| 5 |
+
|
| 6 |
+
The public verify-then-display surface for AYLLU council decisions.
|
| 7 |
+
Doctrine: provenance, not vibes. Every decision shown here is re-verified
|
| 8 |
+
SERVER-SIDE on every request from the DSSE-signed council receipts COMMITTED
|
| 9 |
+
to the a11oy GitHub repository (`ayllu/decisions/`). Receipt BYTES are cached
|
| 10 |
+
briefly to spare raw.githubusercontent.com; the cryptographic verification
|
| 11 |
+
itself runs on every request and is never cached or skipped.
|
| 12 |
+
|
| 13 |
+
Per decision, the band FAILS CLOSED unless ALL of:
|
| 14 |
+
- the DSSE envelope's ECDSA-P256-SHA256 signature verifies over the DSSE PAE
|
| 15 |
+
(`DSSEv1 SP len(type) SP type SP len(body) SP body`) against the PINNED
|
| 16 |
+
council runtime public key committed at `ayllu/keys/` in the same repo;
|
| 17 |
+
- the payload's `payload_digest` reproduces byte-exactly from the canonical
|
| 18 |
+
JSON (sorted keys, compact separators) of the payload `body`;
|
| 19 |
+
- the receipt's `receipt_id` equals the chain receipt id in the body.
|
| 20 |
+
|
| 21 |
+
KEY HONESTY (do not soften this):
|
| 22 |
+
The envelopes carry keyid "szlholdings-cosign", but the runtime signing key
|
| 23 |
+
in the a11oy Space does NOT match the published org key at
|
| 24 |
+
szl-holdings/.github/cosign.pub (verified 2026-07-21; the live
|
| 25 |
+
/api/a11oy/v1/verify/receipt reports the same MISMATCH). The actual
|
| 26 |
+
verifying public key was recovered from two independent live signatures
|
| 27 |
+
(ECDSA public-key recovery, unique common candidate) and is PINNED in-repo
|
| 28 |
+
with that provenance stated. Owner reconciliation of the secret vs the
|
| 29 |
+
published cosign.pub is still pending — this wall verifies against the
|
| 30 |
+
pinned RUNTIME key and says exactly that.
|
| 31 |
+
|
| 32 |
+
Optional env pin: A11OY_COUNCIL_PUB_SHA256 = sha256 hex of the pinned PEM
|
| 33 |
+
bytes. Set-but-different → keyTrust "PIN_MISMATCH" and the wall FAILS CLOSED.
|
| 34 |
+
Unset → "REPO_DECLARED". Matching → "PINNED".
|
| 35 |
+
|
| 36 |
+
Additive module per Space convention: register(app) adds routes and
|
| 37 |
+
front-moves them so exact paths win over the SPA history fallback. No new
|
| 38 |
+
dependencies (httpx + cryptography are pinned in the image).
|
| 39 |
+
"""
|
| 40 |
+
|
| 41 |
+
import base64
|
| 42 |
+
import hashlib
|
| 43 |
+
import json
|
| 44 |
+
import os
|
| 45 |
+
import time
|
| 46 |
+
from datetime import datetime, timezone
|
| 47 |
+
|
| 48 |
+
import httpx
|
| 49 |
+
from cryptography.hazmat.primitives.serialization import load_pem_public_key
|
| 50 |
+
from cryptography.hazmat.primitives.asymmetric import ec
|
| 51 |
+
from cryptography.hazmat.primitives import hashes
|
| 52 |
+
|
| 53 |
+
_API_ROUTE = "/api/ayllu/wall"
|
| 54 |
+
_PAGE_ROUTE = "/ayllu/wall"
|
| 55 |
+
_RAW = "https://raw.githubusercontent.com/szl-holdings/a11oy/main"
|
| 56 |
+
_INDEX_PATH = "ayllu/decisions/index.json"
|
| 57 |
+
_KEY_PATH = "ayllu/keys/council-runtime-2026-07-21.pub"
|
| 58 |
+
_PIN_ENV = "A11OY_COUNCIL_PUB_SHA256"
|
| 59 |
+
_CACHE_TTL_SECONDS = 300 # bytes only; verification always re-runs
|
| 60 |
+
_MAX_DECISIONS = 24
|
| 61 |
+
|
| 62 |
+
# path -> {"at": epoch, "data": bytes}
|
| 63 |
+
_byte_cache: dict = {}
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def _now_iso() -> str:
|
| 67 |
+
return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def _sha256_hex(data: bytes) -> str:
|
| 71 |
+
return hashlib.sha256(data).hexdigest()
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def _canonical(obj) -> bytes:
|
| 75 |
+
return json.dumps(obj, sort_keys=True, separators=(",", ":"),
|
| 76 |
+
ensure_ascii=False).encode("utf-8")
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def _pae(payload_type: str, body: bytes) -> bytes:
|
| 80 |
+
t = payload_type.encode("utf-8")
|
| 81 |
+
return b"DSSEv1 %d %s %d %s" % (len(t), t, len(body), body)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
async def _fetch(client: httpx.AsyncClient, path: str) -> bytes:
|
| 85 |
+
cached = _byte_cache.get(path)
|
| 86 |
+
if cached and (time.time() - cached["at"]) < _CACHE_TTL_SECONDS:
|
| 87 |
+
return cached["data"]
|
| 88 |
+
resp = await client.get(f"{_RAW}/{path}")
|
| 89 |
+
resp.raise_for_status()
|
| 90 |
+
_byte_cache[path] = {"at": time.time(), "data": resp.content}
|
| 91 |
+
return resp.content
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def _runtime_key_state(pinned_pem: bytes) -> dict:
|
| 95 |
+
"""Honest, guarded check: does the CURRENT process signing secret match the
|
| 96 |
+
pinned public key? Never raises; never exposes private material."""
|
| 97 |
+
try:
|
| 98 |
+
import szl_dsse as _dsse # type: ignore
|
| 99 |
+
priv = _dsse._load_private_key()
|
| 100 |
+
if priv is None:
|
| 101 |
+
return {"state": "RUNTIME_SIGNER_ABSENT",
|
| 102 |
+
"note": "no signing secret in this runtime; new decisions would be honestly UNSIGNED"}
|
| 103 |
+
from cryptography.hazmat.primitives.serialization import (
|
| 104 |
+
Encoding, PublicFormat)
|
| 105 |
+
cur = priv.public_key().public_bytes(
|
| 106 |
+
Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)
|
| 107 |
+
if cur.strip() == pinned_pem.strip():
|
| 108 |
+
return {"state": "RUNTIME_MATCHES_PINNED",
|
| 109 |
+
"note": "current runtime signing key equals the pinned council key"}
|
| 110 |
+
return {"state": "RUNTIME_KEY_ROTATED",
|
| 111 |
+
"note": "current runtime signing key differs from the pinned council key; "
|
| 112 |
+
"new decisions will not verify against this pin until re-pinned"}
|
| 113 |
+
except Exception as exc:
|
| 114 |
+
return {"state": "UNKNOWN", "note": f"{type(exc).__name__}: could not compare"}
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def _verify_decision(doc: dict, public_key, pinned_fpr: str) -> dict:
|
| 118 |
+
"""All checks for one committed council decision. Fail-closed."""
|
| 119 |
+
checks = {}
|
| 120 |
+
env = doc.get("receipt") or {}
|
| 121 |
+
payload_b64 = env.get("payload") or ""
|
| 122 |
+
payload_type = env.get("payloadType") or ""
|
| 123 |
+
sigs = env.get("signatures") or []
|
| 124 |
+
payload_bytes = b""
|
| 125 |
+
payload = {}
|
| 126 |
+
try:
|
| 127 |
+
payload_bytes = base64.b64decode(payload_b64, validate=True)
|
| 128 |
+
payload = json.loads(payload_bytes)
|
| 129 |
+
checks["payloadDecodes"] = True
|
| 130 |
+
except Exception:
|
| 131 |
+
checks["payloadDecodes"] = False
|
| 132 |
+
sig_ok = False
|
| 133 |
+
if checks.get("payloadDecodes") and sigs:
|
| 134 |
+
try:
|
| 135 |
+
public_key.verify(
|
| 136 |
+
base64.b64decode(sigs[0].get("sig", ""), validate=True),
|
| 137 |
+
_pae(payload_type, payload_bytes),
|
| 138 |
+
ec.ECDSA(hashes.SHA256()),
|
| 139 |
+
)
|
| 140 |
+
sig_ok = True
|
| 141 |
+
except Exception:
|
| 142 |
+
sig_ok = False
|
| 143 |
+
checks["ecdsaSignatureVerifiesOverPae"] = sig_ok
|
| 144 |
+
body = payload.get("body") if isinstance(payload, dict) else None
|
| 145 |
+
checks["payloadDigestReproduces"] = (
|
| 146 |
+
isinstance(body, dict)
|
| 147 |
+
and payload.get("payload_digest") == _sha256_hex(_canonical(body))
|
| 148 |
+
)
|
| 149 |
+
chain = (body or {}).get("chain") or {}
|
| 150 |
+
checks["receiptIdMatchesChain"] = (
|
| 151 |
+
payload.get("receipt_id") is not None
|
| 152 |
+
and payload.get("receipt_id") == chain.get("receipt_id")
|
| 153 |
+
)
|
| 154 |
+
verified = all(checks.values())
|
| 155 |
+
return {
|
| 156 |
+
"verified": verified,
|
| 157 |
+
"status": "DECISION_RECEIPT_VERIFIED" if verified else "DECISION_RECEIPT_FAILED",
|
| 158 |
+
"checks": checks,
|
| 159 |
+
"keyid_label": (sigs[0].get("keyid") if sigs else None),
|
| 160 |
+
"verifyingKeyFingerprintSha256": pinned_fpr,
|
| 161 |
+
"signedAt": env.get("_signed_at"),
|
| 162 |
+
"derived": {
|
| 163 |
+
# DERIVED from the verified payload — never hand-typed.
|
| 164 |
+
"councilId": (body or {}).get("council_id"),
|
| 165 |
+
"receiptId": payload.get("receipt_id") if isinstance(payload, dict) else None,
|
| 166 |
+
"participants": (body or {}).get("participants"),
|
| 167 |
+
"mode": (body or {}).get("mode"),
|
| 168 |
+
"models": (body or {}).get("models"),
|
| 169 |
+
"decisionState": (body or {}).get("decision_state"),
|
| 170 |
+
"evidenceState": (body or {}).get("evidence_state"),
|
| 171 |
+
"promptSha256": (body or {}).get("prompt_sha256"),
|
| 172 |
+
"humanCheckpoint": (body or {}).get("human_checkpoint"),
|
| 173 |
+
"chainSeq": chain.get("seq"),
|
| 174 |
+
} if verified else None,
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
async def _wall_payload() -> dict:
|
| 179 |
+
decisions = []
|
| 180 |
+
key_meta: dict = {}
|
| 181 |
+
error = None
|
| 182 |
+
try:
|
| 183 |
+
async with httpx.AsyncClient(timeout=10.0, follow_redirects=True) as client:
|
| 184 |
+
pem = await _fetch(client, _KEY_PATH)
|
| 185 |
+
pinned_fpr = _sha256_hex(pem)
|
| 186 |
+
pin = os.environ.get(_PIN_ENV, "").strip()
|
| 187 |
+
if not pin:
|
| 188 |
+
key_trust, pin_ok = "REPO_DECLARED", True
|
| 189 |
+
elif pin.lower() == pinned_fpr:
|
| 190 |
+
key_trust, pin_ok = "PINNED", True
|
| 191 |
+
else:
|
| 192 |
+
key_trust, pin_ok = "PIN_MISMATCH", False # fail CLOSED
|
| 193 |
+
public_key = load_pem_public_key(pem)
|
| 194 |
+
key_meta = {
|
| 195 |
+
"keyPath": _KEY_PATH,
|
| 196 |
+
"fingerprintSha256": pinned_fpr,
|
| 197 |
+
"keyTrust": key_trust,
|
| 198 |
+
"pinEnv": _PIN_ENV,
|
| 199 |
+
"provenance": (
|
| 200 |
+
"recovered from two independent live council signatures "
|
| 201 |
+
"(ECDSA public-key recovery, unique common candidate, "
|
| 202 |
+
"2026-07-21); does NOT match the published org "
|
| 203 |
+
"szl-holdings/.github cosign.pub — owner reconciliation "
|
| 204 |
+
"pending; envelopes' keyid label 'szlholdings-cosign' is a "
|
| 205 |
+
"LABEL, not proof of that published key"
|
| 206 |
+
),
|
| 207 |
+
"runtime": _runtime_key_state(pem),
|
| 208 |
+
}
|
| 209 |
+
index = json.loads(await _fetch(client, _INDEX_PATH))
|
| 210 |
+
files = list(index.get("decisions", []))[:_MAX_DECISIONS]
|
| 211 |
+
for name in files:
|
| 212 |
+
try:
|
| 213 |
+
raw = await _fetch(client, f"ayllu/decisions/{name}")
|
| 214 |
+
doc = json.loads(raw)
|
| 215 |
+
band = _verify_decision(doc, public_key, pinned_fpr)
|
| 216 |
+
if not pin_ok:
|
| 217 |
+
band["verified"] = False
|
| 218 |
+
band["status"] = "PIN_MISMATCH_FAILED_CLOSED"
|
| 219 |
+
band["file"] = name
|
| 220 |
+
band["fileSha256"] = _sha256_hex(raw)
|
| 221 |
+
decisions.append(band)
|
| 222 |
+
except Exception as one_err:
|
| 223 |
+
decisions.append({
|
| 224 |
+
"file": name, "verified": False,
|
| 225 |
+
"status": "UNAVAILABLE",
|
| 226 |
+
"error": f"{type(one_err).__name__}: {one_err}",
|
| 227 |
+
})
|
| 228 |
+
except Exception as exc: # loud, honest, fail-closed
|
| 229 |
+
error = f"{type(exc).__name__}: {exc}"
|
| 230 |
+
ok = bool(decisions) and all(d.get("verified") is True for d in decisions) and error is None
|
| 231 |
+
return {
|
| 232 |
+
"ok": ok,
|
| 233 |
+
"wall": "ayllu-council",
|
| 234 |
+
"servedFrom": "a-11-oy.com (a11oy flagship Space)",
|
| 235 |
+
"source": {
|
| 236 |
+
"repo": "szl-holdings/a11oy",
|
| 237 |
+
"decisionsPath": "ayllu/decisions/",
|
| 238 |
+
"note": "decisions fetched from the committed repo files; "
|
| 239 |
+
"bytes cached briefly; verification never cached",
|
| 240 |
+
},
|
| 241 |
+
"verifier": {
|
| 242 |
+
"mode": "ECDSA-P256-SHA256 over DSSE PAE via cryptography, server-side",
|
| 243 |
+
"perRequest": True,
|
| 244 |
+
"offline": "scripts/verify_ayllu_council_receipt.py (same repo) proves the "
|
| 245 |
+
"chain with no trust in this server",
|
| 246 |
+
},
|
| 247 |
+
"scope": {
|
| 248 |
+
"whatThisIs": (
|
| 249 |
+
"cryptographic proof that these committed council deliberation "
|
| 250 |
+
"receipts were signed by the pinned a11oy runtime key and were "
|
| 251 |
+
"not altered since"
|
| 252 |
+
),
|
| 253 |
+
"whatThisIsNot": (
|
| 254 |
+
"NOT proof of decision quality, consensus, model identity, or "
|
| 255 |
+
"autonomous authority — the council PROPOSES only (zero "
|
| 256 |
+
"effectors, tool_dispatch=false); every decision requires the "
|
| 257 |
+
"human checkpoint it declares"
|
| 258 |
+
),
|
| 259 |
+
},
|
| 260 |
+
"keyHonesty": key_meta,
|
| 261 |
+
"error": error,
|
| 262 |
+
"generatedAt": _now_iso(),
|
| 263 |
+
"count": len(decisions),
|
| 264 |
+
"decisions": decisions,
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
async def _api_handler():
|
| 269 |
+
return await _wall_payload()
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
_PAGE = """<!doctype html><html lang="en"><head><meta charset="utf-8">
|
| 273 |
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
| 274 |
+
<title>AYLLU council wall — verify-then-display</title>
|
| 275 |
+
<style>
|
| 276 |
+
:root{--void:#080c14;--panel:#0d1520;--line:#16202c;--teal:#3af4c8;--fg:#dfe7ee;--dim:#7f93a6;--gold:#d4a444;--bad:#ff5d73}
|
| 277 |
+
*{box-sizing:border-box}body{margin:0;background:var(--void);color:var(--fg);font:15px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace}
|
| 278 |
+
main{max-width:960px;margin:0 auto;padding:32px 20px 64px}
|
| 279 |
+
h1{font-size:20px;color:var(--teal);letter-spacing:.04em;margin:0 0 4px}
|
| 280 |
+
.sub{color:var(--dim);font-size:13px;margin-bottom:20px}
|
| 281 |
+
.scope{border:1px solid var(--gold);border-radius:8px;padding:12px 16px;margin:18px 0;background:rgba(212,164,68,.06);font-size:13px}
|
| 282 |
+
.scope b{color:var(--gold)}
|
| 283 |
+
.card{border:1px solid var(--line);border-radius:8px;background:var(--panel);padding:14px 16px;margin:12px 0}
|
| 284 |
+
.ok{color:var(--teal)}.fail{color:var(--bad)}
|
| 285 |
+
.kv{color:var(--dim);font-size:12.5px}.kv code{color:var(--fg)}
|
| 286 |
+
.badge{display:inline-block;border:1px solid;border-radius:4px;padding:1px 8px;font-size:12px;margin-right:8px}
|
| 287 |
+
.badge.ok{border-color:var(--teal)}.badge.fail{border-color:var(--bad)}
|
| 288 |
+
.keybox{border:1px dashed var(--line);border-radius:8px;padding:10px 14px;margin:16px 0;font-size:12.5px;color:var(--dim)}
|
| 289 |
+
a{color:var(--teal)}
|
| 290 |
+
</style></head><body><main>
|
| 291 |
+
<h1>AYLLU COUNCIL — SIGNED DECISIONS</h1>
|
| 292 |
+
<div class="sub">every band below is re-verified server-side on this request · fail-closed · <a href="/api/ayllu/wall">raw JSON</a> · <a href="/ayllu">council console</a></div>
|
| 293 |
+
<div class="scope" id="scope"><b>WHAT THIS IS / WHAT THIS IS NOT</b><div id="scopebody">loading…</div></div>
|
| 294 |
+
<div class="keybox" id="keybox">key: loading…</div>
|
| 295 |
+
<div id="bands">verifying…</div>
|
| 296 |
+
<script>
|
| 297 |
+
fetch('/api/ayllu/wall').then(r=>r.json()).then(d=>{
|
| 298 |
+
const esc=s=>String(s??'').replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c]));
|
| 299 |
+
document.getElementById('scopebody').innerHTML =
|
| 300 |
+
'<div style="margin-top:6px"><span class="ok">IS:</span> '+esc(d.scope?.whatThisIs)+'</div>'+
|
| 301 |
+
'<div style="margin-top:4px"><span class="fail">IS NOT:</span> '+esc(d.scope?.whatThisIsNot)+'</div>';
|
| 302 |
+
const k=d.keyHonesty||{};
|
| 303 |
+
document.getElementById('keybox').innerHTML =
|
| 304 |
+
'verifying key <code>'+esc((k.fingerprintSha256||'').slice(0,16))+'…</code> · trust '+esc(k.keyTrust||'?')+
|
| 305 |
+
' · runtime '+esc(k.runtime?.state||'?')+'<br>'+esc(k.provenance||'');
|
| 306 |
+
const el=document.getElementById('bands');
|
| 307 |
+
if(d.error){el.innerHTML='<div class="card fail">WALL FAILED CLOSED — '+esc(d.error)+'</div>';return;}
|
| 308 |
+
if(!(d.decisions||[]).length){el.innerHTML='<div class="card fail">no committed decisions found — failing closed</div>';return;}
|
| 309 |
+
el.innerHTML=d.decisions.map(b=>{
|
| 310 |
+
const v=b.verified===true, dv=b.derived||{};
|
| 311 |
+
return '<div class="card">'
|
| 312 |
+
+'<span class="badge '+(v?'ok':'fail')+'">'+(v?'VERIFIED':'FAILED')+'</span>'
|
| 313 |
+
+'<code>'+esc(b.file)+'</code>'
|
| 314 |
+
+(v?'<div class="kv" style="margin-top:8px">council <code>'+esc(String(dv.councilId||'').slice(0,8))+'</code>'
|
| 315 |
+
+' · receipt <code>'+esc(String(dv.receiptId||'').slice(0,12))+'…</code>'
|
| 316 |
+
+' · personas <code>'+esc((dv.participants||[]).join(', '))+'</code>'
|
| 317 |
+
+' · decision <code>'+esc(dv.decisionState)+'</code>'
|
| 318 |
+
+' · human checkpoint <code>'+esc(dv.humanCheckpoint?.required===true?'REQUIRED':JSON.stringify(dv.humanCheckpoint).slice(0,40))+'</code>'
|
| 319 |
+
+'</div>'
|
| 320 |
+
:'<div class="kv fail" style="margin-top:8px">'+esc(b.status)+(b.error?' — '+esc(b.error):'')+'</div>')
|
| 321 |
+
+'</div>';
|
| 322 |
+
}).join('');
|
| 323 |
+
}).catch(e=>{document.getElementById('bands').innerHTML='<div class="card fail">fetch failed — '+String(e)+'</div>';});
|
| 324 |
+
</script>
|
| 325 |
+
</main></body></html>"""
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
async def _page_handler():
|
| 329 |
+
from fastapi.responses import HTMLResponse
|
| 330 |
+
return HTMLResponse(_PAGE)
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
def register(app) -> str:
|
| 334 |
+
"""Additive registration + front-move (exact routes must beat SPA fallback)."""
|
| 335 |
+
app.add_api_route(_API_ROUTE, _api_handler, methods=["GET"], include_in_schema=False)
|
| 336 |
+
app.add_api_route(_PAGE_ROUTE, _page_handler, methods=["GET"], include_in_schema=False)
|
| 337 |
+
for target in (_API_ROUTE, _PAGE_ROUTE):
|
| 338 |
+
for index, route in enumerate(app.router.routes):
|
| 339 |
+
if getattr(route, "path", None) == target:
|
| 340 |
+
app.router.routes.insert(0, app.router.routes.pop(index))
|
| 341 |
+
break
|
| 342 |
+
return f"{_API_ROUTE} + {_PAGE_ROUTE} (verify-then-display, fail-closed)"
|
serve.py
CHANGED
|
@@ -14102,6 +14102,29 @@ except Exception as _ecosystem_atlas_error:
|
|
| 14102 |
)
|
| 14103 |
|
| 14104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14105 |
if __name__ == "__main__":
|
| 14106 |
import uvicorn
|
| 14107 |
port = int(os.environ.get("PORT", "7860"))
|
|
|
|
| 14102 |
)
|
| 14103 |
|
| 14104 |
|
| 14105 |
+
|
| 14106 |
+
# ============================================================================
|
| 14107 |
+
# AYLLU COUNCIL WALL (2026-07-21): /api/ayllu/wall + /ayllu/wall — the public
|
| 14108 |
+
# verify-then-display surface for committed council decision receipts.
|
| 14109 |
+
# Fetches ayllu/decisions/* from the GitHub repo, re-verifies each DSSE
|
| 14110 |
+
# envelope (ECDSA-P256 over the PAE) against the pinned council runtime key
|
| 14111 |
+
# (ayllu/keys/) SERVER-SIDE ON EVERY REQUEST, and fails closed. Key honesty is
|
| 14112 |
+
# stated in-band: the runtime key does NOT match the published org cosign.pub
|
| 14113 |
+
# (owner reconciliation pending) — see ayllu/keys/README.md. Additive,
|
| 14114 |
+
# try/except-guarded, front-moved so exact routes beat the SPA fallback.
|
| 14115 |
+
# ============================================================================
|
| 14116 |
+
try:
|
| 14117 |
+
import a11oy_ayllu_wall as _a11oy_ayllu_wall
|
| 14118 |
+
|
| 14119 |
+
_ayllu_wall_result = _a11oy_ayllu_wall.register(app)
|
| 14120 |
+
print(f"[a11oy] ayllu council wall registered (front-moved): {_ayllu_wall_result}", file=sys.stderr)
|
| 14121 |
+
except Exception as _ayllu_wall_error: # additive: never take down the SPA
|
| 14122 |
+
print(
|
| 14123 |
+
f"[a11oy] ayllu council wall NOT registered (non-fatal): {_ayllu_wall_error!r}",
|
| 14124 |
+
file=sys.stderr,
|
| 14125 |
+
)
|
| 14126 |
+
|
| 14127 |
+
|
| 14128 |
if __name__ == "__main__":
|
| 14129 |
import uvicorn
|
| 14130 |
port = int(os.environ.get("PORT", "7860"))
|