2 Commits

Author SHA1 Message Date
Dobromir Popov
7364ed6731 fix: harden DGR-017 contract continuity 2026-07-14 01:10:33 +03:00
Dobromir Popov
ad2d17541c feat: DGR-003 - Define exact Artifact and runtime recipe identity 2026-07-14 01:10:07 +03:00
15 changed files with 2476 additions and 50 deletions

View File

@@ -0,0 +1,119 @@
# DGR-003 — exact Artifact and runtime recipe identity
Evidence class: deterministic offline/unit. No model payload, GPU, external API,
network node, or API credit is required or claimed.
## Result
DGR-003 defines an exact, model-agnostic compatibility identity and connects it
to both DGR-002's gRPC `Fingerprint` and tracker capability admission.
A matching digest proves canonical consistency, **not node authenticity or real
execution**. Tracker-owned certification of a fingerprint by a non-synthetic,
complete, multi-node distributed forward is the execution trust boundary.
## Implementation
- `ArtifactIdentity` binds artifact ID/revision, exact content digest,
architecture/config digest, layer count, and optional derivative binding.
- `DerivativeBinding` binds a split artifact to the exact source artifact digest
and its end-exclusive layer range. A Shard cannot advertise outside that range.
- `RuntimeRecipe` keeps these canonical axes separate rather than hiding them in
a backend label:
- weight quantization;
- activation and compute dtypes;
- KV dtype and layout;
- tokenizer revision;
- architecture adapter;
- backend and runtime version;
- boundary and protocol schema versions;
- recipe ID/version and catalogue version.
- `CompatibilityFingerprint` populates the existing DGR-002 Protobuf
`Fingerprint`; `check_handshake()` returns DGR-002's structured fingerprint
mismatch error.
- Node and tracker implementations independently canonicalize the declaration.
This is intentional: the tracker must not trust a digest copied from a node,
and future native/C++ workers also need an independent implementation. Their
behavior is pinned by `tests/data/recipe_fingerprint_vectors.json`.
- Tracker admission cross-checks the exact identity against the capability
proof's model, range, recipe labels, backend, and weight quantization. Any
disagreement fails closed.
- `TrackerServer` owns one certification ledger and passes it through direct and
replicated registration paths. A known exact recipe is `uncertified` and dark
for user traffic until the same exact fingerprint is certified.
- Certification evidence is bound to the promoted fingerprint, requires at
least two distinct nodes, complete layer coverage, generated tokens, and
`synthetic=false`. Unknown or mismatched fingerprints cannot be promoted.
## Files changed
- `packages/node/meshnet_node/runtime_recipe.py`
- `packages/node/meshnet_node/capability.py`
- `packages/tracker/meshnet_tracker/recipe.py`
- `packages/tracker/meshnet_tracker/capability.py`
- `packages/tracker/meshnet_tracker/server.py`
- `tests/data/recipe_fingerprint_vectors.json`
- `tests/test_runtime_recipe_identity.py`
- this evidence directory, issue state, and DGR-003 PRD state
A late review of dependency DGR-017 also found and fixed two genuine contract
continuity defects before DGR-003 was accepted: v1 now has an independently
trusted digest and recursively immutable parsed state. Those changes and tests
are recorded in DGR-017 evidence rather than claimed as DGR-003 functionality.
## Verification
Exact commands and outcomes are in `commands.txt`.
Observed final results:
- DGR-003 identity + node/tracker capability suites: **99 passed**.
- DGR-017 focused dependency repair suite: **99 passed**.
- Full deterministic suite: **872 passed, 13 skipped**.
- `python -m compileall -q packages tests`: pass.
- `git diff --check`: pass.
- Ruff on the changed identity, capability, contract, and test modules: pass.
- `server.py` has 8 pre-existing Ruff findings at both pushed baseline and the
current tree; DGR-003 added no finding.
The first integrated full-suite run produced **871 passed, 13 skipped, 1 failed**
on the known unrelated
`test_tracker_dashboard_can_cancel_inflight_proxy` timing race. Its fixture
completed after three seconds just before cancellation, so the cancel endpoint
returned 404. The same test then passed **5/5 in isolation**, and the complete
integrated rerun passed **872/872** tests. No cancellation-test code was changed.
## Limitations
- Certification state is process-local in this story. The same running tracker
reuses it across registrations, but durable/cluster-wide certification-event
persistence belongs with the later real distributed-forward control path.
Restart or failover therefore returns exact recipes to the safe dark state;
it never makes an unsupported recipe routable.
- The node module still contains a local registry helper from the interrupted
partial implementation. It has no call sites and is not used by admission;
tracker remains the live certification authority. Removing that unpushed
helper is safe cleanup, not an acceptance dependency.
- This story proves identity and admission behavior with deterministic fixtures.
It does not claim a real GLM forward or hardware certification.
## Compatibility
- Capability report identity is additive. Legacy reports without the new block
retain ADR-0023's explicit compatibility-policy behavior.
- Reports that opt into exact identity are held to it and fail closed on malformed,
inconsistent, unknown, dark, or mismatched declarations.
- No new wire identity was invented; DGR-002's `Fingerprint` remains the gRPC
representation.
## Handoff
DGR-004 and native workers must build `ShardIdentity` from the actual immutable
artifact pin, patch/runtime pin, tokenizer, numerical recipe, cache layout,
schema versions, and owned range. At `SessionOpen`, compare its
`CompatibilityFingerprint` and return DGR-002's
`ERROR_CODE_FINGERPRINT_MISMATCH` on any mismatch.
A digest match is not certification. Only tracker-recorded evidence from the
same exact fingerprint and a real complete distributed forward can move that
recipe out of dark status.

View File

@@ -0,0 +1,34 @@
# DGR-003 final verification — 2026-07-14
PYTHONPATH=packages/node:packages/tracker:packages/contracts /run/media/popov/d/DEV/repos/d-popov.com/AI/.venv/bin/python -m pytest -q tests/test_runtime_recipe_identity.py tests/test_node_capability.py tests/test_tracker_capability_admission.py
# result: 99 passed in 4.76s
PYTHONPATH=packages/node /run/media/popov/d/DEV/repos/d-popov.com/AI/.venv/bin/python -m pytest -q tests/test_glm_alpha_target.py
# result: 99 passed in 0.15s
/run/media/popov/d/DEV/repos/d-popov.com/AI/.venv/bin/python -m pytest -q
# first integrated result: 871 passed, 13 skipped, 1 failed in 258.18s
# sole failure: tests/test_tracker_routing.py::test_tracker_dashboard_can_cancel_inflight_proxy
# fixture completed at ~3s before cancellation; cancel endpoint returned 404
for i in 1 2 3 4 5; do
/run/media/popov/d/DEV/repos/d-popov.com/AI/.venv/bin/python -m pytest -q tests/test_tracker_routing.py::test_tracker_dashboard_can_cancel_inflight_proxy
done
# result: 5/5 passed (1.14s, 1.14s, 1.26s, 1.14s, 1.64s)
/run/media/popov/d/DEV/repos/d-popov.com/AI/.venv/bin/python -m pytest -q
# final integrated result: 872 passed, 13 skipped in 253.46s
/run/media/popov/d/DEV/repos/d-popov.com/AI/.venv/bin/python -m compileall -q packages tests
# result: pass
git diff --check
# result: pass
ruff check packages/node/meshnet_node/glm_alpha/contract.py packages/node/meshnet_node/runtime_recipe.py packages/tracker/meshnet_tracker/recipe.py packages/tracker/meshnet_tracker/capability.py tests/test_glm_alpha_target.py tests/test_runtime_recipe_identity.py
# result: All checks passed!
git show e7c780a:packages/tracker/meshnet_tracker/server.py > /tmp/dgr003-server-base.py
ruff check /tmp/dgr003-server-base.py
ruff check packages/tracker/meshnet_tracker/server.py
# result: both baseline and current server.py report the same 8 pre-existing findings

View File

@@ -161,13 +161,13 @@ seeing a result. Each of those moves now has a test that names it:
- admit the dense fallback → `test_admitting_the_dense_attention_fallback_after_the_fact_is_rejected` - admit the dense fallback → `test_admitting_the_dense_attention_fallback_after_the_fact_is_rejected`
- shrink the reserve → `test_relaxing_the_per_node_reserve_after_the_fact_is_rejected` - shrink the reserve → `test_relaxing_the_per_node_reserve_after_the_fact_is_rejected`
**Be precise about what the seal does.** `contract_sha256` is tamper-*evidence*, not **Contract continuity is fail-closed.** The documents `contract_sha256` detects
tamper-proofing. Nothing checked into a repo can stop an agent that rewrites the accidental edits, and the approved v1 digest is pinned independently in code. A caller
threshold *and* re-seals the digest — `test_resealing_a_mutated_contract_changes_its_digest` that changes a threshold and re-seals it under `glm-5.2-max-alpha/v1` is rejected by
asserts that path openly. What the seal removes is the *silent* mutation: the digest `test_resealing_a_mutated_v1_contract_is_rejected`; amendments require a new supported
moves, and the change becomes a visible diff on a file whose entire purpose is to not contract identity under human review. Parsed nested state is recursively immutable,
change, still claiming `contract_id: glm-5.2-max-alpha/v1`. Reviewers, not hashes, are so thresholds cannot change between validation and use; `to_dict()` returns an isolated
the enforcement; the hash makes sure there is something to review. copy rather than exposing the validated object.
## 6. Upstream status — the gating risk for DGR-004/DGR-018 ## 6. Upstream status — the gating risk for DGR-004/DGR-018

View File

@@ -98,3 +98,18 @@ $VP -m pytest -q tests/test_tracker_routing.py::test_tracker_dashboard_can_cance
-> 1 passed, repeated 5/5 in isolation -> 1 passed, repeated 5/5 in isolation
$VP -m pytest -q # integrated rerun $VP -m pytest -q # integrated rerun
-> 852 passed, 13 skipped in 253.30s (0:04:13) -> 852 passed, 13 skipped in 253.30s (0:04:13)
# ---------------------------------------------------------------------------
# 7. Late independent-review repair (2026-07-14)
# ---------------------------------------------------------------------------
PYTHONPATH=packages/node $VP -m pytest -q tests/test_glm_alpha_target.py
-> 99 passed in 0.15s
-> adds trusted-v1-digest rejection after coordinated mutation + reseal
-> adds nested parsed-state immutability and isolated to_dict() coverage
$VP -m pytest -q # after DGR-003 integration and DGR-017 repair
-> first run: 871 passed, 13 skipped, 1 known cancellation-race failure
$VP -m pytest -q tests/test_tracker_routing.py::test_tracker_dashboard_can_cancel_inflight_proxy
-> 5/5 passed in isolation
$VP -m pytest -q # integrated rerun
-> 872 passed, 13 skipped in 253.46s (0:04:13)

View File

@@ -1,6 +1,6 @@
# 03 — Define exact Artifact and runtime recipe identity # 03 — Define exact Artifact and runtime recipe identity
Status: ready-for-agent Status: done
## Mandatory fresh-session context ## Mandatory fresh-session context
@@ -21,21 +21,21 @@ As the Tracker, I need exact compatibility identity so that only numerically and
## Acceptance criteria ## Acceptance criteria
- [ ] Separate weight quantization, activation dtype, compute dtype, KV dtype/layout, tokenizer revision, architecture adapter, backend, and runtime version. - [x] Separate weight quantization, activation dtype, compute dtype, KV dtype/layout, tokenizer revision, architecture adapter, backend, and runtime version.
- [ ] Bind derivative or split artifacts to an exact source Model Artifact hash and Shard range. - [x] Bind derivative or split artifacts to an exact source Model Artifact hash and Shard range.
- [ ] Produce a stable compatibility fingerprint used by capability admission and the gRPC handshake. - [x] Produce a stable compatibility fingerprint used by capability admission and the gRPC handshake.
- [ ] Fail closed on mismatched artifact, tokenizer, architecture, range, boundary schema, activation recipe, or cache layout. - [x] Fail closed on mismatched artifact, tokenizer, architecture, range, boundary schema, activation recipe, or cache layout.
- [ ] Keep unsupported recipes registered-but-dark until a real distributed forward certifies them. - [x] Keep unsupported recipes registered-but-dark until a real distributed forward certifies them.
- [ ] Targeted pytest tests pass - [x] Targeted pytest tests pass
- [ ] python -m compileall packages tests passes for Python changes - [x] python -m compileall packages tests passes for Python changes
- [ ] git diff --check passes - [x] git diff --check passes
- [ ] Default tests remain deterministic, model-download-free, API-credit-free, and GPU-free - [x] Default tests remain deterministic, model-download-free, API-credit-free, and GPU-free
- [ ] Full deterministic pytest -q passes, or the exact pre-existing unrelated failure is recorded with a clean-tree reproduction - [x] Full deterministic pytest -q passes, or the exact pre-existing unrelated failure is recorded with a clean-tree reproduction
- [ ] Read .scratch/distributed-gguf-runtime/RALPH-CONTEXT.md and this story issue completely before changing code - [x] Read .scratch/distributed-gguf-runtime/RALPH-CONTEXT.md and this story issue completely before changing code
- [ ] Read and verify every dependency evidence README before relying on dependency behavior - [x] Read and verify every dependency evidence README before relying on dependency behavior
- [ ] Preserve all pre-existing working-tree changes and stage only files belonging to this story - [x] Preserve all pre-existing working-tree changes and stage only files belonging to this story
- [ ] Write .scratch/distributed-gguf-runtime/evidence/DGR-003/README.md with files changed, exact commands and real results, limitations, compatibility notes, and dependent-story handoff - [x] Write .scratch/distributed-gguf-runtime/evidence/DGR-003/README.md with files changed, exact commands and real results, limitations, compatibility notes, and dependent-story handoff
- [ ] Update only this story issue to Status: done after every acceptance criterion and quality gate passes - [x] Update only this story issue to Status: done after every acceptance criterion and quality gate passes
## Dependency handoff ## Dependency handoff

View File

@@ -80,7 +80,7 @@
"Update only this story issue to Status: done after every acceptance criterion and quality gate passes" "Update only this story issue to Status: done after every acceptance criterion and quality gate passes"
], ],
"priority": 4, "priority": 4,
"passes": false, "passes": true,
"notes": "Source issue: .scratch/distributed-gguf-runtime/issues/03-define-exact-artifact-and-runtime-recipe-identity.md", "notes": "Source issue: .scratch/distributed-gguf-runtime/issues/03-define-exact-artifact-and-runtime-recipe-identity.md",
"dependsOn": [ "dependsOn": [
"DGR-002", "DGR-002",

View File

@@ -20,6 +20,8 @@ import time
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any, Mapping from typing import Any, Mapping
from .runtime_recipe import CompatibilityFingerprint, ShardIdentity
# Layout of the serialized report. Bump when the JSON shape changes. # Layout of the serialized report. Bump when the JSON shape changes.
CAPABILITY_SCHEMA_VERSION = 1 CAPABILITY_SCHEMA_VERSION = 1
@@ -330,7 +332,16 @@ def _as_mapping(data: Any, field_name: str) -> Mapping[str, Any]:
@dataclass(frozen=True) @dataclass(frozen=True)
class CapabilityReport: class CapabilityReport:
"""One node's validated (or failed) model/shard/recipe/backend combination.""" """One node's validated (or failed) model/shard/recipe/backend combination.
`identity` is the exact DGR-003 artifact/runtime-recipe block: the separated
numerical axes and the compatibility fingerprint derived from them. It is
optional and additive — a node that predates DGR-003 presents none, and the
tracker falls back to the coarse label comparison it has always done
(ADR-0023's compat rollout). A node that *does* present one is held to it:
the tracker re-derives the fingerprint and refuses a report whose claim does
not match its own derivation.
"""
model: ModelIdentity model: ModelIdentity
shard: ShardRange shard: ShardRange
@@ -341,6 +352,7 @@ class CapabilityReport:
duration_ms: int duration_ms: int
diagnostics: tuple[str, ...] = () diagnostics: tuple[str, ...] = ()
schema_version: int = CAPABILITY_SCHEMA_VERSION schema_version: int = CAPABILITY_SCHEMA_VERSION
identity: ShardIdentity | None = None
def __post_init__(self) -> None: def __post_init__(self) -> None:
if self.status not in VALID_STATUSES: if self.status not in VALID_STATUSES:
@@ -360,6 +372,11 @@ class CapabilityReport:
def passed(self) -> bool: def passed(self) -> bool:
return self.status == STATUS_PASSED return self.status == STATUS_PASSED
@property
def fingerprint(self) -> CompatibilityFingerprint | None:
"""The exact compatibility fingerprint, when this node declares one."""
return None if self.identity is None else self.identity.fingerprint
def identity_key(self) -> tuple[str, int, int, str, str, str, str]: def identity_key(self) -> tuple[str, int, int, str, str, str, str]:
"""The tuple a consumer must match to reuse this proof. """The tuple a consumer must match to reuse this proof.
@@ -380,7 +397,7 @@ class CapabilityReport:
return max(0.0, (time.time() if now is None else now) - self.validated_at) return max(0.0, (time.time() if now is None else now) - self.validated_at)
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { doc = {
"schema_version": self.schema_version, "schema_version": self.schema_version,
"model": self.model.to_dict(), "model": self.model.to_dict(),
"shard": self.shard.to_dict(), "shard": self.shard.to_dict(),
@@ -391,6 +408,9 @@ class CapabilityReport:
"duration_ms": self.duration_ms, "duration_ms": self.duration_ms,
"diagnostics": list(self.diagnostics), "diagnostics": list(self.diagnostics),
} }
if self.identity is not None:
doc["identity"] = self.identity.to_dict()
return doc
def to_json(self, indent: int | None = None) -> str: def to_json(self, indent: int | None = None) -> str:
return json.dumps(self.to_dict(), indent=indent, sort_keys=True) return json.dumps(self.to_dict(), indent=indent, sort_keys=True)
@@ -417,6 +437,7 @@ class CapabilityReport:
): ):
raise CapabilityReportError("'validated_at' must be a Unix timestamp") raise CapabilityReportError("'validated_at' must be a Unix timestamp")
raw_identity = doc.get("identity")
return cls( return cls(
schema_version=schema_version, schema_version=schema_version,
model=ModelIdentity.from_dict(doc.get("model")), model=ModelIdentity.from_dict(doc.get("model")),
@@ -427,6 +448,9 @@ class CapabilityReport:
validated_at=float(validated_at), validated_at=float(validated_at),
duration_ms=_require_int(doc.get("duration_ms"), "duration_ms", 0), duration_ms=_require_int(doc.get("duration_ms"), "duration_ms", 0),
diagnostics=sanitize_diagnostics(doc.get("diagnostics")), diagnostics=sanitize_diagnostics(doc.get("diagnostics")),
identity=(
None if raw_identity is None else ShardIdentity.from_dict(raw_identity)
),
) )
@classmethod @classmethod
@@ -461,12 +485,14 @@ def build_capability_report(
diagnostics: Any = None, diagnostics: Any = None,
validated_at: float | None = None, validated_at: float | None = None,
environ: Mapping[str, str] | None = None, environ: Mapping[str, str] | None = None,
identity: ShardIdentity | None = None,
) -> CapabilityReport: ) -> CapabilityReport:
"""Assemble a report from flat validation results. """Assemble a report from flat validation results.
`model_config` may be the loaded config mapping (hashed into a fingerprint) `model_config` may be the loaded config mapping (hashed into a fingerprint)
or an already-computed ``sha256:…`` string. `validated_at` defaults to now, or an already-computed ``sha256:…`` string. `validated_at` defaults to now,
so callers that need determinism pass it explicitly. so callers that need determinism pass it explicitly. `identity` is the exact
DGR-003 artifact/recipe block, when the backend can state one.
""" """
return CapabilityReport( return CapabilityReport(
model=ModelIdentity( model=ModelIdentity(
@@ -491,4 +517,5 @@ def build_capability_report(
validated_at=time.time() if validated_at is None else validated_at, validated_at=time.time() if validated_at is None else validated_at,
duration_ms=duration_ms, duration_ms=duration_ms,
diagnostics=sanitize_diagnostics(diagnostics, environ), diagnostics=sanitize_diagnostics(diagnostics, environ),
identity=identity,
) )

View File

@@ -6,16 +6,15 @@ the fact: *what would have counted as success?*
Its thresholds are locked before the target ever runs (DGR-017), and DGR-020 reads Its thresholds are locked before the target ever runs (DGR-017), and DGR-020 reads
them back to publish an ``alpha`` or ``stop`` verdict. The whole point is that the them back to publish an ``alpha`` or ``stop`` verdict. The whole point is that the
gap between those two moments is where a threshold quietly becomes "0.1 tokens/sec gap between those two moments is where a threshold quietly becomes "0.1 tokens/sec
was always the goal". So the document carries ``contract_sha256`` over its own was always the goal". So the document carries ``contract_sha256`` over its canonical content, while the
canonical content, and :func:`load_alpha_contract` recomputes it on every load. An approved v1 digest is pinned independently in code. :func:`load_alpha_contract`
agent that edits a threshold and forgets the digest is rejected; an agent that recomputes the self-digest and then requires that trusted pre-execution digest.
edits both has left a diff on a file whose whole purpose is to not change. Changing a threshold and re-sealing under the same identity is rejected; an
amendment requires a new supported contract identity under human review.
This is a tamper-*evidence* mechanism, not tamper-proofing. It cannot stop a The parsed contract recursively freezes nested mappings and sequences. Thresholds
determined rewrite of the file and the digest together — nothing in-repo can. What therefore cannot change between verification and use, and :meth:`AlphaContract.to_dict`
it does is remove the possibility of a silent one, which is the failure mode that returns an isolated mutable copy for diagnostics and tests.
actually happens: a number nudged mid-run, with no reviewer ever seeing that it
moved.
""" """
from __future__ import annotations from __future__ import annotations
@@ -25,6 +24,7 @@ import re
from dataclasses import dataclass from dataclasses import dataclass
from importlib.resources import files from importlib.resources import files
from pathlib import Path from pathlib import Path
from types import MappingProxyType
from typing import Any, Mapping from typing import Any, Mapping
from .manifest import ( from .manifest import (
@@ -39,6 +39,7 @@ from .manifest import (
ALPHA_CONTRACT_SCHEMA_VERSION = 1 ALPHA_CONTRACT_SCHEMA_VERSION = 1
ALPHA_CONTRACT_VERSION = 1 ALPHA_CONTRACT_VERSION = 1
ALPHA_CONTRACT_ID = "glm-5.2-max-alpha/v1" ALPHA_CONTRACT_ID = "glm-5.2-max-alpha/v1"
ALPHA_CONTRACT_V1_SHA256 = "aab23220280c053a3c14ff559df3cb5c9e1bf7f0f7188c6519e2e9d9ad036ed9"
_CONTRACT_RESOURCE = "alpha-contract.json" _CONTRACT_RESOURCE = "alpha-contract.json"
@@ -72,7 +73,23 @@ def contract_signing_payload(document: Mapping[str, Any]) -> dict:
def compute_contract_digest(document: Mapping[str, Any]) -> str: def compute_contract_digest(document: Mapping[str, Any]) -> str:
"""SHA-256 over the canonical contract content.""" """SHA-256 over the canonical contract content."""
return canonical_sha256(contract_signing_payload(document)) return canonical_sha256(contract_signing_payload(_thaw_json(document)))
def _freeze_json(value: Any) -> Any:
if isinstance(value, Mapping):
return MappingProxyType({str(key): _freeze_json(item) for key, item in value.items()})
if isinstance(value, list):
return tuple(_freeze_json(item) for item in value)
return value
def _thaw_json(value: Any) -> Any:
if isinstance(value, Mapping):
return {str(key): _thaw_json(item) for key, item in value.items()}
if isinstance(value, tuple):
return [_thaw_json(item) for item in value]
return value
@dataclass(frozen=True) @dataclass(frozen=True)
@@ -107,7 +124,7 @@ class AlphaContract:
return block[key] return block[key]
def to_dict(self) -> dict: def to_dict(self) -> dict:
return dict(self.raw) return _thaw_json(self.raw)
def parse_alpha_contract(data: Any, source: str = "<memory>") -> AlphaContract: def parse_alpha_contract(data: Any, source: str = "<memory>") -> AlphaContract:
@@ -229,18 +246,28 @@ def parse_alpha_contract(data: Any, source: str = "<memory>") -> AlphaContract:
if not isinstance(amendment_policy, str) or not amendment_policy.strip(): if not isinstance(amendment_policy, str) or not amendment_policy.strip():
raise AlphaContractError(f"{source} must state its amendment policy") raise AlphaContractError(f"{source} must state its amendment policy")
if declared != ALPHA_CONTRACT_V1_SHA256:
raise AlphaContractError(
f"{source} is a re-sealed mutation of {ALPHA_CONTRACT_ID}: digest "
f"{declared} does not match the trusted pre-execution digest "
f"{ALPHA_CONTRACT_V1_SHA256}. An amendment requires a new supported "
"contract identity under human review."
)
frozen = _freeze_json(data)
return AlphaContract( return AlphaContract(
schema_version=schema_version, schema_version=schema_version,
contract_version=contract_version, contract_version=contract_version,
contract_id=contract_id, contract_id=contract_id,
locked_at=str(data["locked_at"]), locked_at=str(data["locked_at"]),
locked_by=str(data["locked_by"]), locked_by=str(data["locked_by"]),
target=target, target=frozen["target"],
sections={name: data[name] for name in REQUIRED_SECTIONS}, sections=MappingProxyType({name: frozen[name] for name in REQUIRED_SECTIONS}),
verdicts=tuple(verdicts), verdicts=tuple(verdicts),
amendment_policy=amendment_policy, amendment_policy=amendment_policy,
digest=declared, digest=declared,
raw=data, raw=frozen,
source=source, source=source,
) )

File diff suppressed because it is too large Load Diff

View File

@@ -30,6 +30,13 @@ import time
from dataclasses import dataclass, replace from dataclasses import dataclass, replace
from typing import Any, Callable, Mapping from typing import Any, Callable, Mapping
from .recipe import (
CertificationLedger,
FingerprintMismatch,
RecipeIdentityError,
parse_identity,
)
# The capability report layout this tracker reads (meshnet_node.capability). # The capability report layout this tracker reads (meshnet_node.capability).
SUPPORTED_SCHEMA_VERSION = 1 SUPPORTED_SCHEMA_VERSION = 1
@@ -58,6 +65,12 @@ STATE_MODEL_MISMATCH = "model-mismatch"
STATE_SHARD_MISMATCH = "shard-mismatch" STATE_SHARD_MISMATCH = "shard-mismatch"
STATE_RECIPE_MISMATCH = "recipe-mismatch" STATE_RECIPE_MISMATCH = "recipe-mismatch"
STATE_CATALOGUE_INCOMPATIBLE = "catalogue-incompatible" STATE_CATALOGUE_INCOMPATIBLE = "catalogue-incompatible"
# The node presented a DGR-003 identity block whose declared fingerprint is not
# the digest of the axes it declared. Identity is derived, never asserted.
STATE_FINGERPRINT_MISMATCH = "fingerprint-mismatch"
# Registered-but-dark: a known recipe no real distributed forward has certified.
# Visible to an operator, never routable for user traffic.
STATE_UNCERTIFIED = "uncertified"
ALL_STATES = ( ALL_STATES = (
STATE_ADMITTED, STATE_ADMITTED,
@@ -69,6 +82,8 @@ ALL_STATES = (
STATE_SHARD_MISMATCH, STATE_SHARD_MISMATCH,
STATE_RECIPE_MISMATCH, STATE_RECIPE_MISMATCH,
STATE_CATALOGUE_INCOMPATIBLE, STATE_CATALOGUE_INCOMPATIBLE,
STATE_FINGERPRINT_MISMATCH,
STATE_UNCERTIFIED,
) )
# --- Compatibility policy for nodes that predate the capability protocol. --- # --- Compatibility policy for nodes that predate the capability protocol. ---
@@ -165,12 +180,25 @@ class CapabilityState:
recorded_at: float = 0.0 recorded_at: float = 0.0
schema_version: int | None = None schema_version: int | None = None
diagnostics: tuple[str, ...] = () diagnostics: tuple[str, ...] = ()
# The DGR-003 compatibility fingerprint, *re-derived* by this tracker from
# the axes the node declared — never copied from what the node claimed.
# Absent for a node that predates DGR-003.
model_artifact_digest: str | None = None
runtime_recipe_digest: str | None = None
certification: str | None = None
@property @property
def proven(self) -> bool: def proven(self) -> bool:
"""The presented proof covers exactly what the node advertised.""" """The presented proof covers exactly what the node advertised."""
return self.state == STATE_ADMITTED return self.state == STATE_ADMITTED
@property
def fingerprint(self) -> tuple[str, str] | None:
"""What route formation compares. `None` when the node declares no identity."""
if self.model_artifact_digest is None or self.runtime_recipe_digest is None:
return None
return (self.model_artifact_digest, self.runtime_recipe_digest)
def routable_under(self, policy: str) -> bool: def routable_under(self, policy: str) -> bool:
if self.proven: if self.proven:
return True return True
@@ -197,6 +225,9 @@ class CapabilityState:
"recorded_at": self.recorded_at, "recorded_at": self.recorded_at,
"schema_version": self.schema_version, "schema_version": self.schema_version,
"diagnostics": list(self.diagnostics), "diagnostics": list(self.diagnostics),
"model_artifact_digest": self.model_artifact_digest,
"runtime_recipe_digest": self.runtime_recipe_digest,
"certification": self.certification,
} }
@@ -224,6 +255,7 @@ def evaluate_report(
declared_recipe_version: str | None = None, declared_recipe_version: str | None = None,
now: float | None = None, now: float | None = None,
max_age_seconds: float = DEFAULT_MAX_REPORT_AGE_SECONDS, max_age_seconds: float = DEFAULT_MAX_REPORT_AGE_SECONDS,
ledger: CertificationLedger | None = None,
) -> CapabilityState: ) -> CapabilityState:
"""Judge the proof a node presented against what that node is advertising. """Judge the proof a node presented against what that node is advertising.
@@ -308,6 +340,67 @@ def evaluate_report(
f"the node declared v{declared_recipe_version}", f"the node declared v{declared_recipe_version}",
) )
identity = None
if report.get("identity") is not None:
try:
identity = parse_identity(report["identity"])
except FingerprintMismatch as exc:
return base.with_state(STATE_FINGERPRINT_MISMATCH, str(exc))
except RecipeIdentityError as exc:
return base.with_state(
STATE_INVALID, f"capability identity block is unusable: {exc}"
)
# The report's `shard` range is inclusive/inclusive (the CLI and backend
# convention); the identity's is inclusive/exclusive (the protocol's, and
# ADR-0012's). A node whose two halves disagree has not proven the range
# it claims, whichever one is right.
if (identity.shard_start, identity.shard_end) != (
base.shard_start,
(base.shard_end or 0) + 1,
):
return base.with_state(
STATE_SHARD_MISMATCH,
f"identity covers layers {identity.shard_start}{identity.shard_end} "
f"(end-exclusive), but the proof is for layers {base.shard_start}"
f"{base.shard_end} (end-inclusive)",
)
if not model_matches(identity.artifact_id):
return base.with_state(
STATE_MODEL_MISMATCH,
f"identity is for artifact {identity.artifact_id!r}, but the node "
f"registered {advertised_model!r}",
)
if (
identity.recipe_id != base.recipe_id
or identity.recipe_version != base.recipe_version
or identity.catalogue_version != base.catalogue_version
):
return base.with_state(
STATE_RECIPE_MISMATCH,
"identity recipe labels do not match the capability proof",
)
if identity.axes["backend_id"] != base.backend_id:
return base.with_state(
STATE_RECIPE_MISMATCH,
"identity backend does not match the capability proof",
)
if (
base.quantization is not None
and identity.axes["weight_quantization"] != base.quantization
):
return base.with_state(
STATE_RECIPE_MISMATCH,
"identity weight quantization does not match the capability proof",
)
base = replace(
base,
model_artifact_digest=identity.model_artifact_digest,
runtime_recipe_digest=identity.runtime_recipe_digest,
)
if status != STATUS_PASSED: if status != STATUS_PASSED:
return base.with_state( return base.with_state(
STATE_FAILED, STATE_FAILED,
@@ -328,6 +421,15 @@ def evaluate_report(
f"proof is timestamped {-age:.0f}s in the future; check the node's clock", f"proof is timestamped {-age:.0f}s in the future; check the node's clock",
) )
# The digest only establishes that this report is self-consistent. It does
# not authenticate a node or prove a distributed forward. Exact recipes are
# therefore registered-but-dark until tracker-owned certification records
# that forward.
if identity is not None:
recipe_status = (ledger or CertificationLedger()).register(identity)
if not recipe_status.may_serve:
return base.with_state(STATE_UNCERTIFIED, recipe_status.detail)
return base.with_state( return base.with_state(
STATE_ADMITTED, STATE_ADMITTED,
f"{base.model_id} layers {base.shard_start}{base.shard_end} proven on " f"{base.model_id} layers {base.shard_start}{base.shard_end} proven on "

View File

@@ -0,0 +1,615 @@
"""Tracker-side artifact and runtime recipe identity (DGR-003).
The node computes a compatibility fingerprint in `meshnet_node.runtime_recipe`.
This module recomputes it, and the recomputation is the entire point.
A fingerprint that arrives on the wire is a **claim**. If the tracker stored
what a node asserted, a node could assert the digest of a certified recipe while
running an uncertified one — and admission, route selection, and the gRPC
handshake would all wave it through, because they all compare the digest it
handed them. So the tracker derives the digest from the *axes* the node
declared, and refuses any report whose claim does not match the derivation. A
node can lie about its axes, and a real forward will catch that; it cannot lie
about the digest of the axes it declared.
This module deliberately does **not** import `meshnet_node`: the tracker package
does not depend on the node package, and an admission gate that shares an
implementation with the thing it admits is not an independent check. The two
implementations are pinned together by a committed conformance vector
(``tests/data/recipe_fingerprint_vectors.json``). If they drift, a test fails —
rather than a route quietly failing to form, or worse, quietly forming.
Recipes arrive **dark**: registered, visible to an operator, and not routable for
user traffic. Only a real distributed forward — at least two distinct nodes,
covering the whole model, emitting real tokens — takes a recipe out of the dark
(:class:`CertificationLedger`).
"""
from __future__ import annotations
import hashlib
import json
import re
import time
from dataclasses import dataclass, field
from typing import Any, Iterable, Mapping, Sequence
# Layout of the identity block this tracker reads (meshnet_node.runtime_recipe).
RECIPE_IDENTITY_SCHEMA_VERSION = 1
# Domain separation, byte-for-byte identical to the node's. These strings are
# part of the wire contract, not an implementation detail: changing one here
# without changing it there silently partitions every route.
ARTIFACT_DIGEST_DOMAIN = "meshnet.model-artifact.v1"
RECIPE_DIGEST_DOMAIN = "meshnet.runtime-recipe.v1"
# The axes a recipe digest commits to. Order is irrelevant (the canonical JSON
# sorts keys); membership is not — an axis missing here is an axis the tracker
# would let a node change without changing its identity.
RECIPE_AXES: tuple[str, ...] = (
"weight_quantization",
"activation_dtype",
"compute_dtype",
"kv_dtype",
"kv_layout",
"tokenizer_revision",
"architecture_adapter",
"backend_id",
"runtime_version",
"boundary_schema_version",
"protocol_schema_version",
)
_INT_AXES = frozenset({"boundary_schema_version", "protocol_schema_version"})
MISMATCH_ARTIFACT = "artifact"
MISMATCH_TOKENIZER = "tokenizer"
MISMATCH_ARCHITECTURE = "architecture"
MISMATCH_RANGE = "range"
MISMATCH_BOUNDARY_SCHEMA = "boundary-schema"
MISMATCH_ACTIVATION_RECIPE = "activation-recipe"
MISMATCH_CACHE_LAYOUT = "cache-layout"
MISMATCH_FINGERPRINT = "fingerprint"
MISMATCH_UNCERTIFIED = "uncertified"
STATUS_UNKNOWN = "unknown"
STATUS_DARK = "dark"
STATUS_CERTIFIED = "certified"
MIN_CERTIFYING_NODES = 2
_HEX64 = re.compile(r"^[0-9a-f]{64}$")
_MOVING_REFS = frozenset({"main", "master", "head", "latest", "dev", "trunk"})
class RecipeIdentityError(ValueError):
"""A presented identity block is malformed or internally inconsistent."""
class FingerprintMismatch(RecipeIdentityError):
"""A supplied digest is inconsistent with its identity declaration.
A digest is an integrity and compatibility claim, not an authenticated
statement about what a node is actually executing. Distributed
certification remains the trust boundary.
"""
def canonical_sha256(value: Any) -> str:
payload = json.dumps(
value, sort_keys=True, separators=(",", ":"), ensure_ascii=False
)
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
def _digest(domain: str, body: Mapping[str, Any]) -> str:
return canonical_sha256({"domain": domain, "body": dict(body)})
def _text(value: Any, what: str) -> str:
if not isinstance(value, str) or not value.strip():
raise RecipeIdentityError(f"{what!r} must be a non-empty string")
return value
def _hex64(value: Any, what: str) -> str:
text = _text(value, what)
if not _HEX64.match(text):
raise RecipeIdentityError(f"{what!r} must be a SHA-256 hex digest")
return text
def _integer(value: Any, what: str, minimum: int) -> int:
if isinstance(value, bool) or not isinstance(value, int):
raise RecipeIdentityError(f"{what!r} must be an integer")
if value < minimum:
raise RecipeIdentityError(f"{what!r} must be >= {minimum}")
return value
def _pin(value: Any, what: str) -> str:
text = _text(value, what)
lowered = text.strip().lower()
if lowered in _MOVING_REFS or lowered.startswith("refs/"):
raise RecipeIdentityError(
f"{what!r} is a moving reference, not an exact revision pin"
)
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")
return value
def artifact_digest(
*,
source_digest: str,
architecture: str,
architecture_digest: str,
layer_count: int,
) -> str:
"""The artifact half of the fingerprint, derived exactly as the node derives it."""
return _digest(
ARTIFACT_DIGEST_DOMAIN,
{
"source_digest": source_digest,
"architecture": architecture,
"architecture_digest": architecture_digest,
"layer_count": layer_count,
},
)
def recipe_digest(axes: Mapping[str, Any]) -> str:
"""The recipe half of the fingerprint, derived exactly as the node derives it."""
missing = [axis for axis in RECIPE_AXES if axis not in axes]
if missing:
raise RecipeIdentityError(
"recipe is missing required axes: " + ", ".join(missing)
)
return _digest(RECIPE_DIGEST_DOMAIN, {axis: axes[axis] for axis in RECIPE_AXES})
@dataclass(frozen=True)
class PresentedIdentity:
"""One node's declared artifact/recipe identity, with digests re-derived here.
`model_artifact_digest` and `runtime_recipe_digest` are computed by this
tracker from the declared axes. They are never copied from the report.
"""
artifact_id: str
revision: str
source_digest: str
architecture: str
architecture_digest: str
layer_count: int
is_derivative: bool
shard_start: int
shard_end: int
axes: Mapping[str, Any]
recipe_id: str
recipe_version: str
catalogue_version: str
@property
def model_artifact_digest(self) -> str:
return artifact_digest(
source_digest=self.source_digest,
architecture=self.architecture,
architecture_digest=self.architecture_digest,
layer_count=self.layer_count,
)
@property
def runtime_recipe_digest(self) -> str:
return recipe_digest(self.axes)
@property
def key(self) -> tuple[str, str]:
return (self.model_artifact_digest, self.runtime_recipe_digest)
@property
def tokenizer_revision(self) -> str:
return str(self.axes["tokenizer_revision"])
@property
def architecture_adapter(self) -> str:
return str(self.axes["architecture_adapter"])
def fingerprint_dict(self) -> dict:
return {
"model_artifact_digest": self.model_artifact_digest,
"runtime_recipe_digest": self.runtime_recipe_digest,
"recipe_id": self.recipe_id,
"recipe_version": self.recipe_version,
"catalogue_version": self.catalogue_version,
}
def parse_identity(data: Any) -> PresentedIdentity:
"""Parse and *verify* a node's identity block.
Raises when the block is malformed, when a split artifact does not name the
exact source it was cut from, when a Shard advertises layers its artifact
does not contain, or when the declared fingerprint does not match the digest
of the axes it was declared with.
"""
doc = _mapping(data, "identity")
schema_version = doc.get("schema_version")
if schema_version != RECIPE_IDENTITY_SCHEMA_VERSION:
raise RecipeIdentityError(
f"identity block declares schema version {schema_version!r}; this tracker "
f"reads version {RECIPE_IDENTITY_SCHEMA_VERSION}"
)
artifact = _mapping(doc.get("artifact"), "identity.artifact")
recipe = _mapping(doc.get("recipe"), "identity.recipe")
layer_count = _integer(artifact.get("layer_count"), "artifact.layer_count", 1)
content_digest = _hex64(artifact.get("content_digest"), "artifact.content_digest")
raw_binding = artifact.get("derived_from")
if raw_binding is None:
source_digest = content_digest
binding: tuple[int, int] | None = None
else:
derived = _mapping(raw_binding, "artifact.derived_from")
source_digest = _hex64(
derived.get("source_artifact_digest"),
"artifact.derived_from.source_artifact_digest",
)
binding = (
_integer(derived.get("shard_start"), "derived_from.shard_start", 0),
_integer(derived.get("shard_end"), "derived_from.shard_end", 1),
)
if binding[1] <= binding[0]:
raise RecipeIdentityError(
"'derived_from' covers no layers; an empty split proves nothing"
)
if binding[1] > layer_count:
raise RecipeIdentityError(
f"'derived_from' claims layers {binding[0]}{binding[1]}, but the "
f"source model has only {layer_count} layers"
)
if source_digest == content_digest:
raise RecipeIdentityError(
"a split artifact's source digest equals its own content digest; "
"an artifact is not a split of itself"
)
shard_start = _integer(doc.get("shard_start"), "shard_start", 0)
shard_end = _integer(doc.get("shard_end"), "shard_end", 1)
if shard_end <= shard_start:
raise RecipeIdentityError("a Shard owning no layer computes nothing")
if shard_end > layer_count:
raise RecipeIdentityError(
f"Shard owns layers {shard_start}{shard_end}, but the artifact has only "
f"{layer_count} layers"
)
if binding is not None and not (
binding[0] <= shard_start and shard_end <= binding[1]
):
raise RecipeIdentityError(
f"Shard advertises layers {shard_start}{shard_end}, but its split "
f"artifact only contains layers {binding[0]}{binding[1]}"
)
axes: dict[str, Any] = {}
for axis in RECIPE_AXES:
if axis not in recipe:
raise RecipeIdentityError(
f"recipe is missing axis {axis!r}; an unstated axis cannot default"
)
value = recipe[axis]
if axis in _INT_AXES:
axes[axis] = _integer(value, f"recipe.{axis}", 1)
else:
axes[axis] = _text(value, f"recipe.{axis}")
_pin(axes["tokenizer_revision"], "recipe.tokenizer_revision")
identity = PresentedIdentity(
artifact_id=_text(artifact.get("artifact_id"), "artifact.artifact_id"),
revision=_pin(artifact.get("revision"), "artifact.revision"),
source_digest=source_digest,
architecture=_text(artifact.get("architecture"), "artifact.architecture"),
architecture_digest=_hex64(
artifact.get("architecture_digest"), "artifact.architecture_digest"
),
layer_count=layer_count,
is_derivative=binding is not None,
shard_start=shard_start,
shard_end=shard_end,
axes=axes,
recipe_id=_text(recipe.get("recipe_id"), "recipe.recipe_id"),
recipe_version=_text(recipe.get("recipe_version"), "recipe.recipe_version"),
catalogue_version=_text(
recipe.get("catalogue_version"), "recipe.catalogue_version"
),
)
declared = doc.get("fingerprint")
if declared is not None:
claim = _mapping(declared, "identity.fingerprint")
claimed_artifact = _hex64(
claim.get("model_artifact_digest"), "fingerprint.model_artifact_digest"
)
claimed_recipe = _hex64(
claim.get("runtime_recipe_digest"), "fingerprint.runtime_recipe_digest"
)
if (claimed_artifact, claimed_recipe) != identity.key:
raise FingerprintMismatch(
"declared fingerprint is inconsistent with the artifact and recipe "
"claim; the tracker recomputes compatibility digests"
)
return identity
@dataclass(frozen=True)
class RouteMismatch:
reason: str
detail: str
def describe(self) -> str:
return f"{self.reason}: {self.detail}"
def compare(
route: PresentedIdentity, candidate: PresentedIdentity
) -> tuple[RouteMismatch, ...]:
"""Why `candidate` may not join a route already running `route`."""
if route.key == candidate.key:
return ()
reasons: list[RouteMismatch] = []
if route.source_digest != candidate.source_digest:
reasons.append(
RouteMismatch(
MISMATCH_ARTIFACT,
f"serves Model Artifact {candidate.source_digest[:12]}…, the route "
f"runs {route.source_digest[:12]}",
)
)
if route.architecture_digest != candidate.architecture_digest:
reasons.append(
RouteMismatch(
MISMATCH_ARCHITECTURE,
"architecture/config snapshot differs from the route's",
)
)
if route.layer_count != candidate.layer_count:
reasons.append(
RouteMismatch(
MISMATCH_ARTIFACT,
f"artifact has {candidate.layer_count} layers, the route's has "
f"{route.layer_count}",
)
)
axis_reason = {
"tokenizer_revision": MISMATCH_TOKENIZER,
"architecture_adapter": MISMATCH_ARCHITECTURE,
"activation_dtype": MISMATCH_ACTIVATION_RECIPE,
"compute_dtype": MISMATCH_ACTIVATION_RECIPE,
"kv_dtype": MISMATCH_CACHE_LAYOUT,
"kv_layout": MISMATCH_CACHE_LAYOUT,
"boundary_schema_version": MISMATCH_BOUNDARY_SCHEMA,
"protocol_schema_version": MISMATCH_BOUNDARY_SCHEMA,
}
for axis in RECIPE_AXES:
mine = route.axes.get(axis)
theirs = candidate.axes.get(axis)
if mine != theirs:
reasons.append(
RouteMismatch(
axis_reason.get(axis, MISMATCH_FINGERPRINT),
f"{axis} is {theirs!r}, the route runs {mine!r}",
)
)
if not reasons:
reasons.append(
RouteMismatch(
MISMATCH_FINGERPRINT,
"fingerprints differ on a field this comparison does not cover",
)
)
return tuple(reasons)
def coverage_gap(
ranges: Iterable[tuple[int, int]], layer_count: int
) -> str | None:
"""Why `ranges` fail to tile ``[0, layer_count)``, or None when they do.
Overlaps are legal — ADR-0012 lets the Tracker resolve one by telling a hop
where the previous hop stopped. A hole is not: its layers are never computed,
and the route emits fluent tokens from a truncated model.
"""
ordered = sorted(ranges)
if not ordered:
return "the route owns no layers"
if ordered[0][0] != 0:
return f"layers 0{ordered[0][0]} are owned by no Shard on the route"
covered = 0
for start, end in ordered:
if start > covered:
return f"layers {covered}{start} are owned by no Shard on the route"
covered = max(covered, end)
if covered < layer_count:
return f"layers {covered}{layer_count} are owned by no Shard on the route"
if covered > layer_count:
return (
f"the route claims {covered} layers, but the model has only {layer_count}"
)
return None
@dataclass(frozen=True)
class DistributedForwardEvidence:
"""A real distributed forward — the only thing that certifies a recipe."""
route_session_id: str
route_epoch: int
node_ids: tuple[str, ...]
shard_ranges: tuple[tuple[int, int], ...]
tokens_generated: int
layer_count: int
fingerprint: tuple[str, str]
synthetic: bool = False
certified_at: float = field(default_factory=time.time)
def rejection(self) -> str | None:
if self.synthetic:
return (
"evidence comes from a synthetic worker; only a real distributed "
"forward certifies a recipe"
)
distinct = set(self.node_ids)
if len(distinct) < MIN_CERTIFYING_NODES:
return (
f"evidence covers {len(distinct)} distinct node(s); a distributed "
f"forward requires at least {MIN_CERTIFYING_NODES}"
)
if len(distinct) != len(self.node_ids):
return "the same node is counted more than once in the certifying route"
if self.tokens_generated < 1:
return "the certifying forward generated no tokens"
return coverage_gap(self.shard_ranges, self.layer_count)
@dataclass(frozen=True)
class RecipeStatus:
status: str
detail: str = ""
certified_at: float | None = None
@property
def may_serve(self) -> bool:
return self.status == STATUS_CERTIFIED
@property
def may_certify(self) -> bool:
"""A dark recipe may be routed to *certify* it, and for nothing else.
Without this, certification is unreachable by construction: serving needs
certification, certification needs a real distributed forward, and a real
distributed forward needs a route.
"""
return self.status in (STATUS_DARK, STATUS_CERTIFIED)
def to_dict(self) -> dict:
return {
"status": self.status,
"detail": self.detail,
"certified_at": self.certified_at,
}
_DARK_DETAIL = "registered; dark until a real distributed forward certifies it"
_UNKNOWN_DETAIL = "this recipe has never been registered with the tracker"
class CertificationLedger:
"""Which recipes the tracker has seen, and which a real forward has proven."""
def __init__(self) -> None:
self._dark: set[tuple[str, str]] = set()
self._certified: dict[tuple[str, str], RecipeStatus] = {}
def register(self, identity: PresentedIdentity) -> RecipeStatus:
key = identity.key
if key in self._certified:
return self._certified[key]
self._dark.add(key)
return RecipeStatus(STATUS_DARK, _DARK_DETAIL)
def status(self, identity: PresentedIdentity | tuple[str, str]) -> RecipeStatus:
key = identity if isinstance(identity, tuple) else identity.key
if key in self._certified:
return self._certified[key]
if key in self._dark:
return RecipeStatus(STATUS_DARK, _DARK_DETAIL)
return RecipeStatus(STATUS_UNKNOWN, _UNKNOWN_DETAIL)
def certify(
self,
identity: PresentedIdentity | tuple[str, str],
evidence: DistributedForwardEvidence,
) -> RecipeStatus:
"""Promote a recipe out of the dark, or raise saying why the evidence is short."""
key = identity if isinstance(identity, tuple) else identity.key
if key not in self._dark and key not in self._certified:
raise RecipeIdentityError(
"this fingerprint is not registered; unknown recipes cannot be certified"
)
if evidence.fingerprint != key:
raise RecipeIdentityError(
"certification evidence fingerprint does not match the recipe being promoted"
)
rejection = evidence.rejection()
if rejection is not None:
raise RecipeIdentityError(f"this evidence does not certify: {rejection}")
status = RecipeStatus(
STATUS_CERTIFIED,
(
f"certified by route session {evidence.route_session_id} across "
f"{len(set(evidence.node_ids))} nodes"
),
certified_at=evidence.certified_at,
)
self._certified[key] = status
self._dark.discard(key)
return status
def to_dict(self) -> dict:
return {
"dark": [list(key) for key in sorted(self._dark)],
"certified": {
"/".join(key): status.to_dict()
for key, status in sorted(self._certified.items())
},
}
def admit_route(
identities: Sequence[PresentedIdentity],
*,
ledger: CertificationLedger | None = None,
for_certification: bool = False,
) -> tuple[RouteMismatch, ...]:
"""Every reason these Shards may not form one Inference Route; empty when they may.
Fail-closed by construction: an empty route, a hole in the coverage, one
differing dtype, and an uncertified recipe each produce a reason, so a caller
that proceeds only on an empty result cannot admit any of them.
"""
if not identities:
return (RouteMismatch(MISMATCH_RANGE, "a route needs at least one Shard"),)
head = identities[0]
reasons: list[RouteMismatch] = []
for candidate in identities[1:]:
reasons.extend(compare(head, candidate))
gap = coverage_gap(
[(i.shard_start, i.shard_end) for i in identities], head.layer_count
)
if gap is not None:
reasons.append(RouteMismatch(MISMATCH_RANGE, gap))
if ledger is not None:
status = ledger.status(head)
allowed = status.may_certify if for_certification else status.may_serve
if not allowed:
reasons.append(
RouteMismatch(MISMATCH_UNCERTIFIED, f"{status.status}: {status.detail}")
)
return tuple(reasons)

View File

@@ -84,6 +84,7 @@ from .routing_stats import (
) )
from .model_files import files_for_layer_range, snapshot_dir_for_repo from .model_files import files_for_layer_range, snapshot_dir_for_repo
from .raft import RaftNode from .raft import RaftNode
from .recipe import CertificationLedger
_CONSOLE_LIMIT = 300 _CONSOLE_LIMIT = 300
@@ -792,6 +793,7 @@ def _capability_from_registration(
hf_repo: str | None, hf_repo: str | None,
shard_start: int | None, shard_start: int | None,
shard_end: int | None, shard_end: int | None,
recipe_certifications: CertificationLedger,
) -> CapabilityState: ) -> CapabilityState:
"""The tracker's verdict on the proof carried by one registration payload. """The tracker's verdict on the proof carried by one registration payload.
@@ -811,6 +813,7 @@ def _capability_from_registration(
declared_recipe_version=( declared_recipe_version=(
recipe_version if isinstance(recipe_version, str) else None recipe_version if isinstance(recipe_version, str) else None
), ),
ledger=recipe_certifications,
) )
@@ -2847,9 +2850,11 @@ class _TrackerHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
relay_status: dict | None = None, relay_status: dict | None = None,
test_runner: "TestRunManager | None" = None, test_runner: "TestRunManager | None" = None,
capability_policy: str | None = None, capability_policy: str | None = None,
recipe_certifications: CertificationLedger | None = None,
) -> None: ) -> None:
super().__init__(addr, handler) super().__init__(addr, handler)
self.registry = registry self.registry = registry
self.recipe_certifications = recipe_certifications or CertificationLedger()
self.capability_policy = normalize_policy( self.capability_policy = normalize_policy(
capability_policy if capability_policy is not None else policy_from_env() capability_policy if capability_policy is not None else policy_from_env()
) )
@@ -4555,6 +4560,7 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
hf_repo=hf_repo, hf_repo=hf_repo,
shard_start=shard_start, shard_start=shard_start,
shard_end=shard_end, shard_end=shard_end,
recipe_certifications=server.recipe_certifications,
) )
node_id = _node_id_for_registration( node_id = _node_id_for_registration(
@@ -6531,6 +6537,7 @@ class TrackerServer:
self._embedded_relay: Any | None = None self._embedded_relay: Any | None = None
self._embedded_relay_actual_port: int | None = None self._embedded_relay_actual_port: int | None = None
self._registry: dict[str, _NodeEntry] = {} self._registry: dict[str, _NodeEntry] = {}
self._recipe_certifications = CertificationLedger()
self._lock = threading.Lock() self._lock = threading.Lock()
self._server: _TrackerHTTPServer | None = None self._server: _TrackerHTTPServer | None = None
self._thread: threading.Thread | None = None self._thread: threading.Thread | None = None
@@ -6725,6 +6732,7 @@ class TrackerServer:
relay_status=http_relay_status, relay_status=http_relay_status,
test_runner=self._test_runner, test_runner=self._test_runner,
capability_policy=self._capability_policy, capability_policy=self._capability_policy,
recipe_certifications=self._recipe_certifications,
) )
self.port = self._server.server_address[1] self.port = self._server.server_address[1]
@@ -6983,6 +6991,7 @@ class TrackerServer:
hf_repo=payload.get("hf_repo"), hf_repo=payload.get("hf_repo"),
shard_start=shard_start, shard_start=shard_start,
shard_end=shard_end, shard_end=shard_end,
recipe_certifications=self._recipe_certifications,
), ),
) )
with self._lock: with self._lock:

View File

@@ -0,0 +1,52 @@
{
"schema_version": 1,
"vectors": [
{
"fingerprint": {
"catalogue_version": "2026.07.1",
"model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b",
"recipe_id": "example-gguf",
"recipe_version": "1",
"runtime_recipe_digest": "9b14d70b0835a6428457e4888d453649dd0d2e41fc8ac9d84d232c8c237e68fa"
},
"identity": {
"artifact": {
"architecture": "dense-llama",
"architecture_digest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"artifact_id": "example/model",
"content_digest": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"derived_from": null,
"layer_count": 8,
"revision": "0123456789abcdef"
},
"fingerprint": {
"catalogue_version": "2026.07.1",
"model_artifact_digest": "8a0f43d6aa49d77834bdb47bcae9f42c886b7ccfe0ac014932b2a2b38697a47b",
"recipe_id": "example-gguf",
"recipe_version": "1",
"runtime_recipe_digest": "9b14d70b0835a6428457e4888d453649dd0d2e41fc8ac9d84d232c8c237e68fa"
},
"recipe": {
"activation_dtype": "bfloat16",
"architecture_adapter": "llama/range-v1",
"backend_id": "llama.cpp",
"boundary_schema_version": 1,
"catalogue_version": "2026.07.1",
"compute_dtype": "float32",
"kv_dtype": "q8_0",
"kv_layout": "paged-v1",
"protocol_schema_version": 1,
"recipe_id": "example-gguf",
"recipe_version": "1",
"runtime_version": "llama.cpp@deadbeef+meshnet.1",
"tokenizer_revision": "0123456789abcdef",
"weight_quantization": "Q4_K_M"
},
"schema_version": 1,
"shard_end": 4,
"shard_start": 0
},
"name": "example-v1"
}
]
}

View File

@@ -80,7 +80,7 @@ def snapshot_doc(snapshot):
@pytest.fixture @pytest.fixture
def contract_doc(contract): def contract_doc(contract):
return copy.deepcopy(dict(contract.raw)) return contract.to_dict()
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
@@ -746,7 +746,7 @@ def test_the_contract_locks_the_roadmap_thresholds(contract):
assert contract.threshold("performance", "quality_pass_with_speed_fail_verdict") == "stop" assert contract.threshold("performance", "quality_pass_with_speed_fail_verdict") == "stop"
assert contract.threshold("reliability", "synthetic_workers_satisfy_alpha") is False assert contract.threshold("reliability", "synthetic_workers_satisfy_alpha") is False
assert contract.threshold("storage", "forbidden_path_prefixes") == ["/home"] assert contract.threshold("storage", "forbidden_path_prefixes") == ("/home",)
def test_the_contract_offers_only_alpha_or_stop(contract): def test_the_contract_offers_only_alpha_or_stop(contract):
@@ -820,17 +820,26 @@ def test_a_dropped_acceptance_section_is_rejected(contract_doc):
parse_alpha_contract(contract_doc) parse_alpha_contract(contract_doc)
def test_resealing_a_mutated_contract_changes_its_digest(contract, contract_doc): def test_resealing_a_mutated_v1_contract_is_rejected(contract, contract_doc):
"""Tamper-evidence, not tamper-proofing: a rewrite is legal but never silent."""
contract_doc["performance"]["min_median_decode_tokens_per_second"] = 0.05 contract_doc["performance"]["min_median_decode_tokens_per_second"] = 0.05
resealed = seal_contract(contract_doc) resealed = seal_contract(contract_doc)
# It parses — nothing in-repo can stop a determined rewrite of file plus digest. with pytest.raises(AlphaContractError, match="trusted pre-execution digest"):
reparsed = parse_alpha_contract(resealed) parse_alpha_contract(resealed)
assert resealed["contract_sha256"] != contract.digest
# But the digest moved, so the change is a visible diff on a file whose entire
# purpose is to not change, and the contract_id still claims to be v1. def test_parsed_contract_nested_state_is_immutable(contract):
assert reparsed.digest != contract.digest with pytest.raises(TypeError):
contract.raw["performance"]["min_median_decode_tokens_per_second"] = 0.05
with pytest.raises(TypeError):
contract.target["reasoning_effort"] = "high"
def test_contract_to_dict_returns_an_isolated_mutable_copy(contract):
copied = contract.to_dict()
copied["performance"]["min_median_decode_tokens_per_second"] = 0.05
assert contract.threshold("performance", "min_median_decode_tokens_per_second") == 0.5
def test_an_unknown_threshold_cannot_be_invented_at_read_time(contract): def test_an_unknown_threshold_cannot_be_invented_at_read_time(contract):

View File

@@ -0,0 +1,270 @@
"""Deterministic DGR-003 identity and tracker-admission conformance tests."""
from __future__ import annotations
from dataclasses import replace
import json
from pathlib import Path
import time
import pytest
from meshnet_node.runtime_recipe import (
ArtifactIdentity,
CompatibilityFingerprint,
DerivativeBinding,
RecipeIdentityError,
RuntimeRecipe,
ShardIdentity,
check_handshake,
check_route,
handshake_error,
)
from meshnet_tracker.capability import (
STATE_ADMITTED,
STATE_FINGERPRINT_MISMATCH,
STATE_MODEL_MISMATCH,
STATE_RECIPE_MISMATCH,
STATE_UNCERTIFIED,
evaluate_report,
)
from meshnet_tracker.server import TrackerServer, _capability_from_registration
from meshnet_tracker.recipe import (
CertificationLedger,
DistributedForwardEvidence,
parse_identity,
)
def _digest(char: str) -> str:
return char * 64
def _identity(start: int = 0, end: int = 4, **recipe_changes: object) -> ShardIdentity:
recipe_fields: dict[str, object] = {
"weight_quantization": "Q4_K_M",
"activation_dtype": "bfloat16",
"compute_dtype": "float32",
"kv_dtype": "q8_0",
"kv_layout": "paged-v1",
"tokenizer_revision": "0123456789abcdef",
"architecture_adapter": "llama/range-v1",
"backend_id": "llama.cpp",
"runtime_version": "llama.cpp@deadbeef+meshnet.1",
"recipe_id": "example-gguf",
"recipe_version": "1",
"catalogue_version": "2026.07.1",
}
recipe_fields.update(recipe_changes)
recipe = RuntimeRecipe(**recipe_fields) # type: ignore[arg-type]
return ShardIdentity(
ArtifactIdentity(
"example/model",
"0123456789abcdef",
_digest("a"),
"dense-llama",
_digest("b"),
8,
),
recipe,
start,
end,
)
def _report(identity: ShardIdentity) -> dict:
return {
"schema_version": 1,
"model": {"model_id": "example/model"},
"shard": {"start": identity.shard_start, "end": identity.shard_end - 1},
"recipe": identity.recipe.to_dict() | {
"recipe_id": identity.recipe.recipe_id,
"recipe_version": identity.recipe.recipe_version,
"catalogue_version": identity.recipe.catalogue_version,
},
"backend": {"backend_id": "llama.cpp", "device": "test"},
"status": "passed",
"validated_at": 100.0,
"duration_ms": 1,
"diagnostics": [],
"identity": identity.to_dict(),
}
def _evaluate(report: dict, **kwargs: object):
return evaluate_report(
report,
model_matches=lambda model: model == "example/model",
advertised_model="example/model",
shard_start=0,
shard_end=3,
now=100.0,
**kwargs,
)
def test_node_and_tracker_share_the_committed_canonical_fingerprint_vector():
vector_path = Path(__file__).parent / "data" / "recipe_fingerprint_vectors.json"
vector = json.loads(vector_path.read_text(encoding="utf-8"))["vectors"][0]
expected = vector["fingerprint"]
identity = ShardIdentity.from_dict(vector["identity"])
assert identity.fingerprint.to_dict() == expected
assert parse_identity(vector["identity"]).fingerprint_dict() == expected
assert (
CompatibilityFingerprint.from_proto(identity.fingerprint.to_proto()).to_dict()
== expected
)
@pytest.mark.parametrize(
"axis,value",
[
("weight_quantization", "Q5_K_M"),
("activation_dtype", "float16"),
("compute_dtype", "float16"),
("kv_dtype", "float16"),
("kv_layout", "contiguous-v2"),
("tokenizer_revision", "fedcba9876543210"),
("architecture_adapter", "llama/range-v2"),
("backend_id", "other-backend"),
("runtime_version", "llama.cpp@other+meshnet.1"),
("boundary_schema_version", 2),
("protocol_schema_version", 2),
],
)
def test_every_recipe_axis_changes_the_fingerprint_and_blocks_route(axis, value):
left = _identity()
right = _identity(4, 8, **{axis: value})
assert left.fingerprint.key != right.fingerprint.key
assert check_route([left, right])
def test_split_artifact_is_bound_to_exact_source_and_owned_range():
source = _identity()
split = ShardIdentity(
ArtifactIdentity(
"example/model",
"0123456789abcdef",
_digest("c"),
"dense-llama",
_digest("b"),
8,
DerivativeBinding(_digest("a"), 4, 8),
),
source.recipe,
4,
8,
)
assert source.fingerprint.matches(split.fingerprint)
with pytest.raises(RecipeIdentityError):
ShardIdentity(split.artifact, split.recipe, 3, 8)
def test_declared_digest_mismatch_is_recomputed_and_rejected_not_authenticated():
report = _report(_identity())
report["identity"]["fingerprint"]["runtime_recipe_digest"] = _digest("f")
assert _evaluate(report).state == STATE_FINGERPRINT_MISMATCH
def test_exact_recipe_registers_dark_until_tracker_certification():
identity = _identity()
report = _report(identity)
ledger = CertificationLedger()
dark = _evaluate(report, ledger=ledger)
assert dark.state == STATE_UNCERTIFIED
# Unit fixtures cannot promote a recipe: this is the explicit trust boundary.
with pytest.raises(ValueError, match="synthetic"):
ledger.certify(
parse_identity(identity.to_dict()),
DistributedForwardEvidence(
"session",
1,
("a", "b"),
((0, 4), (4, 8)),
1,
8,
identity.fingerprint.key,
synthetic=True,
),
)
with pytest.raises(ValueError, match="fingerprint does not match"):
ledger.certify(
parse_identity(identity.to_dict()),
DistributedForwardEvidence(
"session",
1,
("a", "b"),
((0, 4), (4, 8)),
1,
8,
(_digest("e"), _digest("f")),
),
)
def test_capability_identity_must_match_the_proof_labels():
identity = _identity()
wrong_model = _report(identity)
wrong_model["identity"]["artifact"]["artifact_id"] = "other/model"
wrong_model["identity"]["fingerprint"] = None
assert _evaluate(wrong_model).state == STATE_MODEL_MISMATCH
wrong_recipe = _report(identity)
wrong_recipe["recipe"]["recipe_id"] = "other-recipe"
assert _evaluate(wrong_recipe).state == STATE_RECIPE_MISMATCH
wrong_backend = _report(identity)
wrong_backend["backend"]["backend_id"] = "other-backend"
assert _evaluate(wrong_backend).state == STATE_RECIPE_MISMATCH
wrong_quantization = _report(identity)
wrong_quantization["backend"]["quantization"] = "Q5_K_M"
assert _evaluate(wrong_quantization).state == STATE_RECIPE_MISMATCH
def test_tracker_server_owns_the_ledger_used_by_registration():
identity = _identity()
report = _report(identity)
report["validated_at"] = time.time()
payload = {"capability_report": report}
tracker = TrackerServer()
def evaluate():
return _capability_from_registration(
payload,
model="example/model",
hf_repo=None,
shard_start=0,
shard_end=3,
recipe_certifications=tracker._recipe_certifications,
)
assert evaluate().state == STATE_UNCERTIFIED
tracker._recipe_certifications.certify(
parse_identity(identity.to_dict()),
DistributedForwardEvidence(
"session",
1,
("physical-a", "physical-b"),
((0, 4), (4, 8)),
1,
8,
identity.fingerprint.key,
),
)
assert evaluate().state == STATE_ADMITTED
def test_grpc_handshake_uses_the_dgr_002_fingerprint_and_fails_closed():
local = _identity()
remote = replace(local.fingerprint, runtime_recipe_digest=_digest("f")).to_proto()
mismatches = check_handshake(local, remote)
assert mismatches
assert handshake_error(mismatches).code != 0