[verified] fix: enforce canonical native runtime pin
This commit is contained in:
@@ -72,17 +72,22 @@ explicitly requires fingerprinting the "runtime pin/patch stack".
|
||||
committed manifest only; fetching/patching stays with
|
||||
`scripts/llama_cpp_dependency.py` (DGR-027).
|
||||
- `packages/node/meshnet_node/runtime_recipe.py` — `runtime_version` is now
|
||||
pin-enforced (`_require_pin`) exactly like `tokenizer_revision`; docstring
|
||||
points at the canonical derivation.
|
||||
pin-enforced (`_require_pin`) exactly like `tokenizer_revision`; for the
|
||||
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
|
||||
implementation applies the same pin rule in `parse_identity`, keeping the two
|
||||
implementations in step.
|
||||
- `tests/test_runtime_pin_identity.py` (new, TDD — written first and observed
|
||||
failing) — 17 deterministic tests: the committed manifest derives a
|
||||
deterministic pin whose axis value is a valid recipe pin; a changed patch
|
||||
byte and a reordered stack each change the runtime identity; every manifest
|
||||
disagreement above fails closed; and both node and tracker reject a moving
|
||||
`runtime_version`.
|
||||
implementation applies the same backend-specific grammar before re-deriving
|
||||
the recipe digest, so forged operator labels cannot register or certify.
|
||||
- `tests/test_runtime_pin_identity.py` and
|
||||
`tests/test_native_identity_emission.py` — deterministic tests cover lock
|
||||
derivation, production native emission, and node/tracker rejection of the
|
||||
forged values from independent review. Conformance vectors were regenerated
|
||||
through `scripts/gen_recipe_fingerprint_vectors.py` for the tightened wire
|
||||
contract.
|
||||
|
||||
### 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
|
||||
```
|
||||
```text
|
||||
17 passed in 0.11s
|
||||
23 passed in 0.15s
|
||||
```
|
||||
|
||||
```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
|
||||
```
|
||||
```text
|
||||
196 passed, 1 warning in 5.35s
|
||||
202 passed, 1 warning in 5.38s
|
||||
```
|
||||
|
||||
```bash
|
||||
@@ -169,13 +174,12 @@ artifact was touched and nothing was written under `/home`.
|
||||
|
||||
## Limitations
|
||||
|
||||
- `runtime_pin.py` proves what the *manifest* pins; it does not prove the
|
||||
running binary was built from that manifest. Binding the built native worker
|
||||
to the pin it reports (e.g. embedding the patched-tree hash at build time and
|
||||
echoing it through the DGR-022 status contract) belongs with the native
|
||||
worker stories (DGR-028+/DGR-031); until then `runtime_version` is exactly as
|
||||
trustworthy as the rest of the declared axes — a claim the tracker digests,
|
||||
with real distributed certification as the trust boundary (unchanged design).
|
||||
- The production native identity seam now derives the manifest pin and cannot
|
||||
accept an operator-supplied runtime label. It still cannot attest that the
|
||||
running binary was built from those locked bytes. Embedding the patched-tree
|
||||
hash at build time and echoing it through the DGR-022 status contract belongs
|
||||
with DGR-028+/DGR-031; real distributed certification remains the final trust
|
||||
boundary.
|
||||
- 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
|
||||
story: the identity commits to the patch *bytes as committed*, which is
|
||||
|
||||
@@ -12,6 +12,7 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .native_protocol import BUNDLE_VERSION, SCHEMA_VERSION, pb
|
||||
from .runtime_pin import load_runtime_pin
|
||||
from .runtime_recipe import (
|
||||
ArtifactIdentity,
|
||||
DerivativeBinding,
|
||||
@@ -72,7 +73,6 @@ class NativeNumericalRecipe:
|
||||
kv_layout: str
|
||||
architecture_adapter: str
|
||||
backend_id: str
|
||||
runtime_version: str
|
||||
recipe_id: str
|
||||
recipe_version: str
|
||||
catalogue_version: str
|
||||
@@ -95,6 +95,11 @@ def shard_identity_from_native_report(inputs: NativeIdentityInputs) -> ShardIden
|
||||
report = inputs.loaded_artifact
|
||||
pin = inputs.artifact_pin
|
||||
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_id=pin.artifact_id,
|
||||
revision=pin.revision,
|
||||
@@ -115,7 +120,7 @@ def shard_identity_from_native_report(inputs: NativeIdentityInputs) -> ShardIden
|
||||
tokenizer_revision=inputs.tokenizer_revision,
|
||||
architecture_adapter=recipe.architecture_adapter,
|
||||
backend_id=recipe.backend_id,
|
||||
runtime_version=recipe.runtime_version,
|
||||
runtime_version=runtime_version,
|
||||
boundary_schema_version=recipe.boundary_schema_version,
|
||||
protocol_schema_version=recipe.protocol_schema_version,
|
||||
recipe_id=recipe.recipe_id,
|
||||
|
||||
@@ -125,6 +125,10 @@ _AXIS_MISMATCH: Mapping[str, str] = {
|
||||
}
|
||||
|
||||
_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;
|
||||
# it is just as true of a tokenizer.
|
||||
@@ -194,6 +198,17 @@ def _require_pin(value: Any, what: str) -> str:
|
||||
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]:
|
||||
if not isinstance(value, Mapping):
|
||||
raise RecipeIdentityError(
|
||||
@@ -405,7 +420,7 @@ class RuntimeRecipe:
|
||||
else:
|
||||
_require_text(value, f"recipe.{axis}")
|
||||
_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_version, "recipe.recipe_version")
|
||||
_require_text(self.catalogue_version, "recipe.catalogue_version")
|
||||
|
||||
@@ -70,6 +70,10 @@ STATUS_CERTIFIED = "certified"
|
||||
MIN_CERTIFYING_NODES = 2
|
||||
|
||||
_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"})
|
||||
|
||||
|
||||
@@ -128,6 +132,17 @@ def _pin(value: Any, what: str) -> str:
|
||||
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]:
|
||||
if not isinstance(value, Mapping):
|
||||
raise RecipeIdentityError(f"{what!r} must be a JSON object")
|
||||
@@ -333,7 +348,7 @@ def parse_identity(data: Any) -> PresentedIdentity:
|
||||
else:
|
||||
axes[axis] = _text(value, f"recipe.{axis}")
|
||||
_pin(axes["tokenizer_revision"], "recipe.tokenizer_revision")
|
||||
_pin(axes["runtime_version"], "recipe.runtime_version")
|
||||
_runtime_pin(axes["runtime_version"], axes["backend_id"])
|
||||
|
||||
identity = PresentedIdentity(
|
||||
artifact_id=_text(artifact.get("artifact_id"), "artifact.artifact_id"),
|
||||
|
||||
@@ -49,7 +49,7 @@ _RECIPE = RuntimeRecipe(
|
||||
tokenizer_revision="0123456789abcdef",
|
||||
architecture_adapter="llama/range-v1",
|
||||
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_version="1",
|
||||
catalogue_version="2026.07.1",
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
"model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b",
|
||||
"recipe_id": "example-gguf",
|
||||
"recipe_version": "1",
|
||||
"runtime_recipe_digest": "9b14d70b0835a6428457e4888d453649dd0d2e41fc8ac9d84d232c8c237e68fa"
|
||||
"runtime_recipe_digest": "63001e0efeada5b97f2f3562562dc0fd2d9bd8904dc6b7b99a71d98f3e938bd0"
|
||||
},
|
||||
"fingerprint_proto_hex": "0a40386130663433643661613439643737383334626462343762636165396634326338383662376363666530616330313439333262326132623338363937613437621240396231346437306230383335613634323834353765343838386434353336343964643064326534316663386163396438346432333263386332333765363866611a0c6578616d706c652d676775662201312a09323032362e30372e31",
|
||||
"fingerprint_proto_hex": "0a40386130663433643661613439643737383334626462343762636165396634326338383662376363666530616330313439333262326132623338363937613437621240363330303165306566656164613562393766326633353632353632646330666432643962643839303464633662376239396137316439386633653933386264301a0c6578616d706c652d676775662201312a09323032362e30372e31",
|
||||
"identity": {
|
||||
"artifact": {
|
||||
"architecture": "dense-llama",
|
||||
@@ -26,7 +26,7 @@
|
||||
"model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b",
|
||||
"recipe_id": "example-gguf",
|
||||
"recipe_version": "1",
|
||||
"runtime_recipe_digest": "9b14d70b0835a6428457e4888d453649dd0d2e41fc8ac9d84d232c8c237e68fa"
|
||||
"runtime_recipe_digest": "63001e0efeada5b97f2f3562562dc0fd2d9bd8904dc6b7b99a71d98f3e938bd0"
|
||||
},
|
||||
"recipe": {
|
||||
"activation_dtype": "bfloat16",
|
||||
@@ -40,7 +40,7 @@
|
||||
"protocol_schema_version": 1,
|
||||
"recipe_id": "example-gguf",
|
||||
"recipe_version": "1",
|
||||
"runtime_version": "llama.cpp@deadbeef+meshnet.1",
|
||||
"runtime_version": "llama.cpp@dddddddddddddddddddddddddddddddddddddddd+patchstack.eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
"tokenizer_revision": "0123456789abcdef",
|
||||
"weight_quantization": "Q4_K_M"
|
||||
},
|
||||
@@ -58,9 +58,9 @@
|
||||
"model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b",
|
||||
"recipe_id": "example-gguf",
|
||||
"recipe_version": "1",
|
||||
"runtime_recipe_digest": "9b14d70b0835a6428457e4888d453649dd0d2e41fc8ac9d84d232c8c237e68fa"
|
||||
"runtime_recipe_digest": "63001e0efeada5b97f2f3562562dc0fd2d9bd8904dc6b7b99a71d98f3e938bd0"
|
||||
},
|
||||
"fingerprint_proto_hex": "0a40386130663433643661613439643737383334626462343762636165396634326338383662376363666530616330313439333262326132623338363937613437621240396231346437306230383335613634323834353765343838386434353336343964643064326534316663386163396438346432333263386332333765363866611a0c6578616d706c652d676775662201312a09323032362e30372e31",
|
||||
"fingerprint_proto_hex": "0a40386130663433643661613439643737383334626462343762636165396634326338383662376363666530616330313439333262326132623338363937613437621240363330303165306566656164613562393766326633353632353632646330666432643962643839303464633662376239396137316439386633653933386264301a0c6578616d706c652d676775662201312a09323032362e30372e31",
|
||||
"identity": {
|
||||
"artifact": {
|
||||
"architecture": "dense-llama",
|
||||
@@ -80,7 +80,7 @@
|
||||
"model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b",
|
||||
"recipe_id": "example-gguf",
|
||||
"recipe_version": "1",
|
||||
"runtime_recipe_digest": "9b14d70b0835a6428457e4888d453649dd0d2e41fc8ac9d84d232c8c237e68fa"
|
||||
"runtime_recipe_digest": "63001e0efeada5b97f2f3562562dc0fd2d9bd8904dc6b7b99a71d98f3e938bd0"
|
||||
},
|
||||
"recipe": {
|
||||
"activation_dtype": "bfloat16",
|
||||
@@ -94,7 +94,7 @@
|
||||
"protocol_schema_version": 1,
|
||||
"recipe_id": "example-gguf",
|
||||
"recipe_version": "1",
|
||||
"runtime_version": "llama.cpp@deadbeef+meshnet.1",
|
||||
"runtime_version": "llama.cpp@dddddddddddddddddddddddddddddddddddddddd+patchstack.eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
"tokenizer_revision": "0123456789abcdef",
|
||||
"weight_quantization": "Q4_K_M"
|
||||
},
|
||||
|
||||
@@ -15,6 +15,7 @@ from meshnet_node.native_backend import (
|
||||
shard_identity_from_native_report,
|
||||
)
|
||||
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_tracker.capability import STATE_UNCERTIFIED, evaluate_report
|
||||
|
||||
@@ -42,7 +43,6 @@ def _inputs(**changes: object) -> NativeIdentityInputs:
|
||||
kv_layout="llama-kv-v1",
|
||||
architecture_adapter="dense-llama-v1",
|
||||
backend_id="llama-cpp",
|
||||
runtime_version="llama.cpp:e920c523",
|
||||
recipe_id="native",
|
||||
recipe_version="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.artifact.architecture == "llama"
|
||||
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():
|
||||
|
||||
@@ -42,7 +42,7 @@ def _recipe(**changes: object) -> RuntimeRecipe:
|
||||
"tokenizer_revision": "0123456789abcdef",
|
||||
"architecture_adapter": "llama/range-v1",
|
||||
"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_version": "1",
|
||||
"catalogue_version": "2026.07.1",
|
||||
@@ -276,3 +276,37 @@ def test_tracker_rejects_a_moving_runtime_version():
|
||||
doc.pop("fingerprint", None)
|
||||
with pytest.raises(TrackerRecipeIdentityError, match="moving reference"):
|
||||
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)
|
||||
|
||||
@@ -65,7 +65,7 @@ def _recipe(**changes: object) -> RuntimeRecipe:
|
||||
"tokenizer_revision": "0123456789abcdef",
|
||||
"architecture_adapter": "llama/range-v1",
|
||||
"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_version": "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"),
|
||||
("architecture_adapter", "llama/range-v2"),
|
||||
("backend_id", "other-backend"),
|
||||
("runtime_version", "llama.cpp@other+meshnet.1"),
|
||||
("runtime_version", "llama.cpp@" + "c" * 40 + "+patchstack." + "b" * 64),
|
||||
("boundary_schema_version", 2),
|
||||
("protocol_schema_version", 2),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user