deploy: exact szl-forge source 53e8b0f1a8bb
Browse filesGit-controlled Space subtree publication with post-commit byte and runtime verification.
Signed-off-by: SZL Holdings <noreply@szlholdings.ai>
- README.md +14 -2
- app.py +160 -23
- release.json +5 -5
- tests/test_app.py +83 -2
- verify_execution_record.py +1 -1
README.md
CHANGED
|
@@ -54,6 +54,13 @@ non-secret `SZL_GITHUB_SOURCE_REVISION` Space variable and verifies it at
|
|
| 54 |
That endpoint reports `UNKNOWN` rather than inferring a source revision when
|
| 55 |
the binding is absent or malformed.
|
| 56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
An isolated image-build stage fetches only the exact GGUF and three receipt
|
| 58 |
files from the immutable model revision, without a token, and verifies them
|
| 59 |
before the image can finish. It copies only verified regular bytes into the
|
|
@@ -80,8 +87,13 @@ It requires no provider token or Space secret and is intended for the Hub's free
|
|
| 80 |
tokens, and a 45-second best-effort cutoff checked between streamed chunks
|
| 81 |
(not a hard wall-clock deadline).
|
| 82 |
- Greedy decoding (`temperature=0`); outputs are model-generated and may be wrong.
|
| 83 |
-
- `/live`
|
| 84 |
-
readiness and
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
- `/api/v1/identity` exposes the immutable artifact, runtime limits, source release
|
| 86 |
marker, and receipt boundary. Source checksums establish internal bundle
|
| 87 |
consistency only; they are not external authorship evidence.
|
|
|
|
| 54 |
That endpoint reports `UNKNOWN` rather than inferring a source revision when
|
| 55 |
the binding is absent or malformed.
|
| 56 |
|
| 57 |
+
The human-facing surface is the **Khipu Loom**: a responsive Formula Genome
|
| 58 |
+
instrument that keeps the source thread, immutable model pin, receipt boundary,
|
| 59 |
+
runtime state, and unsigned-output limitation visible beside the bounded
|
| 60 |
+
inference controls. It uses no external scripts, fonts, trackers, or UI assets,
|
| 61 |
+
and exposes a deterministic `data-screenshot-ready` signal only after the
|
| 62 |
+
runtime reaches `READY`.
|
| 63 |
+
|
| 64 |
An isolated image-build stage fetches only the exact GGUF and three receipt
|
| 65 |
files from the immutable model revision, without a token, and verifies them
|
| 66 |
before the image can finish. It copies only verified regular bytes into the
|
|
|
|
| 87 |
tokens, and a 45-second best-effort cutoff checked between streamed chunks
|
| 88 |
(not a hard wall-clock deadline).
|
| 89 |
- Greedy decoding (`temperature=0`); outputs are model-generated and may be wrong.
|
| 90 |
+
- `/live` and `/healthz` are liveness (`STARTING`/`READY` = 200; `FAILED` =
|
| 91 |
+
503); `/health` and `/readyz` are readiness and return 503 until `READY`.
|
| 92 |
+
- `/version` fails closed unless the governed deployment provides one exact
|
| 93 |
+
40-character source revision. `/evidence` fails closed unless that exact
|
| 94 |
+
source identity, source-bundle integrity, and both declared-key receipts are
|
| 95 |
+
simultaneously available. Neither endpoint upgrades unsigned runtime output
|
| 96 |
+
into an attestation.
|
| 97 |
- `/api/v1/identity` exposes the immutable artifact, runtime limits, source release
|
| 98 |
marker, and receipt boundary. Source checksums establish internal bundle
|
| 99 |
consistency only; they are not external authorship evidence.
|
app.py
CHANGED
|
@@ -64,6 +64,8 @@ SYSTEM_PROMPT = (
|
|
| 64 |
SOURCE_ROOT = Path(__file__).resolve().parent
|
| 65 |
SOURCE_REVISION_ENV = "SZL_GITHUB_SOURCE_REVISION"
|
| 66 |
ARTIFACT_ROOT = Path("/opt/szl/model-artifacts")
|
|
|
|
|
|
|
| 67 |
|
| 68 |
|
| 69 |
state: dict[str, Any] = {
|
|
@@ -622,6 +624,22 @@ def identity_payload() -> dict[str, Any]:
|
|
| 622 |
}
|
| 623 |
|
| 624 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 625 |
def build_info_payload() -> dict[str, Any]:
|
| 626 |
"""Expose the exact Git source bound by the governed deploy workflow.
|
| 627 |
|
|
@@ -630,17 +648,14 @@ def build_info_payload() -> dict[str, Any]:
|
|
| 630 |
the internal release manifest.
|
| 631 |
"""
|
| 632 |
|
| 633 |
-
|
| 634 |
-
revision_observed =
|
| 635 |
-
len(raw_revision) == 40
|
| 636 |
-
and all(character in "0123456789abcdef" for character in raw_revision)
|
| 637 |
-
)
|
| 638 |
return {
|
| 639 |
"schema": "szl.build-info/v1",
|
| 640 |
-
"service":
|
| 641 |
"build": {
|
| 642 |
"state": "OBSERVED" if revision_observed else "UNKNOWN",
|
| 643 |
-
"revision":
|
| 644 |
"revision_source": (
|
| 645 |
f"Hugging Face Space variable {SOURCE_REVISION_ENV}"
|
| 646 |
if revision_observed
|
|
@@ -659,6 +674,77 @@ def build_info_payload() -> dict[str, Any]:
|
|
| 659 |
}
|
| 660 |
|
| 661 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 662 |
def canonical_json_bytes(payload: dict[str, Any]) -> bytes:
|
| 663 |
return json.dumps(
|
| 664 |
payload, ensure_ascii=False, separators=(",", ":"), sort_keys=True
|
|
@@ -802,12 +888,37 @@ def health() -> JSONResponse:
|
|
| 802 |
)
|
| 803 |
|
| 804 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 805 |
@app.get("/live")
|
| 806 |
def live() -> JSONResponse:
|
| 807 |
code = 503 if state["status"] == "FAILED" else 200
|
| 808 |
return JSONResponse({"status": state["status"]}, status_code=code)
|
| 809 |
|
| 810 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 811 |
@app.get("/api/v1/identity")
|
| 812 |
def identity() -> dict[str, Any]:
|
| 813 |
return identity_payload()
|
|
@@ -1040,22 +1151,48 @@ def openai_chat_completions(request: ChatCompletionRequest) -> JSONResponse:
|
|
| 1040 |
@app.get("/", response_class=HTMLResponse)
|
| 1041 |
def index() -> str:
|
| 1042 |
return """<!doctype html>
|
| 1043 |
-
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
| 1044 |
-
<
|
| 1045 |
-
|
| 1046 |
-
|
| 1047 |
-
|
| 1048 |
-
|
| 1049 |
-
<
|
| 1050 |
-
<
|
| 1051 |
-
<
|
| 1052 |
-
|
| 1053 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1054 |
<script>
|
| 1055 |
-
const b=document.querySelector('#run'),o=document.querySelector('#out'),p=document.querySelector('#prompt');
|
|
|
|
| 1056 |
let running=false,hasResult=false;
|
| 1057 |
-
function
|
| 1058 |
-
|
| 1059 |
-
|
| 1060 |
-
|
|
|
|
|
|
|
|
|
|
| 1061 |
</script></body></html>"""
|
|
|
|
| 64 |
SOURCE_ROOT = Path(__file__).resolve().parent
|
| 65 |
SOURCE_REVISION_ENV = "SZL_GITHUB_SOURCE_REVISION"
|
| 66 |
ARTIFACT_ROOT = Path("/opt/szl/model-artifacts")
|
| 67 |
+
SERVICE_NAME = "szl-model-inference-lab"
|
| 68 |
+
SURFACE_NAME = "model-inference"
|
| 69 |
|
| 70 |
|
| 71 |
state: dict[str, Any] = {
|
|
|
|
| 624 |
}
|
| 625 |
|
| 626 |
|
| 627 |
+
def observed_source_revision() -> str | None:
|
| 628 |
+
"""Return only an exact deploy-bound Git revision.
|
| 629 |
+
|
| 630 |
+
The Space repository SHA, release identifier, and model revision are all
|
| 631 |
+
different identities. None is allowed to stand in for the governed
|
| 632 |
+
GitHub source revision.
|
| 633 |
+
"""
|
| 634 |
+
|
| 635 |
+
raw_revision = os.getenv(SOURCE_REVISION_ENV, "").strip().lower()
|
| 636 |
+
if len(raw_revision) != 40:
|
| 637 |
+
return None
|
| 638 |
+
if any(character not in "0123456789abcdef" for character in raw_revision):
|
| 639 |
+
return None
|
| 640 |
+
return raw_revision
|
| 641 |
+
|
| 642 |
+
|
| 643 |
def build_info_payload() -> dict[str, Any]:
|
| 644 |
"""Expose the exact Git source bound by the governed deploy workflow.
|
| 645 |
|
|
|
|
| 648 |
the internal release manifest.
|
| 649 |
"""
|
| 650 |
|
| 651 |
+
revision = observed_source_revision()
|
| 652 |
+
revision_observed = revision is not None
|
|
|
|
|
|
|
|
|
|
| 653 |
return {
|
| 654 |
"schema": "szl.build-info/v1",
|
| 655 |
+
"service": SERVICE_NAME,
|
| 656 |
"build": {
|
| 657 |
"state": "OBSERVED" if revision_observed else "UNKNOWN",
|
| 658 |
+
"revision": revision,
|
| 659 |
"revision_source": (
|
| 660 |
f"Hugging Face Space variable {SOURCE_REVISION_ENV}"
|
| 661 |
if revision_observed
|
|
|
|
| 674 |
}
|
| 675 |
|
| 676 |
|
| 677 |
+
def version_payload() -> dict[str, Any]:
|
| 678 |
+
revision = observed_source_revision()
|
| 679 |
+
return {
|
| 680 |
+
"schemaVersion": "szl.vertical-conformance.version.v1",
|
| 681 |
+
"service": SERVICE_NAME,
|
| 682 |
+
"surface": SURFACE_NAME,
|
| 683 |
+
"gitSha": revision,
|
| 684 |
+
"evidenceState": "MEASURED" if revision is not None else "UNAVAILABLE",
|
| 685 |
+
}
|
| 686 |
+
|
| 687 |
+
|
| 688 |
+
def evidence_payload() -> dict[str, Any]:
|
| 689 |
+
revision = observed_source_revision()
|
| 690 |
+
receipts_verified = (
|
| 691 |
+
state["receipt_status"] == "DECLARED_KEY_SIGNATURES_VALID"
|
| 692 |
+
)
|
| 693 |
+
return {
|
| 694 |
+
"schemaVersion": "szl.vertical-conformance.evidence.v1",
|
| 695 |
+
"service": SERVICE_NAME,
|
| 696 |
+
"surface": SURFACE_NAME,
|
| 697 |
+
"gitSha": revision,
|
| 698 |
+
"evidenceState": (
|
| 699 |
+
"MEASURED"
|
| 700 |
+
if revision is not None and state["source_integrity"] and receipts_verified
|
| 701 |
+
else "UNAVAILABLE"
|
| 702 |
+
),
|
| 703 |
+
"runtime": {
|
| 704 |
+
"status": state["status"],
|
| 705 |
+
"ready": state["status"] == "READY",
|
| 706 |
+
"sourceIntegrity": state["source_integrity"],
|
| 707 |
+
"modelSha256Verified": state["model_sha256"] == MODEL_SHA256,
|
| 708 |
+
},
|
| 709 |
+
"model": {
|
| 710 |
+
"repo": MODEL_REPO,
|
| 711 |
+
"revision": MODEL_REVISION,
|
| 712 |
+
"file": MODEL_FILE,
|
| 713 |
+
"sha256": MODEL_SHA256,
|
| 714 |
+
},
|
| 715 |
+
"receipts": [
|
| 716 |
+
{
|
| 717 |
+
"kind": "training",
|
| 718 |
+
"status": state["receipt_status"],
|
| 719 |
+
"canonicalSha256": (
|
| 720 |
+
state.get("receipt_evidence", {}).get(
|
| 721 |
+
"training_canonical_sha256"
|
| 722 |
+
)
|
| 723 |
+
),
|
| 724 |
+
"scope": "repository-declared key continuity only",
|
| 725 |
+
},
|
| 726 |
+
{
|
| 727 |
+
"kind": "evaluation",
|
| 728 |
+
"status": state["receipt_status"],
|
| 729 |
+
"canonicalSha256": (
|
| 730 |
+
state.get("receipt_evidence", {}).get("eval_canonical_sha256")
|
| 731 |
+
),
|
| 732 |
+
"scope": "repository-declared key continuity only",
|
| 733 |
+
},
|
| 734 |
+
],
|
| 735 |
+
"outputProvenance": {
|
| 736 |
+
"signatureStatus": "UNSIGNED",
|
| 737 |
+
"authenticityEstablished": False,
|
| 738 |
+
"record": "content-addressed and returned to the caller; not persisted",
|
| 739 |
+
},
|
| 740 |
+
"limitations": [
|
| 741 |
+
"No independent benchmark or safety certification is claimed.",
|
| 742 |
+
"Training and evaluation receipts do not cover this runtime output.",
|
| 743 |
+
"The public Space is best-effort and has no service-level agreement.",
|
| 744 |
+
],
|
| 745 |
+
}
|
| 746 |
+
|
| 747 |
+
|
| 748 |
def canonical_json_bytes(payload: dict[str, Any]) -> bytes:
|
| 749 |
return json.dumps(
|
| 750 |
payload, ensure_ascii=False, separators=(",", ":"), sort_keys=True
|
|
|
|
| 888 |
)
|
| 889 |
|
| 890 |
|
| 891 |
+
@app.get("/readyz")
|
| 892 |
+
def readyz() -> JSONResponse:
|
| 893 |
+
return health()
|
| 894 |
+
|
| 895 |
+
|
| 896 |
@app.get("/live")
|
| 897 |
def live() -> JSONResponse:
|
| 898 |
code = 503 if state["status"] == "FAILED" else 200
|
| 899 |
return JSONResponse({"status": state["status"]}, status_code=code)
|
| 900 |
|
| 901 |
|
| 902 |
+
@app.get("/healthz")
|
| 903 |
+
def healthz() -> JSONResponse:
|
| 904 |
+
return live()
|
| 905 |
+
|
| 906 |
+
|
| 907 |
+
@app.get("/version")
|
| 908 |
+
def version() -> JSONResponse:
|
| 909 |
+
payload = version_payload()
|
| 910 |
+
return JSONResponse(payload, status_code=200 if payload["gitSha"] else 503)
|
| 911 |
+
|
| 912 |
+
|
| 913 |
+
@app.get("/evidence")
|
| 914 |
+
def evidence() -> JSONResponse:
|
| 915 |
+
payload = evidence_payload()
|
| 916 |
+
return JSONResponse(
|
| 917 |
+
payload,
|
| 918 |
+
status_code=200 if payload["evidenceState"] == "MEASURED" else 503,
|
| 919 |
+
)
|
| 920 |
+
|
| 921 |
+
|
| 922 |
@app.get("/api/v1/identity")
|
| 923 |
def identity() -> dict[str, Any]:
|
| 924 |
return identity_payload()
|
|
|
|
| 1151 |
@app.get("/", response_class=HTMLResponse)
|
| 1152 |
def index() -> str:
|
| 1153 |
return """<!doctype html>
|
| 1154 |
+
<html lang="en" data-screenshot-ready="false"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
| 1155 |
+
<meta name="theme-color" content="#0c0811"><meta name="color-scheme" content="dark">
|
| 1156 |
+
<meta name="description" content="A bounded, source-bound GGUF inference instrument with visible model, receipt, and unsigned-output boundaries.">
|
| 1157 |
+
<meta property="og:type" content="website"><meta property="og:title" content="SZL Model Inference Lab - Khipu Loom">
|
| 1158 |
+
<meta property="og:description" content="Every token leaves a thread: inspect exact model bytes, deploy identity, receipt evidence, and bounded execution together.">
|
| 1159 |
+
<meta property="og:url" content="https://szlholdings-szl-model-inference-lab.hf.space/">
|
| 1160 |
+
<link rel="canonical" href="https://szlholdings-szl-model-inference-lab.hf.space/">
|
| 1161 |
+
<title>SZL Model Inference Lab - Khipu Loom</title>
|
| 1162 |
+
<style>
|
| 1163 |
+
:root{--ink:#0c0811;--ink-2:#120d19;--panel:#171020;--line:#3b2a45;--text:#f7f0f4;--muted:#bcaebc;--fiber:#ef86d7;--ember:#ffbd69;--mint:#63e6d4;--danger:#ff6f82;--radius:22px;--shadow:0 30px 90px #0009}
|
| 1164 |
+
*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:radial-gradient(circle at 78% 12%,#301733 0,transparent 34%),radial-gradient(circle at 7% 78%,#172b30 0,transparent 30%),var(--ink);color:var(--text);font:16px/1.55 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;min-height:100vh}
|
| 1165 |
+
body:before{content:"";position:fixed;inset:0;pointer-events:none;opacity:.2;background-image:linear-gradient(#fff1 1px,transparent 1px),linear-gradient(90deg,#fff1 1px,transparent 1px);background-size:48px 48px;mask-image:linear-gradient(to bottom,#000,transparent 82%)}
|
| 1166 |
+
a{color:inherit}.skip{position:fixed;left:16px;top:-60px;background:var(--text);color:var(--ink);padding:10px 14px;z-index:20}.skip:focus{top:16px}
|
| 1167 |
+
.shell{width:min(1180px,calc(100% - 40px));margin:auto;position:relative}.nav{display:flex;align-items:center;justify-content:space-between;gap:24px;padding:22px 0;border-bottom:1px solid #ffffff14}.brand{display:flex;align-items:center;gap:12px;text-decoration:none}.mark{width:34px;height:34px;border:1px solid var(--fiber);border-radius:50%;position:relative;box-shadow:inset 0 0 18px #ef86d744}.mark:before,.mark:after{content:"";position:absolute;background:var(--mint)}.mark:before{width:1px;height:46px;left:16px;top:-7px}.mark:after{width:46px;height:1px;left:-7px;top:16px}.brand strong{letter-spacing:.08em}.brand small{display:block;color:var(--muted);font:700 10px/1.2 ui-monospace,monospace;letter-spacing:.18em;text-transform:uppercase}.links{display:flex;gap:22px;align-items:center}.links a{color:var(--muted);font-size:13px;text-decoration:none}.links a:hover{color:var(--text)}
|
| 1168 |
+
.hero{display:grid;grid-template-columns:minmax(0,1.02fr) minmax(360px,.98fr);gap:58px;align-items:center;padding:82px 0 50px}.eyebrow,.mono{font-family:ui-monospace,SFMono-Regular,Consolas,monospace}.eyebrow{color:var(--mint);font-size:12px;font-weight:700;letter-spacing:.18em;text-transform:uppercase}.hero h1{font-size:clamp(52px,7vw,92px);line-height:.94;letter-spacing:-.06em;margin:20px 0 26px;max-width:760px}.hero h1 span{color:var(--ember);font-family:Georgia,serif;font-style:italic;font-weight:500}.lede{font-size:clamp(18px,2vw,22px);color:var(--muted);max-width:660px}.hero-actions{display:flex;flex-wrap:wrap;gap:12px;margin-top:30px}.button,.ghost{display:inline-flex;align-items:center;justify-content:center;min-height:48px;padding:0 20px;border-radius:999px;font-weight:800;text-decoration:none}.button{border:0;background:var(--text);color:var(--ink);cursor:pointer}.button:hover{background:var(--ember)}.button:disabled{cursor:not-allowed;opacity:.48}.ghost{border:1px solid var(--line);color:var(--text);background:#ffffff08}.ghost:hover{border-color:var(--fiber)}
|
| 1169 |
+
.loom{min-height:470px;border:1px solid #ffffff1b;border-radius:32px;background:linear-gradient(145deg,#ffffff0d,#ffffff03);box-shadow:var(--shadow);position:relative;overflow:hidden}.loom:before{content:"";position:absolute;inset:28px;border:1px solid #ffffff12;border-radius:24px}.cord{position:absolute;left:13%;right:13%;height:1px;transform-origin:center;background:linear-gradient(90deg,transparent,var(--fiber),var(--ember),var(--mint),transparent);box-shadow:0 0 14px #ef86d766}.cord.c1{top:23%;transform:rotate(14deg)}.cord.c2{top:43%;transform:rotate(-9deg)}.cord.c3{top:65%;transform:rotate(5deg)}.cord.c4{top:78%;transform:rotate(-15deg)}.knot{position:absolute;width:18px;height:18px;border-radius:50%;background:var(--panel);border:3px solid var(--ember);box-shadow:0 0 0 7px #ffbd6917,0 0 28px #ffbd6955}.k1{left:24%;top:29%}.k2{left:52%;top:39%;border-color:var(--fiber)}.k3{left:72%;top:58%;border-color:var(--mint)}.k4{left:34%;top:70%}.loom-label{position:absolute;padding:9px 12px;border:1px solid var(--line);border-radius:12px;background:#0c0811db;font:700 10px/1.2 ui-monospace,monospace;letter-spacing:.12em;text-transform:uppercase}.l1{left:9%;top:12%;color:var(--fiber)}.l2{right:8%;top:33%;color:var(--ember)}.l3{left:12%;bottom:13%;color:var(--mint)}.loom-core{position:absolute;left:50%;top:50%;width:130px;height:130px;transform:translate(-50%,-50%);border:1px solid #ffffff20;border-radius:50%;display:grid;place-items:center;background:#100b17cc;box-shadow:0 0 0 22px #ffffff05,0 0 80px #ef86d72b;text-align:center}.loom-core b{font:700 12px/1.2 ui-monospace,monospace;letter-spacing:.13em}.loom-core small{display:block;color:var(--muted);margin-top:5px}
|
| 1170 |
+
.status-strip{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--line);border-radius:20px;background:#0c0811b8;overflow:hidden;margin:4px 0 72px}.metric{padding:20px 22px;border-right:1px solid var(--line)}.metric:last-child{border:0}.metric b{display:block;font:800 14px/1.25 ui-monospace,monospace;word-break:break-word}.metric small{color:var(--muted);font-size:11px;letter-spacing:.1em;text-transform:uppercase}.state{display:inline-flex;align-items:center;gap:8px}.state:before{content:"";width:8px;height:8px;border-radius:50%;background:var(--ember);box-shadow:0 0 14px currentColor}.state.ready{color:var(--mint)}.state.ready:before{background:var(--mint)}.state.failed{color:var(--danger)}.state.failed:before{background:var(--danger)}
|
| 1171 |
+
.section{padding:72px 0;border-top:1px solid #ffffff12}.section-head{display:flex;align-items:end;justify-content:space-between;gap:30px;margin-bottom:30px}.section h2{font-size:clamp(34px,5vw,58px);line-height:1;margin:10px 0;letter-spacing:-.04em}.section-copy{color:var(--muted);max-width:600px}.composer{display:grid;grid-template-columns:1.08fr .92fr;border:1px solid var(--line);border-radius:28px;overflow:hidden;background:var(--panel);box-shadow:var(--shadow)}.input-pane,.output-pane{padding:28px}.output-pane{background:#0a0710;border-left:1px solid var(--line)}label{display:block;font-weight:800;margin-bottom:10px}textarea{box-sizing:border-box;width:100%;min-height:180px;resize:vertical;background:#0b0710;color:var(--text);border:1px solid #4a3554;border-radius:16px;padding:16px;font:15px/1.55 ui-monospace,monospace}textarea:focus{outline:2px solid var(--fiber);outline-offset:2px}.composer-meta{display:flex;justify-content:space-between;gap:14px;color:var(--muted);font-size:12px;margin:10px 2px}.run-row{display:flex;align-items:center;justify-content:space-between;gap:18px;margin-top:18px}.output-label{color:var(--mint);font:700 11px ui-monospace,monospace;letter-spacing:.14em;text-transform:uppercase}.output-pane pre{white-space:pre-wrap;margin:18px 0 0;min-height:220px;color:#e8dfe8;font:15px/1.7 ui-monospace,monospace}.thread-id{color:var(--muted);font-size:11px;word-break:break-all}
|
| 1172 |
+
.evidence-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}.evidence-card{min-height:220px;border:1px solid var(--line);border-radius:20px;background:#ffffff08;padding:22px}.evidence-card .number{color:var(--ember);font:700 12px ui-monospace,monospace}.evidence-card h3{font-size:20px;margin:40px 0 10px}.evidence-card p{color:var(--muted);font-size:14px}.evidence-card a{color:var(--mint)}
|
| 1173 |
+
.boundary{display:grid;grid-template-columns:1fr 1fr;gap:16px}.boundary article{padding:26px;border-radius:22px;border:1px solid var(--line)}.boundary article:first-child{background:#17302e55}.boundary article:last-child{background:#34182255}.boundary h3{margin-top:0}.boundary ul{padding-left:20px;color:var(--muted)}footer{display:flex;justify-content:space-between;gap:24px;padding:38px 0 56px;color:var(--muted);font-size:12px;border-top:1px solid #ffffff12}
|
| 1174 |
+
:focus-visible{outline:2px solid var(--mint);outline-offset:3px}@media(max-width:850px){.links{display:none}.hero{grid-template-columns:1fr;padding-top:56px}.loom{min-height:390px}.status-strip{grid-template-columns:1fr 1fr}.metric:nth-child(2){border-right:0}.metric:nth-child(-n+2){border-bottom:1px solid var(--line)}.composer{grid-template-columns:1fr}.output-pane{border-left:0;border-top:1px solid var(--line)}.evidence-grid{grid-template-columns:1fr}.boundary{grid-template-columns:1fr}.section-head{display:block}}@media(max-width:520px){.shell{width:min(100% - 24px,1180px)}.hero{gap:34px}.hero h1{font-size:52px}.loom{min-height:330px}.status-strip{grid-template-columns:1fr}.metric{border-right:0;border-bottom:1px solid var(--line)!important}.metric:last-child{border-bottom:0!important}.input-pane,.output-pane{padding:20px}.run-row{align-items:stretch;flex-direction:column}.button{width:100%}footer{display:block}}
|
| 1175 |
+
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}*,*:before,*:after{animation:none!important;transition:none!important}}
|
| 1176 |
+
</style></head>
|
| 1177 |
+
<body><a class="skip" href="#main">Skip to inference lab</a><div class="shell">
|
| 1178 |
+
<nav class="nav" aria-label="Primary"><a class="brand" href="/"><span class="mark" aria-hidden="true"></span><span><strong>SZL / KHIPU LOOM</strong><small>Bounded inference instrument</small></span></a><div class="links"><a href="#run-lab">Run</a><a href="#evidence">Evidence</a><a href="/api/v1/identity">Identity</a><a href="/.well-known/szl-inference-contract.json">API contract</a></div></nav>
|
| 1179 |
+
<main id="main"><section class="hero"><div><p class="eyebrow">Formula genome 01 / public cpu instrument</p><h1>Every token leaves a <span>thread.</span></h1><p class="lede">A bounded inference surface where the exact model bytes, source revision, receipt chain, limits, and missing guarantees remain visible together.</p><div class="hero-actions"><a class="button" href="#run-lab">Enter the loom</a><a class="ghost" href="/evidence">Inspect machine evidence</a></div></div>
|
| 1180 |
+
<div class="loom" aria-label="Abstract Khipu provenance loom showing source, model, and receipt threads"><i class="cord c1"></i><i class="cord c2"></i><i class="cord c3"></i><i class="cord c4"></i><i class="knot k1"></i><i class="knot k2"></i><i class="knot k3"></i><i class="knot k4"></i><span class="loom-label l1">source / exact sha</span><span class="loom-label l2">model / q4_k_m</span><span class="loom-label l3">output / unsigned</span><div class="loom-core"><div><b>KHIPU<br>1.5B</b><small>CPU bound</small></div></div></div></section>
|
| 1181 |
+
<section class="status-strip" aria-label="Live runtime summary"><div class="metric"><small>Runtime</small><b id="runtime-state" class="state">CHECKING</b></div><div class="metric"><small>Git source</small><b id="source-sha">UNAVAILABLE</b></div><div class="metric"><small>Model pin</small><b>67d60ec...f4a4b5d25</b></div><div class="metric"><small>Output proof</small><b>UNSIGNED / HASHED</b></div></section>
|
| 1182 |
+
<section class="section" id="run-lab"><div class="section-head"><div><p class="eyebrow">Bounded generation</p><h2>Pull one thread.</h2></div><p class="section-copy">One request at a time. At most 1,200 characters, 800 formatted prompt tokens, and 32 generated tokens. Greedy decoding. No tools, streaming, or hidden fallback.</p></div>
|
| 1183 |
+
<div class="composer"><div class="input-pane"><label for="prompt">Prompt</label><textarea id="prompt" maxlength="1200">Reply with one short sentence describing what a cryptographic receipt can prove.</textarea><div class="composer-meta"><span id="char-count">0 / 1,200 characters</span><span>24 output tokens</span></div><div class="run-row"><button class="button" id="run" disabled aria-disabled="true">Run bounded inference</button><span class="mono thread-id" id="request-state">Waiting for readiness</span></div></div><div class="output-pane"><span class="output-label">Model output</span><pre id="out" role="status" aria-live="polite">Checking the runtime and evidence threads...</pre></div></div></section>
|
| 1184 |
+
<section class="section" id="evidence"><div class="section-head"><div><p class="eyebrow">Inspectable by default</p><h2>The evidence bay.</h2></div><p class="section-copy">The interface does not turn provenance into decoration. Every status below resolves to a machine-readable surface.</p></div><div class="evidence-grid"><article class="evidence-card"><span class="number">01 / SOURCE</span><h3>Exact deploy identity</h3><p id="source-detail">The Git revision must come from the governed Space variable. Missing or malformed identity fails closed.</p><a href="/version">Open /version</a></article><article class="evidence-card"><span class="number">02 / RECEIPTS</span><h3>Declared-key chain</h3><p id="receipt-detail">Training and evaluation receipts are verified against the repository-declared key and canonical hash chain.</p><a href="/evidence">Open /evidence</a></article><article class="evidence-card"><span class="number">03 / RUNTIME</span><h3>Exact model bytes</h3><p>Q4_K_M GGUF bytes are fetched at image build from an immutable revision, size-checked, hash-checked, and loaded offline.</p><a href="/api/v1/identity">Open identity</a></article></div></section>
|
| 1185 |
+
<section class="section"><div class="boundary"><article><p class="eyebrow">What is measured</p><h3>Internal integrity and bounded execution</h3><ul><li>Exact GGUF revision, size, and SHA-256</li><li>Exact deploy-bound Git revision when configured</li><li>Declared-key training/evaluation receipt chain</li><li>Runtime readiness and deterministic request limits</li></ul></article><article><p class="eyebrow">What is not claimed</p><h3>Authenticity and quality remain bounded</h3><ul><li>No independent identity or key-ownership binding</li><li>No post-quantization quality or safety certification</li><li>No signed output or reproducible execution claim</li><li>No provider SLA, sensitive-data handling guarantee, or autonomy</li></ul></article></div></section></main>
|
| 1186 |
+
<footer><span>SZL Holdings / Khipu Loom / Apache-2.0</span><span class="mono">MEASURED, REPORTED, UNKNOWN, or UNAVAILABLE - never implied</span></footer></div>
|
| 1187 |
<script>
|
| 1188 |
+
const b=document.querySelector('#run'),o=document.querySelector('#out'),p=document.querySelector('#prompt'),root=document.documentElement;
|
| 1189 |
+
const runtimeState=document.querySelector('#runtime-state'),sourceSha=document.querySelector('#source-sha'),requestState=document.querySelector('#request-state'),charCount=document.querySelector('#char-count'),sourceDetail=document.querySelector('#source-detail'),receiptDetail=document.querySelector('#receipt-detail');
|
| 1190 |
let running=false,hasResult=false;
|
| 1191 |
+
function short(value){return typeof value==='string'&&value.length>12?value.slice(0,7)+'...'+value.slice(-7):value||'UNAVAILABLE'}
|
| 1192 |
+
function countChars(){charCount.textContent=p.value.length.toLocaleString()+' / 1,200 characters'}
|
| 1193 |
+
function renderStatus(status,failure){const ready=status==='READY';runtimeState.textContent=status||'UNAVAILABLE';runtimeState.className='state '+(ready?'ready':status==='FAILED'?'failed':'');b.disabled=!ready||running;b.setAttribute('aria-disabled',String(b.disabled));root.dataset.screenshotReady=String(ready);if(running)return;if(!hasResult)requestState.textContent=ready?'Runtime ready':status==='FAILED'?'Runtime failed':'Warming exact model bytes';if(ready){if(!hasResult)o.textContent='Runtime READY. Pull a thread when you are ready.';return}if(!hasResult)o.textContent=status==='FAILED'?'Runtime FAILED'+(failure?': '+failure:'')+'.':status==='STARTING'?'Runtime STARTING. Verifying exact model bytes and receipts...':'Runtime status unavailable; retrying...'}
|
| 1194 |
+
async function getJson(path){const r=await fetch(path,{cache:'no-store'});let j={};try{j=await r.json()}catch(_){j={}}return{ok:r.ok,status:r.status,json:j}}
|
| 1195 |
+
async function refresh(){const [health,version,evidence]=await Promise.allSettled([getJson('/health'),getJson('/version'),getJson('/evidence')]);if(health.status==='fulfilled')renderStatus(health.value.json.status,health.value.json.failure_code);else renderStatus('UNAVAILABLE');if(version.status==='fulfilled'){const sha=version.value.json.gitSha;sourceSha.textContent=short(sha);sourceDetail.textContent=sha?'Governed Git source '+sha+' is bound to this deployment.':'Exact governed Git source is unavailable; /version fails closed.'}if(evidence.status==='fulfilled'){const receipts=evidence.value.json.receipts||[];const verified=receipts.filter(x=>x.status==='DECLARED_KEY_SIGNATURES_VALID').length;receiptDetail.textContent=verified===2?'Two declared-key receipts are visible and chain-verified. Runtime outputs remain unsigned.':'Receipt evidence is not yet available; no green state is inferred.'}}
|
| 1196 |
+
p.addEventListener('input',countChars);b.addEventListener('click',async()=>{if(b.disabled)return;running=true;hasResult=false;b.disabled=true;b.setAttribute('aria-disabled','true');requestState.textContent='Inference in progress';o.textContent='Pulling the bounded model thread on free CPU...';try{const r=await fetch('/api/v1/infer',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({prompt:p.value,max_new_tokens:24})});const j=await r.json();hasResult=true;o.textContent=r.ok?j.output:JSON.stringify(j);requestState.textContent=r.ok?'Completed / output unsigned':'Request refused / '+r.status}catch(_){hasResult=true;o.textContent='Request unavailable. No result was fabricated.';requestState.textContent='Network unavailable'}finally{running=false;await refresh()}});
|
| 1197 |
+
countChars();refresh();setInterval(refresh,5000);
|
| 1198 |
</script></body></html>"""
|
release.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
| 1 |
{
|
| 2 |
"schema": "szl.space-source-release/v1",
|
| 3 |
-
"release_id": "
|
| 4 |
"source_files": {
|
| 5 |
".dockerignore": "dba1fb4c6538a44cd8d7e77f133af90a8e7927d3dee23f8694fe6d867238333c",
|
| 6 |
"Dockerfile": "6d982909b8678c6ab3bfc21766f3356c1dedfff1c2efea5ba0fbc64ed7cff4b8",
|
| 7 |
"LICENSE": "0a41cda63c0751e5cdb240864525fc8bf29b2d3f2cb5c6406b9bae97f1e1407b",
|
| 8 |
-
"README.md": "
|
| 9 |
-
"app.py": "
|
| 10 |
"download_artifacts.py": "aa9f98b3d19753953864beae167b5e74872270c880e78430bb345a5f06b4ef36",
|
| 11 |
"requirements.txt": "9f690793b7b9c6e1adce88244de01d77898d117779c3428b80190eabaf4658f9",
|
| 12 |
-
"tests/test_app.py": "
|
| 13 |
-
"verify_execution_record.py": "
|
| 14 |
}
|
| 15 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"schema": "szl.space-source-release/v1",
|
| 3 |
+
"release_id": "c893e93c-9bd8-47e9-941e-f8613a608992",
|
| 4 |
"source_files": {
|
| 5 |
".dockerignore": "dba1fb4c6538a44cd8d7e77f133af90a8e7927d3dee23f8694fe6d867238333c",
|
| 6 |
"Dockerfile": "6d982909b8678c6ab3bfc21766f3356c1dedfff1c2efea5ba0fbc64ed7cff4b8",
|
| 7 |
"LICENSE": "0a41cda63c0751e5cdb240864525fc8bf29b2d3f2cb5c6406b9bae97f1e1407b",
|
| 8 |
+
"README.md": "c2edfc0957ccd9f2719ca3c8e71baefd23a9e88d61cfc5d3445009ae470618cb",
|
| 9 |
+
"app.py": "992badf3f02189d7f44bb4cbb5881dce837ecc72f183a7dd40952cf0106a8e37",
|
| 10 |
"download_artifacts.py": "aa9f98b3d19753953864beae167b5e74872270c880e78430bb345a5f06b4ef36",
|
| 11 |
"requirements.txt": "9f690793b7b9c6e1adce88244de01d77898d117779c3428b80190eabaf4658f9",
|
| 12 |
+
"tests/test_app.py": "6538ee45b40e5d0dc12b1849b29c4dcb6b3184542199b0e6669c6239a73888e0",
|
| 13 |
+
"verify_execution_record.py": "1a64e3c8097409c19d971f711d63fbeb2066e3bfe804da31f684d0b795a529a6"
|
| 14 |
}
|
| 15 |
}
|
tests/test_app.py
CHANGED
|
@@ -285,6 +285,77 @@ class AppContractTests(unittest.TestCase):
|
|
| 285 |
else:
|
| 286 |
os.environ[app.SOURCE_REVISION_ENV] = original
|
| 287 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 288 |
def test_openai_model_catalog_is_immutable_and_unsigned(self):
|
| 289 |
response = app.openai_models()
|
| 290 |
payload = json.loads(response.body)
|
|
@@ -736,11 +807,21 @@ class AppContractTests(unittest.TestCase):
|
|
| 736 |
def test_root_waits_for_readiness_before_enabling_inference(self):
|
| 737 |
html = app.index()
|
| 738 |
self.assertIn('id="run" disabled aria-disabled="true"', html)
|
| 739 |
-
self.assertIn("Checking runtime
|
| 740 |
-
self.assertIn("
|
|
|
|
|
|
|
| 741 |
self.assertIn("status==='READY'", html)
|
| 742 |
self.assertIn("status==='STARTING'", html)
|
| 743 |
self.assertIn("status==='FAILED'", html)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 744 |
|
| 745 |
def test_body_limiter_rejects_chunked_overflow(self):
|
| 746 |
downstream_called = False
|
|
|
|
| 285 |
else:
|
| 286 |
os.environ[app.SOURCE_REVISION_ENV] = original
|
| 287 |
|
| 288 |
+
def test_operational_aliases_preserve_liveness_and_readiness(self):
|
| 289 |
+
original = app.state["status"]
|
| 290 |
+
try:
|
| 291 |
+
app.state["status"] = "STARTING"
|
| 292 |
+
self.assertEqual(200, app.healthz().status_code)
|
| 293 |
+
self.assertEqual(503, app.readyz().status_code)
|
| 294 |
+
app.state["status"] = "READY"
|
| 295 |
+
self.assertEqual(200, app.healthz().status_code)
|
| 296 |
+
self.assertEqual(200, app.readyz().status_code)
|
| 297 |
+
app.state["status"] = "FAILED"
|
| 298 |
+
self.assertEqual(503, app.healthz().status_code)
|
| 299 |
+
self.assertEqual(503, app.readyz().status_code)
|
| 300 |
+
finally:
|
| 301 |
+
app.state["status"] = original
|
| 302 |
+
|
| 303 |
+
def test_version_fails_closed_without_exact_deploy_revision(self):
|
| 304 |
+
original = os.environ.pop(app.SOURCE_REVISION_ENV, None)
|
| 305 |
+
try:
|
| 306 |
+
response = app.version()
|
| 307 |
+
payload = json.loads(response.body)
|
| 308 |
+
self.assertEqual(503, response.status_code)
|
| 309 |
+
self.assertIsNone(payload["gitSha"])
|
| 310 |
+
self.assertEqual("UNAVAILABLE", payload["evidenceState"])
|
| 311 |
+
|
| 312 |
+
os.environ[app.SOURCE_REVISION_ENV] = "b" * 40
|
| 313 |
+
response = app.version()
|
| 314 |
+
payload = json.loads(response.body)
|
| 315 |
+
self.assertEqual(200, response.status_code)
|
| 316 |
+
self.assertEqual("b" * 40, payload["gitSha"])
|
| 317 |
+
self.assertEqual("model-inference", payload["surface"])
|
| 318 |
+
finally:
|
| 319 |
+
if original is None:
|
| 320 |
+
os.environ.pop(app.SOURCE_REVISION_ENV, None)
|
| 321 |
+
else:
|
| 322 |
+
os.environ[app.SOURCE_REVISION_ENV] = original
|
| 323 |
+
|
| 324 |
+
def test_evidence_requires_source_and_verified_receipts(self):
|
| 325 |
+
original_revision = os.environ.get(app.SOURCE_REVISION_ENV)
|
| 326 |
+
original_state = dict(app.state)
|
| 327 |
+
try:
|
| 328 |
+
os.environ[app.SOURCE_REVISION_ENV] = "c" * 40
|
| 329 |
+
app.state.update(
|
| 330 |
+
{
|
| 331 |
+
"status": "READY",
|
| 332 |
+
"source_integrity": True,
|
| 333 |
+
"model_sha256": app.MODEL_SHA256,
|
| 334 |
+
"receipt_status": "DECLARED_KEY_SIGNATURES_VALID",
|
| 335 |
+
"receipt_evidence": {
|
| 336 |
+
"training_canonical_sha256": "d" * 64,
|
| 337 |
+
"eval_canonical_sha256": "e" * 64,
|
| 338 |
+
},
|
| 339 |
+
}
|
| 340 |
+
)
|
| 341 |
+
response = app.evidence()
|
| 342 |
+
payload = json.loads(response.body)
|
| 343 |
+
self.assertEqual(200, response.status_code)
|
| 344 |
+
self.assertEqual("MEASURED", payload["evidenceState"])
|
| 345 |
+
self.assertEqual(2, len(payload["receipts"]))
|
| 346 |
+
self.assertEqual("UNSIGNED", payload["outputProvenance"]["signatureStatus"])
|
| 347 |
+
|
| 348 |
+
app.state["receipt_status"] = "NOT_CHECKED"
|
| 349 |
+
response = app.evidence()
|
| 350 |
+
self.assertEqual(503, response.status_code)
|
| 351 |
+
finally:
|
| 352 |
+
app.state.clear()
|
| 353 |
+
app.state.update(original_state)
|
| 354 |
+
if original_revision is None:
|
| 355 |
+
os.environ.pop(app.SOURCE_REVISION_ENV, None)
|
| 356 |
+
else:
|
| 357 |
+
os.environ[app.SOURCE_REVISION_ENV] = original_revision
|
| 358 |
+
|
| 359 |
def test_openai_model_catalog_is_immutable_and_unsigned(self):
|
| 360 |
response = app.openai_models()
|
| 361 |
payload = json.loads(response.body)
|
|
|
|
| 807 |
def test_root_waits_for_readiness_before_enabling_inference(self):
|
| 808 |
html = app.index()
|
| 809 |
self.assertIn('id="run" disabled aria-disabled="true"', html)
|
| 810 |
+
self.assertIn("Checking the runtime and evidence threads", html)
|
| 811 |
+
self.assertIn("getJson('/health')", html)
|
| 812 |
+
self.assertIn("getJson('/version')", html)
|
| 813 |
+
self.assertIn("getJson('/evidence')", html)
|
| 814 |
self.assertIn("status==='READY'", html)
|
| 815 |
self.assertIn("status==='STARTING'", html)
|
| 816 |
self.assertIn("status==='FAILED'", html)
|
| 817 |
+
self.assertIn("if(running)return;if(!hasResult)requestState", html)
|
| 818 |
+
self.assertIn("if(!hasResult)o.textContent", html)
|
| 819 |
+
self.assertIn('data-screenshot-ready="false"', html)
|
| 820 |
+
self.assertIn("prefers-reduced-motion:reduce", html)
|
| 821 |
+
self.assertIn("Every token leaves a", html)
|
| 822 |
+
self.assertIn('name="description"', html)
|
| 823 |
+
self.assertIn('property="og:title"', html)
|
| 824 |
+
self.assertIn('rel="canonical"', html)
|
| 825 |
|
| 826 |
def test_body_limiter_rejects_chunked_overflow(self):
|
| 827 |
downstream_called = False
|
verify_execution_record.py
CHANGED
|
@@ -34,7 +34,7 @@ EXPECTED_MODEL = {
|
|
| 34 |
"sha256": "13c1a1993063e1dff92f7413ccf48eaca6d48efc8801ae9af35961ae3396623a",
|
| 35 |
}
|
| 36 |
EXPECTED_SPACE_ID = "SZLHOLDINGS/szl-model-inference-lab"
|
| 37 |
-
EXPECTED_RELEASE_ID = "
|
| 38 |
EXPECTED_RECORD_HASH_SCOPE = (
|
| 39 |
"canonical UTF-8 JSON with sorted keys and compact separators, "
|
| 40 |
"excluding record_sha256"
|
|
|
|
| 34 |
"sha256": "13c1a1993063e1dff92f7413ccf48eaca6d48efc8801ae9af35961ae3396623a",
|
| 35 |
}
|
| 36 |
EXPECTED_SPACE_ID = "SZLHOLDINGS/szl-model-inference-lab"
|
| 37 |
+
EXPECTED_RELEASE_ID = "c893e93c-9bd8-47e9-941e-f8613a608992"
|
| 38 |
EXPECTED_RECORD_HASH_SCOPE = (
|
| 39 |
"canonical UTF-8 JSON with sorted keys and compact separators, "
|
| 40 |
"excluding record_sha256"
|