[verified] fix: enforce canonical native runtime pin

This commit is contained in:
Dobromir Popov
2026-07-17 23:20:03 +03:00
parent ad66f7a4d8
commit db59caa8e9
9 changed files with 110 additions and 36 deletions

View File

@@ -72,17 +72,22 @@ explicitly requires fingerprinting the "runtime pin/patch stack".
committed manifest only; fetching/patching stays with committed manifest only; fetching/patching stays with
`scripts/llama_cpp_dependency.py` (DGR-027). `scripts/llama_cpp_dependency.py` (DGR-027).
- `packages/node/meshnet_node/runtime_recipe.py``runtime_version` is now - `packages/node/meshnet_node/runtime_recipe.py``runtime_version` is now
pin-enforced (`_require_pin`) exactly like `tokenizer_revision`; docstring pin-enforced (`_require_pin`) exactly like `tokenizer_revision`; for the
points at the canonical derivation. llama.cpp backend it must also match the canonical
`llama.cpp@<40-hex>+patchstack.<64-hex>` grammar.
- `packages/node/meshnet_node/native_backend.py` — the production native
identity seam no longer accepts a caller-supplied runtime string. It derives
`runtime_version` directly through `load_runtime_pin()` from the committed
lock and rejects a non-llama backend at this llama.cpp-specific boundary.
- `packages/tracker/meshnet_tracker/recipe.py` — the independent tracker - `packages/tracker/meshnet_tracker/recipe.py` — the independent tracker
implementation applies the same pin rule in `parse_identity`, keeping the two implementation applies the same backend-specific grammar before re-deriving
implementations in step. the recipe digest, so forged operator labels cannot register or certify.
- `tests/test_runtime_pin_identity.py` (new, TDD — written first and observed - `tests/test_runtime_pin_identity.py` and
failing) — 17 deterministic tests: the committed manifest derives a `tests/test_native_identity_emission.py` — deterministic tests cover lock
deterministic pin whose axis value is a valid recipe pin; a changed patch derivation, production native emission, and node/tracker rejection of the
byte and a reordered stack each change the runtime identity; every manifest forged values from independent review. Conformance vectors were regenerated
disagreement above fails closed; and both node and tracker reject a moving through `scripts/gen_recipe_fingerprint_vectors.py` for the tightened wire
`runtime_version`. contract.
### Backlog-consistency repair (pre-existing damage, honestly recorded) ### Backlog-consistency repair (pre-existing damage, honestly recorded)
@@ -136,7 +141,7 @@ Marked `DGR-025.passes = true` with `completionNotes`; regenerated
PYTHONPATH=packages/node:packages/tracker python3 -m pytest -q tests/test_runtime_pin_identity.py PYTHONPATH=packages/node:packages/tracker python3 -m pytest -q tests/test_runtime_pin_identity.py
``` ```
```text ```text
17 passed in 0.11s 23 passed in 0.15s
``` ```
```bash ```bash
@@ -146,7 +151,7 @@ PYTHONPATH=packages/node:packages/tracker python3 -m pytest -q \
tests/test_node_admission.py tests/test_node_capability.py tests/test_recipe_benchmark.py tests/test_node_admission.py tests/test_node_capability.py tests/test_recipe_benchmark.py
``` ```
```text ```text
196 passed, 1 warning in 5.35s 202 passed, 1 warning in 5.38s
``` ```
```bash ```bash
@@ -169,13 +174,12 @@ artifact was touched and nothing was written under `/home`.
## Limitations ## Limitations
- `runtime_pin.py` proves what the *manifest* pins; it does not prove the - The production native identity seam now derives the manifest pin and cannot
running binary was built from that manifest. Binding the built native worker accept an operator-supplied runtime label. It still cannot attest that the
to the pin it reports (e.g. embedding the patched-tree hash at build time and running binary was built from those locked bytes. Embedding the patched-tree
echoing it through the DGR-022 status contract) belongs with the native hash at build time and echoing it through the DGR-022 status contract belongs
worker stories (DGR-028+/DGR-031); until then `runtime_version` is exactly as with DGR-028+/DGR-031; real distributed certification remains the final trust
trustworthy as the rest of the declared axes — a claim the tracker digests, boundary.
with real distributed certification as the trust boundary (unchanged design).
- The DGR-027-recorded blocker stands: `0002-dense-llama-owned-range-loader.patch` - The DGR-027-recorded blocker stands: `0002-dense-llama-owned-range-loader.patch`
does not apply cleanly against the pin (DGR-028). That does not affect this does not apply cleanly against the pin (DGR-028). That does not affect this
story: the identity commits to the patch *bytes as committed*, which is story: the identity commits to the patch *bytes as committed*, which is

View File

@@ -12,6 +12,7 @@ from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from .native_protocol import BUNDLE_VERSION, SCHEMA_VERSION, pb from .native_protocol import BUNDLE_VERSION, SCHEMA_VERSION, pb
from .runtime_pin import load_runtime_pin
from .runtime_recipe import ( from .runtime_recipe import (
ArtifactIdentity, ArtifactIdentity,
DerivativeBinding, DerivativeBinding,
@@ -72,7 +73,6 @@ class NativeNumericalRecipe:
kv_layout: str kv_layout: str
architecture_adapter: str architecture_adapter: str
backend_id: str backend_id: str
runtime_version: str
recipe_id: str recipe_id: str
recipe_version: str recipe_version: str
catalogue_version: str catalogue_version: str
@@ -95,6 +95,11 @@ def shard_identity_from_native_report(inputs: NativeIdentityInputs) -> ShardIden
report = inputs.loaded_artifact report = inputs.loaded_artifact
pin = inputs.artifact_pin pin = inputs.artifact_pin
recipe = inputs.numerical_recipe recipe = inputs.numerical_recipe
if recipe.backend_id.strip().lower() not in {"llama.cpp", "llama-cpp"}:
raise RecipeIdentityError(
"native llama.cpp identity requires backend_id 'llama.cpp' or 'llama-cpp'"
)
runtime_version = load_runtime_pin().runtime_version
artifact = ArtifactIdentity( artifact = ArtifactIdentity(
artifact_id=pin.artifact_id, artifact_id=pin.artifact_id,
revision=pin.revision, revision=pin.revision,
@@ -115,7 +120,7 @@ def shard_identity_from_native_report(inputs: NativeIdentityInputs) -> ShardIden
tokenizer_revision=inputs.tokenizer_revision, tokenizer_revision=inputs.tokenizer_revision,
architecture_adapter=recipe.architecture_adapter, architecture_adapter=recipe.architecture_adapter,
backend_id=recipe.backend_id, backend_id=recipe.backend_id,
runtime_version=recipe.runtime_version, runtime_version=runtime_version,
boundary_schema_version=recipe.boundary_schema_version, boundary_schema_version=recipe.boundary_schema_version,
protocol_schema_version=recipe.protocol_schema_version, protocol_schema_version=recipe.protocol_schema_version,
recipe_id=recipe.recipe_id, recipe_id=recipe.recipe_id,

View File

@@ -125,6 +125,10 @@ _AXIS_MISMATCH: Mapping[str, str] = {
} }
_HEX64 = re.compile(r"^[0-9a-f]{64}$") _HEX64 = re.compile(r"^[0-9a-f]{64}$")
_LLAMA_CPP_RUNTIME_PIN = re.compile(
r"^llama\.cpp@[0-9a-f]{40}\+patchstack\.[0-9a-f]{64}$"
)
_LLAMA_CPP_BACKEND_IDS = frozenset({"llama.cpp", "llama-cpp"})
# A revision that can move is not a pin. DGR-017 learned this on the artifact; # A revision that can move is not a pin. DGR-017 learned this on the artifact;
# it is just as true of a tokenizer. # it is just as true of a tokenizer.
@@ -194,6 +198,17 @@ def _require_pin(value: Any, what: str) -> str:
return text return text
def _require_runtime_pin(value: Any, backend_id: Any) -> str:
text = _require_pin(value, "recipe.runtime_version")
backend = _require_text(backend_id, "recipe.backend_id").strip().lower()
if backend in _LLAMA_CPP_BACKEND_IDS and not _LLAMA_CPP_RUNTIME_PIN.fullmatch(text):
raise RecipeIdentityError(
"'recipe.runtime_version' for llama.cpp must be "
"'llama.cpp@<40-hex commit>+patchstack.<64-hex digest>'"
)
return text
def _as_mapping(value: Any, what: str) -> Mapping[str, Any]: def _as_mapping(value: Any, what: str) -> Mapping[str, Any]:
if not isinstance(value, Mapping): if not isinstance(value, Mapping):
raise RecipeIdentityError( raise RecipeIdentityError(
@@ -405,7 +420,7 @@ class RuntimeRecipe:
else: else:
_require_text(value, f"recipe.{axis}") _require_text(value, f"recipe.{axis}")
_require_pin(self.tokenizer_revision, "recipe.tokenizer_revision") _require_pin(self.tokenizer_revision, "recipe.tokenizer_revision")
_require_pin(self.runtime_version, "recipe.runtime_version") _require_runtime_pin(self.runtime_version, self.backend_id)
_require_text(self.recipe_id, "recipe.recipe_id") _require_text(self.recipe_id, "recipe.recipe_id")
_require_text(self.recipe_version, "recipe.recipe_version") _require_text(self.recipe_version, "recipe.recipe_version")
_require_text(self.catalogue_version, "recipe.catalogue_version") _require_text(self.catalogue_version, "recipe.catalogue_version")

View File

@@ -70,6 +70,10 @@ STATUS_CERTIFIED = "certified"
MIN_CERTIFYING_NODES = 2 MIN_CERTIFYING_NODES = 2
_HEX64 = re.compile(r"^[0-9a-f]{64}$") _HEX64 = re.compile(r"^[0-9a-f]{64}$")
_LLAMA_CPP_RUNTIME_PIN = re.compile(
r"^llama\.cpp@[0-9a-f]{40}\+patchstack\.[0-9a-f]{64}$"
)
_LLAMA_CPP_BACKEND_IDS = frozenset({"llama.cpp", "llama-cpp"})
_MOVING_REFS = frozenset({"main", "master", "head", "latest", "dev", "trunk"}) _MOVING_REFS = frozenset({"main", "master", "head", "latest", "dev", "trunk"})
@@ -128,6 +132,17 @@ def _pin(value: Any, what: str) -> str:
return text return text
def _runtime_pin(value: Any, backend_id: Any) -> str:
text = _pin(value, "recipe.runtime_version")
backend = _text(backend_id, "recipe.backend_id").strip().lower()
if backend in _LLAMA_CPP_BACKEND_IDS and not _LLAMA_CPP_RUNTIME_PIN.fullmatch(text):
raise RecipeIdentityError(
"'recipe.runtime_version' for llama.cpp must bind a 40-hex commit "
"and a 64-hex ordered patch-stack digest"
)
return text
def _mapping(value: Any, what: str) -> Mapping[str, Any]: def _mapping(value: Any, what: str) -> Mapping[str, Any]:
if not isinstance(value, Mapping): if not isinstance(value, Mapping):
raise RecipeIdentityError(f"{what!r} must be a JSON object") raise RecipeIdentityError(f"{what!r} must be a JSON object")
@@ -333,7 +348,7 @@ def parse_identity(data: Any) -> PresentedIdentity:
else: else:
axes[axis] = _text(value, f"recipe.{axis}") axes[axis] = _text(value, f"recipe.{axis}")
_pin(axes["tokenizer_revision"], "recipe.tokenizer_revision") _pin(axes["tokenizer_revision"], "recipe.tokenizer_revision")
_pin(axes["runtime_version"], "recipe.runtime_version") _runtime_pin(axes["runtime_version"], axes["backend_id"])
identity = PresentedIdentity( identity = PresentedIdentity(
artifact_id=_text(artifact.get("artifact_id"), "artifact.artifact_id"), artifact_id=_text(artifact.get("artifact_id"), "artifact.artifact_id"),

View File

@@ -49,7 +49,7 @@ _RECIPE = RuntimeRecipe(
tokenizer_revision="0123456789abcdef", tokenizer_revision="0123456789abcdef",
architecture_adapter="llama/range-v1", architecture_adapter="llama/range-v1",
backend_id="llama.cpp", backend_id="llama.cpp",
runtime_version="llama.cpp@deadbeef+meshnet.1", runtime_version="llama.cpp@" + "d" * 40 + "+patchstack." + "e" * 64,
recipe_id="example-gguf", recipe_id="example-gguf",
recipe_version="1", recipe_version="1",
catalogue_version="2026.07.1", catalogue_version="2026.07.1",

View File

@@ -8,9 +8,9 @@
"model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b", "model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b",
"recipe_id": "example-gguf", "recipe_id": "example-gguf",
"recipe_version": "1", "recipe_version": "1",
"runtime_recipe_digest": "9b14d70b0835a6428457e4888d453649dd0d2e41fc8ac9d84d232c8c237e68fa" "runtime_recipe_digest": "63001e0efeada5b97f2f3562562dc0fd2d9bd8904dc6b7b99a71d98f3e938bd0"
}, },
"fingerprint_proto_hex": "0a40386130663433643661613439643737383334626462343762636165396634326338383662376363666530616330313439333262326132623338363937613437621240396231346437306230383335613634323834353765343838386434353336343964643064326534316663386163396438346432333263386332333765363866611a0c6578616d706c652d676775662201312a09323032362e30372e31", "fingerprint_proto_hex": "0a40386130663433643661613439643737383334626462343762636165396634326338383662376363666530616330313439333262326132623338363937613437621240363330303165306566656164613562393766326633353632353632646330666432643962643839303464633662376239396137316439386633653933386264301a0c6578616d706c652d676775662201312a09323032362e30372e31",
"identity": { "identity": {
"artifact": { "artifact": {
"architecture": "dense-llama", "architecture": "dense-llama",
@@ -26,7 +26,7 @@
"model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b", "model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b",
"recipe_id": "example-gguf", "recipe_id": "example-gguf",
"recipe_version": "1", "recipe_version": "1",
"runtime_recipe_digest": "9b14d70b0835a6428457e4888d453649dd0d2e41fc8ac9d84d232c8c237e68fa" "runtime_recipe_digest": "63001e0efeada5b97f2f3562562dc0fd2d9bd8904dc6b7b99a71d98f3e938bd0"
}, },
"recipe": { "recipe": {
"activation_dtype": "bfloat16", "activation_dtype": "bfloat16",
@@ -40,7 +40,7 @@
"protocol_schema_version": 1, "protocol_schema_version": 1,
"recipe_id": "example-gguf", "recipe_id": "example-gguf",
"recipe_version": "1", "recipe_version": "1",
"runtime_version": "llama.cpp@deadbeef+meshnet.1", "runtime_version": "llama.cpp@dddddddddddddddddddddddddddddddddddddddd+patchstack.eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"tokenizer_revision": "0123456789abcdef", "tokenizer_revision": "0123456789abcdef",
"weight_quantization": "Q4_K_M" "weight_quantization": "Q4_K_M"
}, },
@@ -58,9 +58,9 @@
"model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b", "model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b",
"recipe_id": "example-gguf", "recipe_id": "example-gguf",
"recipe_version": "1", "recipe_version": "1",
"runtime_recipe_digest": "9b14d70b0835a6428457e4888d453649dd0d2e41fc8ac9d84d232c8c237e68fa" "runtime_recipe_digest": "63001e0efeada5b97f2f3562562dc0fd2d9bd8904dc6b7b99a71d98f3e938bd0"
}, },
"fingerprint_proto_hex": "0a40386130663433643661613439643737383334626462343762636165396634326338383662376363666530616330313439333262326132623338363937613437621240396231346437306230383335613634323834353765343838386434353336343964643064326534316663386163396438346432333263386332333765363866611a0c6578616d706c652d676775662201312a09323032362e30372e31", "fingerprint_proto_hex": "0a40386130663433643661613439643737383334626462343762636165396634326338383662376363666530616330313439333262326132623338363937613437621240363330303165306566656164613562393766326633353632353632646330666432643962643839303464633662376239396137316439386633653933386264301a0c6578616d706c652d676775662201312a09323032362e30372e31",
"identity": { "identity": {
"artifact": { "artifact": {
"architecture": "dense-llama", "architecture": "dense-llama",
@@ -80,7 +80,7 @@
"model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b", "model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b",
"recipe_id": "example-gguf", "recipe_id": "example-gguf",
"recipe_version": "1", "recipe_version": "1",
"runtime_recipe_digest": "9b14d70b0835a6428457e4888d453649dd0d2e41fc8ac9d84d232c8c237e68fa" "runtime_recipe_digest": "63001e0efeada5b97f2f3562562dc0fd2d9bd8904dc6b7b99a71d98f3e938bd0"
}, },
"recipe": { "recipe": {
"activation_dtype": "bfloat16", "activation_dtype": "bfloat16",
@@ -94,7 +94,7 @@
"protocol_schema_version": 1, "protocol_schema_version": 1,
"recipe_id": "example-gguf", "recipe_id": "example-gguf",
"recipe_version": "1", "recipe_version": "1",
"runtime_version": "llama.cpp@deadbeef+meshnet.1", "runtime_version": "llama.cpp@dddddddddddddddddddddddddddddddddddddddd+patchstack.eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"tokenizer_revision": "0123456789abcdef", "tokenizer_revision": "0123456789abcdef",
"weight_quantization": "Q4_K_M" "weight_quantization": "Q4_K_M"
}, },

View File

@@ -15,6 +15,7 @@ from meshnet_node.native_backend import (
shard_identity_from_native_report, shard_identity_from_native_report,
) )
from meshnet_node.native_protocol import SCHEMA_VERSION, pb from meshnet_node.native_protocol import SCHEMA_VERSION, pb
from meshnet_node.runtime_pin import load_runtime_pin
from meshnet_node.recipe_manifest import parse_recipe_manifest from meshnet_node.recipe_manifest import parse_recipe_manifest
from meshnet_tracker.capability import STATE_UNCERTIFIED, evaluate_report from meshnet_tracker.capability import STATE_UNCERTIFIED, evaluate_report
@@ -42,7 +43,6 @@ def _inputs(**changes: object) -> NativeIdentityInputs:
kv_layout="llama-kv-v1", kv_layout="llama-kv-v1",
architecture_adapter="dense-llama-v1", architecture_adapter="dense-llama-v1",
backend_id="llama-cpp", backend_id="llama-cpp",
runtime_version="llama.cpp:e920c523",
recipe_id="native", recipe_id="native",
recipe_version="1", recipe_version="1",
catalogue_version="2026.07.1", catalogue_version="2026.07.1",
@@ -84,6 +84,7 @@ def test_native_identity_uses_loaded_report_not_a_caller_range():
assert (identity.shard_start, identity.shard_end) == (2, 6) assert (identity.shard_start, identity.shard_end) == (2, 6)
assert identity.artifact.architecture == "llama" assert identity.artifact.architecture == "llama"
assert identity.artifact.layer_count == 8 assert identity.artifact.layer_count == 8
assert identity.recipe.runtime_version == load_runtime_pin().runtime_version
def test_native_identity_requires_an_immutable_pin_and_gguf_range(): def test_native_identity_requires_an_immutable_pin_and_gguf_range():

View File

@@ -42,7 +42,7 @@ def _recipe(**changes: object) -> RuntimeRecipe:
"tokenizer_revision": "0123456789abcdef", "tokenizer_revision": "0123456789abcdef",
"architecture_adapter": "llama/range-v1", "architecture_adapter": "llama/range-v1",
"backend_id": "llama.cpp", "backend_id": "llama.cpp",
"runtime_version": "llama.cpp@deadbeef+meshnet.1", "runtime_version": "llama.cpp@" + "d" * 40 + "+patchstack." + "e" * 64,
"recipe_id": "example-gguf", "recipe_id": "example-gguf",
"recipe_version": "1", "recipe_version": "1",
"catalogue_version": "2026.07.1", "catalogue_version": "2026.07.1",
@@ -276,3 +276,37 @@ def test_tracker_rejects_a_moving_runtime_version():
doc.pop("fingerprint", None) doc.pop("fingerprint", None)
with pytest.raises(TrackerRecipeIdentityError, match="moving reference"): with pytest.raises(TrackerRecipeIdentityError, match="moving reference"):
parse_identity(doc) parse_identity(doc)
@pytest.mark.parametrize(
"forged",
[
"llama.cpp@master+patchstack.not-a-digest",
"llama.cpp@e920c523+patchstack.forged",
"release-that-operator-typed",
],
)
def test_node_recipe_rejects_noncanonical_llama_runtime_pins(forged):
with pytest.raises(RecipeIdentityError, match="40-hex commit"):
_recipe(runtime_version=forged)
@pytest.mark.parametrize(
"forged",
[
"llama.cpp@master+patchstack.not-a-digest",
"llama.cpp@e920c523+patchstack.forged",
"release-that-operator-typed",
],
)
def test_tracker_rejects_noncanonical_llama_runtime_pins(forged):
vectors = json.loads(
(Path(__file__).parent / "data" / "recipe_fingerprint_vectors.json").read_text(
encoding="utf-8"
)
)
doc = json.loads(json.dumps(vectors["vectors"][0]["identity"]))
doc["recipe"]["runtime_version"] = forged
doc.pop("fingerprint", None)
with pytest.raises(TrackerRecipeIdentityError, match="40-hex commit"):
parse_identity(doc)

View File

@@ -65,7 +65,7 @@ def _recipe(**changes: object) -> RuntimeRecipe:
"tokenizer_revision": "0123456789abcdef", "tokenizer_revision": "0123456789abcdef",
"architecture_adapter": "llama/range-v1", "architecture_adapter": "llama/range-v1",
"backend_id": "llama.cpp", "backend_id": "llama.cpp",
"runtime_version": "llama.cpp@deadbeef+meshnet.1", "runtime_version": "llama.cpp@" + "d" * 40 + "+patchstack." + "e" * 64,
"recipe_id": "example-gguf", "recipe_id": "example-gguf",
"recipe_version": "1", "recipe_version": "1",
"catalogue_version": "2026.07.1", "catalogue_version": "2026.07.1",
@@ -241,7 +241,7 @@ def test_committed_vectors_cover_a_whole_model_and_a_derivative_shard():
("tokenizer_revision", "fedcba9876543210"), ("tokenizer_revision", "fedcba9876543210"),
("architecture_adapter", "llama/range-v2"), ("architecture_adapter", "llama/range-v2"),
("backend_id", "other-backend"), ("backend_id", "other-backend"),
("runtime_version", "llama.cpp@other+meshnet.1"), ("runtime_version", "llama.cpp@" + "c" * 40 + "+patchstack." + "b" * 64),
("boundary_schema_version", 2), ("boundary_schema_version", 2),
("protocol_schema_version", 2), ("protocol_schema_version", 2),
], ],