feat: pin runtime identity to the exact llama.cpp patch stack (DGR-025)
Derive the recipe's runtime_version axis from the DGR-027 lock manifest (exact upstream commit + ordered patch-stack byte digest) in new meshnet_node.runtime_pin, failing closed on any lock/series/SHA256SUMS/ patch disagreement, and enforce pin discipline on runtime_version in both the node and tracker identity implementations. Also repair pre-existing backlog consistency: add missing DGR-022/DGR-027 completionNotes, regenerate the DGR-022/025/027 issue projections, and relocate three pre-DGR legacy GLM issue files to issues/legacy/. Mark DGR-025 passes=true with evidence at evidence/DGR-025/README.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
221
packages/node/meshnet_node/runtime_pin.py
Normal file
221
packages/node/meshnet_node/runtime_pin.py
Normal file
@@ -0,0 +1,221 @@
|
||||
"""Canonical runtime pin identity for the recipe fingerprint (DGR-025).
|
||||
|
||||
The recipe digest (:mod:`meshnet_node.runtime_recipe`) commits to a
|
||||
``runtime_version`` axis, but a string the operator typed is a label, not a
|
||||
pin: two workers could run different patch stacks under the same label and
|
||||
still hash to the same recipe. The DGR-027 lock manifest
|
||||
(``packages/node/native/llama``) already records the one exact upstream commit
|
||||
and the ordered patch stack the native runtime is built from, so the axis
|
||||
value is *derived* from that manifest, never asserted.
|
||||
|
||||
The derived value has three load-bearing parts, and each is separately fatal
|
||||
to compatibility: the runtime name (from the upstream URL), the exact
|
||||
40-character upstream commit, and a digest over the ordered patch-stack bytes.
|
||||
A different upstream pin, a reordered stack, or a single changed patch byte
|
||||
each produce a different axis value, which produces a different recipe digest,
|
||||
which partitions the route — exactly the fail-closed behavior DGR-025 asks
|
||||
for.
|
||||
|
||||
Every consistency check here fails closed. The manifest keeps three records of
|
||||
the stack — ``UPSTREAM_LOCK.json``'s ``patch_series``, ``patches/series``, and
|
||||
``patches/SHA256SUMS`` — plus the ``UPSTREAM_COMMIT`` convenience file, and a
|
||||
disagreement between any two of them means the workspace's identity is
|
||||
unknowable, not "probably fine". This module reads the committed manifest
|
||||
only; fetching and patching the actual source tree stays with
|
||||
``scripts/llama_cpp_dependency.py`` (DGR-027).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
# Domain separation, matching the runtime_recipe digest convention: a patch
|
||||
# stack digest can never be confused with an artifact or recipe digest.
|
||||
PATCH_STACK_DIGEST_DOMAIN = "meshnet.runtime-patch-stack.v1"
|
||||
|
||||
# The UPSTREAM_LOCK.json layout this reader understands (DGR-027 schema).
|
||||
RUNTIME_PIN_SCHEMA_VERSION = 1
|
||||
|
||||
# The committed DGR-027 manifest for the llama.cpp runtime.
|
||||
DEFAULT_LOCK_DIR = Path(__file__).resolve().parent.parent / "native" / "llama"
|
||||
|
||||
_HEX40 = re.compile(r"^[0-9a-f]{40}$")
|
||||
_HEX64 = re.compile(r"^[0-9a-f]{64}$")
|
||||
|
||||
|
||||
class RuntimePinError(ValueError):
|
||||
"""The lock workspace is missing, malformed, or internally inconsistent."""
|
||||
|
||||
|
||||
def _canonical_sha256(value: object) -> str:
|
||||
payload = json.dumps(
|
||||
value, sort_keys=True, separators=(",", ":"), ensure_ascii=False
|
||||
)
|
||||
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RuntimePin:
|
||||
"""One exact runtime: a name, an upstream commit, and an ordered patch stack."""
|
||||
|
||||
runtime_name: str
|
||||
upstream_commit: str
|
||||
patch_series: tuple[str, ...]
|
||||
patch_digests: tuple[str, ...]
|
||||
|
||||
@property
|
||||
def patch_stack_digest(self) -> str:
|
||||
"""A digest over the ordered (name, bytes-digest) stack.
|
||||
|
||||
Order is digested deliberately: applying the same patches in a
|
||||
different order can produce a different tree, so a reordered stack is
|
||||
a different runtime.
|
||||
"""
|
||||
return _canonical_sha256(
|
||||
{
|
||||
"domain": PATCH_STACK_DIGEST_DOMAIN,
|
||||
"body": {
|
||||
"patches": [
|
||||
[name, digest]
|
||||
for name, digest in zip(self.patch_series, self.patch_digests)
|
||||
]
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@property
|
||||
def runtime_version(self) -> str:
|
||||
"""The exact ``runtime_version`` recipe axis value for this pin."""
|
||||
return (
|
||||
f"{self.runtime_name}@{self.upstream_commit}"
|
||||
f"+patchstack.{self.patch_stack_digest}"
|
||||
)
|
||||
|
||||
|
||||
def _read_text(path: Path, what: str) -> str:
|
||||
try:
|
||||
return path.read_text(encoding="utf-8")
|
||||
except FileNotFoundError:
|
||||
raise RuntimePinError(f"{what} not found at {path}") from None
|
||||
except OSError as exc:
|
||||
raise RuntimePinError(f"{what} at {path} is unreadable: {exc}") from exc
|
||||
|
||||
|
||||
def _read_series_file(path: Path) -> list[str]:
|
||||
lines = _read_text(path, "patches/series").splitlines()
|
||||
return [line.strip() for line in lines if line.strip() and not line.startswith("#")]
|
||||
|
||||
|
||||
def _read_sums_file(path: Path) -> list[tuple[str, str]]:
|
||||
entries: list[tuple[str, str]] = []
|
||||
for line in _read_text(path, "patches/SHA256SUMS").splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
parts = line.split(None, 1)
|
||||
if len(parts) != 2 or not _HEX64.match(parts[0]):
|
||||
raise RuntimePinError(
|
||||
"patches/SHA256SUMS contains a line that is not "
|
||||
"'<sha256> <patch name>'"
|
||||
)
|
||||
entries.append((parts[0], parts[1].strip()))
|
||||
return entries
|
||||
|
||||
|
||||
def load_runtime_pin(lock_dir: Path = DEFAULT_LOCK_DIR) -> RuntimePin:
|
||||
"""Derive the exact runtime pin from a DGR-027 lock workspace, or refuse.
|
||||
|
||||
Refuses — rather than guessing — on a missing or malformed lock, a moving
|
||||
commit reference, a disagreement between the lock's ``patch_series``, the
|
||||
``patches/series`` file, ``patches/SHA256SUMS``, or the actual patch
|
||||
bytes, and on an ``UPSTREAM_COMMIT`` file that names a different commit.
|
||||
"""
|
||||
lock_path = lock_dir / "UPSTREAM_LOCK.json"
|
||||
raw = _read_text(lock_path, "UPSTREAM_LOCK.json")
|
||||
try:
|
||||
lock = json.loads(raw)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise RuntimePinError(f"UPSTREAM_LOCK.json is not valid JSON: {exc}") from exc
|
||||
if not isinstance(lock, dict):
|
||||
raise RuntimePinError("UPSTREAM_LOCK.json must be a JSON object")
|
||||
|
||||
schema = lock.get("schema_version")
|
||||
if schema != RUNTIME_PIN_SCHEMA_VERSION:
|
||||
raise RuntimePinError(
|
||||
f"UPSTREAM_LOCK.json declares schema version {schema!r}; this reader "
|
||||
f"understands version {RUNTIME_PIN_SCHEMA_VERSION}"
|
||||
)
|
||||
|
||||
upstream = lock.get("upstream")
|
||||
if not isinstance(upstream, str) or not upstream.strip():
|
||||
raise RuntimePinError("UPSTREAM_LOCK.json is missing the upstream URL")
|
||||
runtime_name = upstream.rstrip("/").rsplit("/", 1)[-1]
|
||||
if runtime_name.endswith(".git"):
|
||||
runtime_name = runtime_name[: -len(".git")]
|
||||
if not runtime_name:
|
||||
raise RuntimePinError("the upstream URL does not name a runtime")
|
||||
|
||||
commit = lock.get("commit")
|
||||
if not isinstance(commit, str) or not _HEX40.match(commit):
|
||||
raise RuntimePinError(
|
||||
f"UPSTREAM_LOCK.json commit {commit!r} is not an exact 40-character "
|
||||
"hexadecimal object id; a moving reference is not a pin"
|
||||
)
|
||||
|
||||
commit_file = _read_text(lock_dir / "UPSTREAM_COMMIT", "UPSTREAM_COMMIT")
|
||||
recorded = commit_file.strip().splitlines()[0].strip() if commit_file.strip() else ""
|
||||
if recorded != commit:
|
||||
raise RuntimePinError(
|
||||
"UPSTREAM_COMMIT and UPSTREAM_LOCK.json disagree on the pinned commit"
|
||||
)
|
||||
|
||||
lock_series = lock.get("patch_series")
|
||||
if not isinstance(lock_series, list) or not all(
|
||||
isinstance(name, str) and name.strip() for name in lock_series
|
||||
):
|
||||
raise RuntimePinError(
|
||||
"UPSTREAM_LOCK.json patch_series must be a list of patch file names"
|
||||
)
|
||||
if len(set(lock_series)) != len(lock_series):
|
||||
raise RuntimePinError("UPSTREAM_LOCK.json patch_series contains a duplicate")
|
||||
|
||||
series = _read_series_file(lock_dir / "patches" / "series")
|
||||
if series != lock_series:
|
||||
raise RuntimePinError(
|
||||
"patches/series and UPSTREAM_LOCK.json patch_series disagree on the "
|
||||
"ordered patch stack"
|
||||
)
|
||||
|
||||
sums = _read_sums_file(lock_dir / "patches" / "SHA256SUMS")
|
||||
if [name for _, name in sums] != lock_series:
|
||||
raise RuntimePinError(
|
||||
"patches/SHA256SUMS does not record exactly the ordered patch stack "
|
||||
"named by UPSTREAM_LOCK.json"
|
||||
)
|
||||
|
||||
digests: list[str] = []
|
||||
for (expected_digest, name) in sums:
|
||||
patch_path = lock_dir / "patches" / name
|
||||
try:
|
||||
body = patch_path.read_bytes()
|
||||
except FileNotFoundError:
|
||||
raise RuntimePinError(f"patch file {name} is named but missing") from None
|
||||
except OSError as exc:
|
||||
raise RuntimePinError(f"patch file {name} is unreadable: {exc}") from exc
|
||||
actual = hashlib.sha256(body).hexdigest()
|
||||
if actual != expected_digest:
|
||||
raise RuntimePinError(
|
||||
f"patch file {name} does not match its patches/SHA256SUMS digest"
|
||||
)
|
||||
digests.append(actual)
|
||||
|
||||
return RuntimePin(
|
||||
runtime_name=runtime_name,
|
||||
upstream_commit=commit,
|
||||
patch_series=tuple(lock_series),
|
||||
patch_digests=tuple(digests),
|
||||
)
|
||||
Reference in New Issue
Block a user