File size: 23,220 Bytes
6741fc6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
#!/usr/bin/env python3
"""Pre-flight the deployed stack, the way `make audit` pre-flights the source.

`make audit` proves 142 tests against a local PostgreSQL. Nothing proved anything
against the deployment, so every hosted failure so far was found by opening the
page and looking. Two of the worst leave no trace in the API's logs at all,
because in both cases no request ever reaches the API: a UI redirecting every
visitor to a login page, and a `connect-src` baked at build time that still names
the previous API host. Both look exactly like an outage from the outside.

Run it before a demo. It also warms the Space, which sleeps after 48 hours idle,
and the Neon compute behind it, which suspends sooner — so the first document of
the call is not the slow one.

    make verify-hosted

Standard library only, deliberately: this checks a deployment, and should not
itself depend on a working install.

## On cleaning up after itself

It does not, and cannot. `audit_log` is append-only, enforced by a database
trigger that raises on `UPDATE` and `DELETE` — including the cascade from
`documents`. That is the product's central claim, not an oversight, so a hosted
check is not given a back door through it.

What replaces deletion is idempotency. The document uploaded here is pinned: the
same bytes every run, so `UNIQUE(file_hash)` means the first run creates one row
and every run afterwards is told `duplicate` and creates nothing. The ledger
gains one permanent, clearly-named record and never grows again — and the second
run onwards turns the clean-up step into a live proof of the idempotency
guarantee, which is a better check than the one it replaces.

`--fresh` makes the bytes unique so the whole pipeline genuinely re-runs. That
adds a permanent row each time, which is why it is not the default.
"""

from __future__ import annotations

import argparse
import json
import sys
import time
import urllib.error
import urllib.request
from pathlib import Path
from typing import Any, Final

REPO_ROOT: Final = Path(__file__).resolve().parents[1]

UI_DEFAULT: Final = "https://ledgerlens-jet.vercel.app"
API_DEFAULT: Final = "https://Abdr007-ledgerlens.hf.space"

#: A real degraded scan from the labelled evaluation set — a phone photo of an
#: invoice, committed to the repository, so this runs from a fresh clone.
SAMPLE: Final = REPO_ROOT / "eval" / "testset" / "EV-CE-002-scan.jpg"
SAMPLE_NAME: Final = "hosted-verification.jpg"

# A cold Space wakes in ~30 s, and the Neon compute behind it can take another
# few seconds to resume its first connection.
TIMEOUT_S: Final = 120
#: How long to wait for one document to reach a terminal state.
PIPELINE_DEADLINE_S: Final = 90

HTTP_OK: Final = 200
HTTP_ACCEPTED: Final = 202
HTTP_FOUND: Final = 302
HTTP_FORBIDDEN: Final = 403
HTTP_TOO_MANY_REQUESTS: Final = 429

HIGH_SEVERITIES: Final = frozenset({"HIGH", "CRITICAL"})
#: Spec §2: Ingest -> Route -> Extract -> Validate -> Screen -> Ledger.
PIPELINE_STAGES: Final = ("ingest", "route", "extract", "validate", "screen", "ledger")


class CheckError(Exception):
    """A check that did not hold. The message is what the presenter needs to read."""


def _lower_keys(headers: Any) -> dict[str, str]:
    """Header names, lower-cased.

    ``dict(response.headers)`` keeps the wire casing and loses the case-insensitive
    lookup the underlying ``Message`` provides. Over HTTP/2 the names arrive
    lower-cased anyway, so a lower-cased lookup against that dict works — and then
    silently stops working over HTTP/1.1, where Vercel sends
    ``Content-Security-Policy``.
    """
    return {str(key).lower(): str(value) for key, value in headers.items()}


def _request(
    url: str,
    *,
    method: str = "GET",
    body: bytes | None = None,
    headers: dict[str, str] | None = None,
) -> tuple[int, dict[str, str], bytes]:
    request = urllib.request.Request(url, data=body, method=method)  # noqa: S310
    for key, value in (headers or {}).items():
        request.add_header(key, value)
    try:
        with urllib.request.urlopen(request, timeout=TIMEOUT_S) as response:  # noqa: S310
            return int(response.status), _lower_keys(response.headers), response.read()
    except urllib.error.HTTPError as exc:
        return int(exc.code), _lower_keys(exc.headers), exc.read()


def _json(url: str, *, origin: str, expect: int = HTTP_OK) -> Any:
    status, _, raw = _request(url, headers={"Origin": origin})
    if status != expect:
        raise CheckError(f"{url} returned HTTP {status}, expected {expect}")
    return json.loads(raw)


# --------------------------------------------------------------------------- #
# Checks                                                                       #
# --------------------------------------------------------------------------- #


def check_ui_is_public(ui: str) -> str:
    """A 302 here means deployment protection is on and visitors hit a login page."""
    status, headers, _ = _request(ui)
    if status == HTTP_FOUND:
        location = headers.get("location", "")
        if "vercel.com" in location:
            raise CheckError(
                "the UI redirects to Vercel login — deployment protection is enabled. "
                "Project -> Settings -> Deployment Protection -> disable Vercel Authentication"
            )
        raise CheckError(f"the UI redirects to {location}")
    if status != HTTP_OK:
        raise CheckError(f"the UI returned HTTP {status}")
    return f"HTTP 200 at {ui}"


def check_csp_points_at_the_api(ui: str, api: str) -> str:
    """The CSP is baked at build time; a stale one blocks every call inside the browser."""
    _, headers, _ = _request(ui)
    csp = headers.get("content-security-policy", "")
    if not csp:
        raise CheckError("the UI sends no Content-Security-Policy")
    connect = next((part for part in csp.split(";") if part.strip().startswith("connect-src")), "")
    host = api.split("://", 1)[-1]
    if host.lower() not in connect.lower():
        raise CheckError(
            f"CSP connect-src does not name {host} — it reads '{connect.strip()}'. "
            "NEXT_PUBLIC_API_BASE_URL was not set at BUILD time; redeploy after setting it"
        )
    return f"connect-src names {host}"


def check_api_health(api: str, origin: str) -> str:
    data = _json(f"{api}/health", origin=origin)
    if data.get("status") != "ok":
        raise CheckError(
            f"health is '{data.get('status')}' with database '{data.get('database')}' — "
            "the API is up but its database is not; check DATABASE_URL on the Space"
        )
    if data.get("database") != "up":
        raise CheckError(f"database is '{data.get('database')}'")
    return (
        f"v{data.get('version')} · {data.get('environment')} · db up · "
        f"llm {data.get('llm_mode')} · langfuse {data.get('langfuse')}"
    )


def check_cors(api: str, ui: str) -> str:
    """The UI may write; an unknown origin may not — asserted on the server.

    This deliberately does not test `Access-Control-Allow-Origin`, because on this
    host that header is not ours. Hugging Face answers the pre-flight at its edge
    and echoes whatever `Origin` it was sent, so an unknown origin is told it is
    allowed while `ALLOWED_ORIGINS` says otherwise — the same image refuses
    `evil.example` when run locally (AUDIT.md §4c). A check reading that header
    would have to either fail forever against a condition nobody can fix, or be
    softened until it asserted nothing.

    So it checks the thing that is actually enforceable: `OriginGuardMiddleware`
    rejects a cross-origin *write* before any handler runs, which no upstream
    header rewriting can undo. The pre-flight is still checked from the UI's own
    origin, because that one failing would break the product.
    """
    status, _, _ = _request(
        f"{api}/v1/documents",
        method="OPTIONS",
        headers={
            "Origin": ui,
            "Access-Control-Request-Method": "POST",
            "Access-Control-Request-Headers": "content-type",
        },
    )
    if status != HTTP_OK:
        raise CheckError(f"pre-flight from {ui} returned HTTP {status}")

    body, content_type = _multipart("cross-origin-probe.jpg", b"\xff\xd8\xff\xe0 probe")
    status, _, raw = _request(
        f"{api}/v1/documents",
        method="POST",
        body=body,
        headers={"Content-Type": content_type, "Origin": "https://evil.example"},
    )
    if status != HTTP_FORBIDDEN:
        raise CheckError(
            f"a write from https://evil.example returned HTTP {status}, not 403 — "
            "OriginGuardMiddleware is not in force, and on this host the CORS "
            "headers will not stop it either"
        )
    try:
        code = json.loads(raw)["error"]["code"]
    except (json.JSONDecodeError, KeyError, TypeError):
        code = "?"
    if code != "forbidden_origin":
        raise CheckError(f"cross-origin write refused with '{code}', not 'forbidden_origin'")

    # Reported, never asserted: it is the platform's behaviour, not the app's.
    _, headers, _ = _request(
        f"{api}/v1/documents",
        method="OPTIONS",
        headers={"Origin": "https://evil.example", "Access-Control-Request-Method": "POST"},
    )
    edge = (
        "; edge echoes any Origin (platform, see AUDIT §4c)"
        if headers.get("access-control-allow-origin")
        else ""
    )
    return f"{ui} may write, evil.example gets 403 forbidden_origin{edge}"


def check_ledger_reconciles(api: str, origin: str) -> str:
    """The KPI cards and the ledger must be counting the same thing.

    Both read the same tables through different queries. They drifting apart is
    the quiet failure: a dashboard that shows a number nothing else agrees with.
    """
    stats = _json(f"{api}/v1/stats", origin=origin)
    listing = _json(f"{api}/v1/documents?limit=200", origin=origin)

    if listing["total"] != stats["documents_total"]:
        raise CheckError(
            f"stats says {stats['documents_total']} documents, the ledger says {listing['total']}"
        )
    counted: dict[str, int] = {}
    for item in listing["items"]:
        counted[item["status"]] = counted.get(item["status"], 0) + 1
    for label, key in (
        ("DONE", "documents_processed"),
        ("NEEDS_REVIEW", "documents_needs_review"),
        ("FAILED", "documents_failed"),
    ):
        if counted.get(label, 0) != stats[key]:
            raise CheckError(
                f"stats.{key}={stats[key]} but the ledger holds "
                f"{counted.get(label, 0)} {label} documents"
            )
    return (
        f"{stats['documents_total']} documents · {stats['documents_processed']} done · "
        f"{stats['documents_needs_review']} in review · {stats['anomalies_open']} open flags · "
        f"avg {stats['avg_latency_ms']:.0f} ms · p95 {stats['p95_latency_ms']:.0f} ms"
    )


def _routing_is_consistent(detail: dict[str, Any]) -> str | None:
    """The thesis, as an assertion: nothing failing verification sits in the ledger.

    Returns a description of the violation, or None when the record is consistent.
    """
    extraction = detail.get("extraction") or {}
    failed = [c["rule"] for c in extraction.get("checks", []) if not c["passed"]]
    high = [a for a in detail.get("anomalies", []) if a["severity"] in HIGH_SEVERITIES]
    status = detail["status"]

    if status == "DONE" and (failed or high):
        return (
            f"{detail['filename']} is DONE but has "
            f"{len(failed)} failed check(s) {failed} and {len(high)} high-severity flag(s)"
        )
    if status == "NEEDS_REVIEW" and not failed and not high:
        return f"{detail['filename']} is in review with nothing failing — no reason to be there"
    return None


def check_review_routing(api: str, origin: str) -> str:
    """Sweep every document in the live ledger against the product's own claim."""
    listing = _json(f"{api}/v1/documents?limit=200", origin=origin)
    violations: list[str] = []
    reviewed = 0
    for item in listing["items"]:
        detail = _json(f"{api}/v1/documents/{item['id']}", origin=origin)
        if detail["status"] not in {"DONE", "NEEDS_REVIEW"}:
            continue
        reviewed += 1
        if problem := _routing_is_consistent(detail):
            violations.append(problem)

    if violations:
        raise CheckError(f"{len(violations)} document(s) misrouted: {violations[0]}")
    return f"{reviewed} documents, every one routed by its own verification result"


def check_review_queue_is_explainable(api: str, origin: str) -> str:
    """Every flag carries a severity, a score and a reason a human can act on."""
    anomalies = _json(f"{api}/v1/anomalies?limit=100", origin=origin)
    if not anomalies:
        raise CheckError("the review queue is empty — nothing to demonstrate")
    for anomaly in anomalies:
        if not anomaly.get("reason"):
            raise CheckError(f"anomaly {anomaly['id']} carries no reason")
        if not anomaly.get("evidence"):
            raise CheckError(f"anomaly {anomaly['id']} carries no evidence")
        if anomaly.get("severity") not in {"LOW", "MEDIUM", "HIGH", "CRITICAL"}:
            raise CheckError(f"anomaly {anomaly['id']} has severity '{anomaly.get('severity')}'")
    kinds = sorted({a["anomaly_type"] for a in anomalies})
    return f"{len(anomalies)} flag(s), all with severity + evidence + reason · {kinds}"


def _multipart(name: str, payload: bytes) -> tuple[bytes, str]:
    boundary = "----ledgerlensverify0000"
    body = (
        (
            f"--{boundary}\r\n"
            f'Content-Disposition: form-data; name="file"; filename="{name}"\r\n'
            "Content-Type: image/jpeg\r\n\r\n"
        ).encode()
        + payload
        + f"\r\n--{boundary}--\r\n".encode()
    )
    return body, f"multipart/form-data; boundary={boundary}"


def _await_terminal(api: str, origin: str, document_id: str) -> dict[str, Any]:
    deadline = time.monotonic() + PIPELINE_DEADLINE_S
    state: dict[str, Any] = {}
    while time.monotonic() < deadline:
        state = _json(f"{api}/v1/documents/{document_id}/status", origin=origin)
        if state.get("is_terminal"):
            return state
        time.sleep(1)
    raise CheckError(
        f"the document was still '{state.get('status')}' after {PIPELINE_DEADLINE_S}s "
        f"(progress {state.get('progress')})"
    )


def check_round_trip(api: str, origin: str, *, fresh: bool) -> str:
    """Upload a real document and verify what came back out.

    On the first run this drives the whole pipeline. Afterwards the same bytes
    are recognised and no work is repeated — which is the guarantee, so it is
    asserted rather than worked around. See the module docstring.
    """
    if not SAMPLE.exists():
        raise CheckError(f"{SAMPLE} is missing from the checkout")

    payload = SAMPLE.read_bytes()
    name = SAMPLE_NAME
    if fresh:
        # Trailing bytes after the JPEG end-of-image marker change the SHA-256
        # without changing what any decoder reads.
        marker = str(time.time_ns()).encode()
        payload = payload + b"\xff\xfe" + len(marker).to_bytes(2, "big") + marker
        name = f"hosted-verification-{time.time_ns()}.jpg"

    body, content_type = _multipart(name, payload)
    status, _, raw = _request(
        f"{api}/v1/documents",
        method="POST",
        body=body,
        headers={"Content-Type": content_type, "Origin": origin},
    )
    if status != HTTP_ACCEPTED:
        raise CheckError(f"upload returned HTTP {status}: {raw[:300].decode('utf-8', 'replace')}")
    upload = json.loads(raw)

    state = _await_terminal(api, origin, upload["document_id"])
    detail = _json(f"{api}/v1/documents/{upload['document_id']}", origin=origin)

    if problem := _routing_is_consistent(detail):
        raise CheckError(problem)

    seen = {entry["stage"] for entry in detail.get("audit", []) if entry.get("stage")}
    missing = [stage for stage in PIPELINE_STAGES if stage not in seen]
    if missing:
        raise CheckError(f"the audit trail is missing stage(s) {missing} — it should span all six")

    extraction = detail.get("extraction") or {}
    checks = extraction.get("checks", [])
    if not checks:
        raise CheckError("no validation report was stored — nothing verified this document")

    passed = sum(1 for c in checks if c["passed"])
    origin_word = "deduplicated" if upload["duplicate"] else "processed"
    return (
        f"{origin_word} · {detail['status']} · lane {detail.get('lane')} · "
        f"{passed}/{len(checks)} checks passed · {len(seen)}/6 stages audited · "
        f"{state.get('latency_ms')} ms"
    )


def check_idempotency(api: str, origin: str) -> str:
    """The same bytes a second time must not create a second record."""
    before = _json(f"{api}/v1/stats", origin=origin)["documents_total"]

    body, content_type = _multipart(SAMPLE_NAME, SAMPLE.read_bytes())
    status, _, raw = _request(
        f"{api}/v1/documents",
        method="POST",
        body=body,
        headers={"Content-Type": content_type, "Origin": origin},
    )
    if status != HTTP_ACCEPTED:
        raise CheckError(f"re-upload returned HTTP {status}")
    upload = json.loads(raw)
    if not upload["duplicate"]:
        raise CheckError("identical bytes were not recognised as a duplicate")

    after = _json(f"{api}/v1/stats", origin=origin)["documents_total"]
    if after != before:
        raise CheckError(f"the ledger grew from {before} to {after} on a duplicate upload")
    return f"same SHA-256 -> duplicate, ledger still {after} documents"


def check_errors_are_typed(api: str, origin: str) -> str:
    """A rejected upload returns the error envelope, never a stack trace."""
    body, content_type = _multipart("payload.exe", b"MZ\x90\x00 this is not a document")
    status, _, raw = _request(
        f"{api}/v1/documents",
        method="POST",
        body=body,
        headers={"Content-Type": content_type, "Origin": origin},
    )
    text = raw.decode("utf-8", "replace")
    if "Traceback" in text or 'File "' in text:
        raise CheckError("a rejected upload leaked a stack trace")
    if status == HTTP_ACCEPTED:
        raise CheckError("an executable was accepted as a document")
    try:
        code = json.loads(text)["error"]["code"]
    except (json.JSONDecodeError, KeyError, TypeError) as exc:
        raise CheckError(f"HTTP {status} was not the error envelope: {text[:160]}") from exc
    return f"HTTP {status} {code}, no stack trace"


def check_rate_limit_binds(api: str, origin: str) -> str:
    """The ingestion limit must actually stop someone, not merely be configured.

    Production reported `X-RateLimit-Limit: 10` on every upload and never returned
    429, because `TRUSTED_PROXY_COUNT` was one less than the host's real proxy
    depth: the limiter keyed on the address of whichever edge node answered, so
    callers were spread across a handful of buckets and none of them filled. A
    check that read the headers would have called that a pass.

    Rejected uploads are used deliberately. The limiter decorator runs before the
    handler, so a 415 consumes budget while writing nothing — the limit can be
    driven to its ceiling without adding a single row to an append-only ledger.
    """
    boundary = "----ledgerlensratelimit"
    body = (
        (
            f"--{boundary}\r\n"
            'Content-Disposition: form-data; name="file"; filename="rate-limit-probe.exe"\r\n'
            "Content-Type: application/octet-stream\r\n\r\n"
        ).encode()
        + b"MZ\x90\x00 deliberately not a document"
        + f"\r\n--{boundary}--\r\n".encode()
    )
    headers = {"Content-Type": f"multipart/form-data; boundary={boundary}", "Origin": origin}

    limit: str | None = None
    attempts = 0
    # One more than the ceiling this project configures, with headroom for a
    # budget already partly spent by the round-trip checks above.
    for attempts in range(1, 26):
        status, response_headers, _ = _request(
            f"{api}/v1/documents", method="POST", body=body, headers=headers
        )
        limit = response_headers.get("x-ratelimit-limit", limit)
        if status == HTTP_TOO_MANY_REQUESTS:
            return f"429 after {attempts} rejected uploads (limit {limit}), 0 ledger writes"
        if status == HTTP_ACCEPTED:
            raise CheckError("an executable was accepted as a document")

    raise CheckError(
        f"{attempts} uploads in seconds and none was refused, while the API reported "
        f"X-RateLimit-Limit: {limit}. The limit is counted but does not bind — "
        "TRUSTED_PROXY_COUNT is almost certainly lower than the host's real proxy "
        "depth, so the key is a proxy address rather than the caller. The API logs a "
        "`proxy_depth_mismatch` warning naming the value to set."
    )


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--ui", default=UI_DEFAULT)
    parser.add_argument("--api", default=API_DEFAULT)
    parser.add_argument(
        "--fresh",
        action="store_true",
        help="force a full pipeline run with unique bytes (adds a permanent ledger row)",
    )
    args = parser.parse_args(argv)
    ui, api = args.ui.rstrip("/"), args.api.rstrip("/")

    checks: list[tuple[str, Any]] = [
        ("UI is public", lambda: check_ui_is_public(ui)),
        ("CSP points at the API", lambda: check_csp_points_at_the_api(ui, api)),
        ("API health", lambda: check_api_health(api, ui)),
        ("cross-origin writes refused", lambda: check_cors(api, ui)),
        ("ledger reconciles", lambda: check_ledger_reconciles(api, ui)),
        ("nothing invalid committed", lambda: check_review_routing(api, ui)),
        ("review queue explains itself", lambda: check_review_queue_is_explainable(api, ui)),
        ("upload · extract · validate", lambda: check_round_trip(api, ui, fresh=args.fresh)),
        ("re-upload is idempotent", lambda: check_idempotency(api, ui)),
        ("rejections are typed", lambda: check_errors_are_typed(api, ui)),
        ("rate limit binds", lambda: check_rate_limit_binds(api, ui)),
    ]

    print(f"\n  UI   {ui}\n  API  {api}\n")
    failures: list[str] = []
    for name, run in checks:
        started = time.perf_counter()
        try:
            detail = run()
            print(f"  PASS  {name:30} {detail}  [{time.perf_counter() - started:.1f}s]")
        except CheckError as exc:
            failures.append(f"{name}: {exc}")
            print(f"  FAIL  {name:30} {exc}")
        except (urllib.error.URLError, TimeoutError, json.JSONDecodeError, KeyError) as exc:
            failures.append(f"{name}: {exc}")
            print(f"  FAIL  {name:30} unreachable: {exc}")

    if failures:
        print(f"\n  {len(failures)} of {len(checks)} checks failed — do not present\n")
        return 1
    print(f"\n  all {len(checks)} checks passed · the Space is warm · ready to present\n")
    return 0


if __name__ == "__main__":
    sys.exit(main())