- [] Validator/tracker compares fingerprints at each hop cut-point; first mismatch = culprit
- []`_final_text_node` removed or limited to text-only fallback
- [] Integration test: multi-hop pipeline, fault injected at known hop
- [x] Audit requests carry tracker RNG/VRF flag indistinguishable from normal traffic (research §6) — the existing post-hoc `sample_rate` RNG gate in `ValidatorProcess.validate_once` already decides audit selection after the original proxied request completed, so the request the client/nodes saw is unaffected either way; locked in by `test_hop_commitments_are_not_requested_unless_the_event_is_audit_selected`
- [x] Nodes retain recent boundary activations for on-demand commit window (configurable TTL) — `ToplocAuditConfig.commitment_ttl_seconds`; expired commitments fall back to the text-only path (`test_expired_commitment_window_falls_back_to_text_only_audit`)
- [x] Validator/tracker compares fingerprints at each hop cut-point; first mismatch = culprit — `_hop_commitments_from_event` + `_first_divergent_hop` in `packages/validator/meshnet_validator/__init__.py`
- [x]`_final_text_node` removed or limited to text-only fallback — only called from the plain-text divergence branch of `_validate_event` now
- [x] Integration test: multi-hop pipeline, fault injected at known hop — `tests/test_hop_bisection.py`
- [] Integration test: 60-request fraud scenario → ban within threshold
- [x] Audit failure triggers forfeiture + strike in one tracker transaction — `ValidatorProcess._slash_node` (in-process) and the tracker's `_handle_billing_forfeit` handler (remote) both forfeit-then-strike synchronously in a single call path; each already existed pre-AH-010 and is exercised by `tests/test_forfeiture_penalty.py`
- [x] Banned nodes excluded from `payables` / settlement — `BillingLedger.settle_node_payout` now clamps to the wallet's *current* pending balance under the same lock as the debit, and `_settlement_loop` rechecks ban status and uses the post-clamp amount before sending, so a forfeiture landing between the `payables()` snapshot and the actual payout can never be paid out on top of (ADR-0015 race); covered by `test_60_request_stream_bans_intermittent_first_hop_cheater_not_last_hop`
- [x] Validator uses authenticated forfeit endpoint (issue 02) — `POST /v1/billing/forfeit` is validator-token/admin-gated (ADR-0017 §4, issue 20) and is the documented remote path (`packages/validator/README.md` Usage section); `test_forfeit_endpoint_requires_auth_and_forfeits` exercises the 401→200 flow. No standalone remote-validator process exists in this codebase yet (`contracts` has no networked implementation), so the in-process `ValidatorProcess` continues to call `BillingLedger.forfeit_pending` directly when co-located with the tracker — adding an HTTP-only forfeit client with no real consumer was judged out of scope/overengineering for this issue
- [x] README: `L > 19× g` at p=0.05; pending balance = collateral — already present in `packages/validator/README.md` ("Why the penalty deters cheating")
- [x] Integration test: 60-request fraud scenario → ban within threshold — `tests/test_forfeiture_penalty.py::test_60_request_stream_bans_intermittent_first_hop_cheater_not_last_hop`
# 19 — DOC: Cryptography dependency + test environment note
@@ -14,11 +14,18 @@ Document and verify test/dev environment setup for wallet crypto paths. `package
## Acceptance criteria
- [] Confirm `cryptography>=41` remains in node package deps; add to root/dev extras only if tests import wallet without node install
- [] Add short **Test environment** section to `docs/dev/test-env.md` (or `CONTRIBUTING.md` if created): use `.venv/Scripts/python.exe`, `pip install -e packages/node ...`, optional dep skips
- [] Note which tests require optional deps (`--ignore=test_openai_gateway,...`)
- [] No unrelated production code changes
- [x] Confirm `cryptography>=41` remains in node package deps; add to root/dev extras only if tests import wallet without node install
- [x] Add short **Test environment** section to `docs/dev/test-env.md` (or `CONTRIBUTING.md` if created): use `.venv/Scripts/python.exe`, `pip install -e packages/node ...`, optional dep skips
- [x] Note which tests require optional deps (`--ignore=test_openai_gateway,...`)
- [x] No unrelated production code changes
## Blocked by
None
## Resolution
-`packages/node/pyproject.toml` already declared `cryptography>=41` — no change needed.
-`conftest.py` adds every `packages/*` dir to `sys.path`, so first-party imports (e.g. `meshnet_node.wallet`) resolve without an editable install of that package — but third-party deps like `cryptography` still must be installed separately. Added `cryptography>=41` to the root `pyproject.toml``dev` extra so `pip install -e ".[dev]"` alone covers the wallet tests (`test_node_startup.py`, `test_wallet_binding_proof.py`, `test_devnet_treasury.py`, etc.) without requiring a full `packages/node` install (which would otherwise pull in torch/transformers/accelerate/bitsandbytes).
- Added `docs/dev/test-env.md` with setup instructions (Linux + Windows `.venv\Scripts\python.exe`), and a note on optional-dependency tests: `test_real_model_backend.py` / `test_devnet_treasury.py` use `pytest.importorskip` and skip cleanly; `test_openai_gateway.py` hard-imports `openai`/`langchain_openai` with no skip guard (both already in the `dev` extra) — documented the `--ignore=tests/test_openai_gateway.py` fallback for minimal installs.
- Full suite: 311 passed, 3 skipped, 3 pre-existing failures unrelated to this issue (`test_billing_ledger.py::test_proxy_chat_splits_payout_by_tracker_assigned_route_span`, `test_forfeiture_penalty.py::test_probation_earns_nothing_then_earning_begins`, `test_mining_cli.py::test_legacy_start_without_port_uses_next_available_port` — port-in-use env artifact). Wallet-specific tests (`test_wallet_binding_proof.py`, `test_node_startup.py`, `test_devnet_treasury.py`): 50 passed, 2 skipped.
"description":"# 01 \u2014 C1: Authenticate hive gossip endpoints\n\n## What to build\n\nAdd authenticated peer identity to all tracker gossip mutation endpoints. Today any caller can push billing, account, and stats events without verification.\n\n**Code refs:**\n\n- `packages/tracker/meshnet_tracker/server.py` \u2014 `_handle_billing_gossip` (~2414\u20132427)\n- `packages/tracker/meshnet_tracker/server.py` \u2014 `_handle_accounts_gossip` (~2610\u20132623)\n- `packages/tracker/meshnet_tracker/server.py` \u2014 `_handle_stats_gossip` (~2355\u20132364)\n- `packages/tracker/meshnet_tracker/billing.py` \u2014 `apply_events` (~301\u2013311)\n- `packages/tracker/meshnet_tracker/accounts.py` \u2014 `apply_events` (~220\u2013226)\n\nImplement per ADR-0017 \u00a73 using the auth helper/config from issue 02: shared hive HMAC (body + timestamp) or mutual TLS between configured tracker peers. Reject unauthenticated gossip with 401.\n\n**Note:** `/v1/gossip` (node throughput fan-out, `server.py` ~1331) is **not** in scope for this issue \u2014 see ADR-0017 \u00a73 out-of-scope note.\n\n## Test-first\n\n1. Red: unauthenticated POST to `/v1/billing/gossip` applies a credit event today \u2014 test must fail after fix.\n2. Red: authenticated peer with valid HMAC applies events; invalid/missing auth returns 401 and `applied: 0`.\n3. Green: wire the issue-02 verifier/config (`--hive-secret` or peer cert paths) into the three hive mutation endpoints.\n\n## Acceptance criteria\n\n- [ ] `/v1/billing/gossip`, `/v1/accounts/gossip`, `/v1/stats/gossip` reject requests without valid hive auth\n- [ ] Authenticated peers replicate events as today (id-dedup preserved)\n- [ ] Config documented for multi-tracker dev setups\n- [ ] Tests cover reject + accept paths without live network\n\n## ADR links\n\n- [ADR-0017](../../docs/adr/0017-tracker-authentication-and-authorization.md)\n- [ADR-0016](../../docs/adr/0016-alpha-scope-and-known-limitations.md)\n\n## Blocked by\n\n- `02-a2-unified-auth-boundary.md` \u2014 owns shared auth middleware/config. Implement in the same PR if simpler.",
"description":"# 01 — C1: Authenticate hive gossip endpoints\n\n## What to build\n\nAdd authenticated peer identity to all tracker gossip mutation endpoints. Today any caller can push billing, account, and stats events without verification.\n\n**Code refs:**\n\n- `packages/tracker/meshnet_tracker/server.py` — `_handle_billing_gossip` (~2414–2427)\n- `packages/tracker/meshnet_tracker/server.py` — `_handle_accounts_gossip` (~2610–2623)\n- `packages/tracker/meshnet_tracker/server.py` — `_handle_stats_gossip` (~2355–2364)\n- `packages/tracker/meshnet_tracker/billing.py` — `apply_events` (~301–311)\n- `packages/tracker/meshnet_tracker/accounts.py` — `apply_events` (~220–226)\n\nImplement per ADR-0017 §3 using the auth helper/config from issue 02: shared hive HMAC (body + timestamp) or mutual TLS between configured tracker peers. Reject unauthenticated gossip with 401.\n\n**Note:** `/v1/gossip` (node throughput fan-out, `server.py` ~1331) is **not** in scope for this issue — see ADR-0017 §3 out-of-scope note.\n\n## Test-first\n\n1. Red: unauthenticated POST to `/v1/billing/gossip` applies a credit event today — test must fail after fix.\n2. Red: authenticated peer with valid HMAC applies events; invalid/missing auth returns 401 and `applied: 0`.\n3. Green: wire the issue-02 verifier/config (`--hive-secret` or peer cert paths) into the three hive mutation endpoints.\n\n## Acceptance criteria\n\n- [ ] `/v1/billing/gossip`, `/v1/accounts/gossip`, `/v1/stats/gossip` reject requests without valid hive auth\n- [ ] Authenticated peers replicate events as today (id-dedup preserved)\n- [ ] Config documented for multi-tracker dev setups\n- [ ] Tests cover reject + accept paths without live network\n\n## ADR links\n\n- [ADR-0017](../../docs/adr/0017-tracker-authentication-and-authorization.md)\n- [ADR-0016](../../docs/adr/0016-alpha-scope-and-known-limitations.md)\n\n## Blocked by\n\n- `02-a2-unified-auth-boundary.md` — owns shared auth middleware/config. Implement in the same PR if simpler.",
"acceptanceCriteria":[
"`/v1/billing/gossip`, `/v1/accounts/gossip`, `/v1/stats/gossip` reject requests without valid hive auth",
"Authenticated peers replicate events as today (id-dedup preserved)",
"title":"02 \u2014 A2: Unified auth boundary for privileged and financial reads",
"description":"# 02 \u2014 A2: Unified auth boundary for privileged and financial reads\n\n## What to build\n\nReplace header-presence stubs with a single auth middleware that resolves API keys, admin sessions, validator service tokens, and hive peer identity. Close leaks on financial and operator endpoints. This is the auth foundation issue; issue 01 should only apply hive auth to gossip endpoints once the helper exists.\n\n**Code refs:**\n\n- `packages/tracker/meshnet_tracker/server.py` \u2014 `_handle_billing_forfeit` (~2429\u20132464) \u2014 H3: non-empty `Authorization` only\n- `packages/tracker/meshnet_tracker/server.py` \u2014 `_handle_benchmark_hop_penalty` (~2650\u20132658), `_handle_benchmark_results` (~2745\u20132748) \u2014 H3\n- `packages/tracker/meshnet_tracker/server.py` \u2014 `_handle_billing_summary` (~2366\u20132371) \u2014 H4\n- `packages/tracker/meshnet_tracker/server.py` \u2014 `_handle_billing_settlements` (~2407\u20132412) \u2014 H4\n- `packages/tracker/meshnet_tracker/server.py` \u2014 `_handle_registry_wallets` (~2391\u20132405) \u2014 H4\n- `packages/tracker/meshnet_tracker/server.py` \u2014 `_session_account` (~2468+), `_handle_admin_accounts` (~2588\u20132608) \u2014 H4\n- `packages/tracker/meshnet_tracker/accounts.py` \u2014 `session_account()`, `create_session()` only (session store; not handler wiring)\n\nPer ADR-0017 \u00a74: forfeit \u2192 validator or admin; benchmark \u2192 admin; billing summary/settlements/registry wallets \u2192 admin session. Include the validator service token shape from `20-validator-service-token.md` in the same implementation if practical.\n\n## Test-first\n\n1. Red: POST `/v1/billing/forfeit` with `Authorization: Bearer garbage` succeeds today \u2014 must require validator/admin identity.\n2. Red: GET `/v1/billing/summary` without admin session returns 401/403.\n3. Green: middleware + role checks; existing inference API-key path unchanged.\n\n## Acceptance criteria\n\n- [ ] Single `_require_auth(role=...)` (or equivalent) used by all privileged handlers\n- [ ] Shared auth config supports admin sessions, validator service token, and hive peer HMAC/mTLS\n- [ ] Forfeit accepts only validator service token or admin session \u2014 not arbitrary Bearer strings\n- [ ] Financial read endpoints require admin session (alpha posture)\n- [ ] Benchmark write/read require admin or service token\n- [ ] Integration tests for each endpoint class (reject unauth, accept valid)\n\n## ADR links\n\n- [ADR-0017](../../docs/adr/0017-tracker-authentication-and-authorization.md)\n\n## Related\n\n- `20-validator-service-token.md` \u2014 checklist for validator service token format, rotation, forfeit auth\n\n## Blocked by\n\nNone. This issue should land before `01-c1-gossip-auth.md`.",
"title":"02 — A2: Unified auth boundary for privileged and financial reads",
"description":"# 02 — A2: Unified auth boundary for privileged and financial reads\n\n## What to build\n\nReplace header-presence stubs with a single auth middleware that resolves API keys, admin sessions, validator service tokens, and hive peer identity. Close leaks on financial and operator endpoints. This is the auth foundation issue; issue 01 should only apply hive auth to gossip endpoints once the helper exists.\n\n**Code refs:**\n\n- `packages/tracker/meshnet_tracker/server.py` — `_handle_billing_forfeit` (~2429–2464) — H3: non-empty `Authorization` only\n- `packages/tracker/meshnet_tracker/server.py` — `_handle_benchmark_hop_penalty` (~2650–2658), `_handle_benchmark_results` (~2745–2748) — H3\n- `packages/tracker/meshnet_tracker/server.py` — `_handle_billing_summary` (~2366–2371) — H4\n- `packages/tracker/meshnet_tracker/server.py` — `_handle_billing_settlements` (~2407–2412) — H4\n- `packages/tracker/meshnet_tracker/server.py` — `_handle_registry_wallets` (~2391–2405) — H4\n- `packages/tracker/meshnet_tracker/server.py` — `_session_account` (~2468+), `_handle_admin_accounts` (~2588–2608) — H4\n- `packages/tracker/meshnet_tracker/accounts.py` — `session_account()`, `create_session()` only (session store; not handler wiring)\n\nPer ADR-0017 §4: forfeit → validator or admin; benchmark → admin; billing summary/settlements/registry wallets → admin session. Include the validator service token shape from `20-validator-service-token.md` in the same implementation if practical.\n\n## Test-first\n\n1. Red: POST `/v1/billing/forfeit` with `Authorization: Bearer garbage` succeeds today — must require validator/admin identity.\n2. Red: GET `/v1/billing/summary` without admin session returns 401/403.\n3. Green: middleware + role checks; existing inference API-key path unchanged.\n\n## Acceptance criteria\n\n- [ ] Single `_require_auth(role=...)` (or equivalent) used by all privileged handlers\n- [ ] Shared auth config supports admin sessions, validator service token, and hive peer HMAC/mTLS\n- [ ] Forfeit accepts only validator service token or admin session — not arbitrary Bearer strings\n- [ ] Financial read endpoints require admin session (alpha posture)\n- [ ] Benchmark write/read require admin or service token\n- [ ] Integration tests for each endpoint class (reject unauth, accept valid)\n\n## ADR links\n\n- [ADR-0017](../../docs/adr/0017-tracker-authentication-and-authorization.md)\n\n## Related\n\n- `20-validator-service-token.md` — checklist for validator service token format, rotation, forfeit auth\n\n## Blocked by\n\nNone. This issue should land before `01-c1-gossip-auth.md`.",
"acceptanceCriteria":[
"Single `_require_auth(role=...)` (or equivalent) used by all privileged handlers",
"Shared auth config supports admin sessions, validator service token, and hive peer HMAC/mTLS",
"Forfeit accepts only validator service token or admin session \u2014 not arbitrary Bearer strings",
"Forfeit accepts only validator service token or admin session — not arbitrary Bearer strings",
"title":"04 \u2014 H2: Tracker-authoritative token and work-unit accounting",
"description":"# 04 \u2014 H2: Tracker-authoritative token and work-unit accounting\n\n## What to build\n\nStop trusting node-reported usage for billing. The tracker already proxies responses \u2014 use tracker-observed response data and request limits to cap billable tokens, and compute work units from the **route it constructed**, not node declarations.\n\n**Code refs:**\n\n- `packages/tracker/meshnet_tracker/server.py` \u2014 `node_work` from route construction (~1776\u20131782, ~1781\u20131782)\n- `packages/tracker/meshnet_tracker/server.py` \u2014 streaming token/chunk billing (~1890\u20131921)\n- `packages/tracker/meshnet_tracker/server.py` \u2014 non-streaming `_usage_total_tokens` (~1938\u20131943)\n- `packages/tracker/meshnet_tracker/billing.py` \u2014 `charge_request` node_work split (~104\u2013151)\n\nAccounting fraud = inflating tokens or shard span. Per ADR-0018 \u00a75.\n\n## Test-first\n\n1. Red: mock upstream returns inflated `usage.total_tokens` in body but tracker bills that value \u2014 test expects the tracker to cap billable tokens from observed stream chunks or request bounds.\n2. Red: node registers false `shard_end`; billing uses tracker route span, not registration field alone.\n3. Green: authoritative counters; ignore node-reported work units on charge path.\n\n## Acceptance criteria\n\n- [ ] Streaming token count uses tracker-observed chunks/tokens; upstream `usage.total_tokens` can only lower or match that observed count, never inflate it\n- [ ] Non-streaming token count caps upstream `usage.total_tokens` by tracker-known request bounds (`max_tokens`, and prompt estimate if available); exact tokenizer-backed counts are deferred unless already available locally\n- [ ] Work units = tracker-computed layer span per hop at route build time (~1781\u20131782)\n- [ ] Nodes cannot increase payout by lying about shard range mid-request\n- [ ] Integration test: malicious node metadata does not inflate `charge_request` shares\n\n## ADR links\n\n- [ADR-0018](../../docs/adr/0018-fraud-detection-verification-and-reputation.md) \u00a75\n\n## Blocked by\n\n- `02-a2-unified-auth-boundary.md`",
"title":"04 — H2: Tracker-authoritative token and work-unit accounting",
"description":"# 04 — H2: Tracker-authoritative token and work-unit accounting\n\n## What to build\n\nStop trusting node-reported usage for billing. The tracker already proxies responses — use tracker-observed response data and request limits to cap billable tokens, and compute work units from the **route it constructed**, not node declarations.\n\n**Code refs:**\n\n- `packages/tracker/meshnet_tracker/server.py` — `node_work` from route construction (~1776–1782, ~1781–1782)\n- `packages/tracker/meshnet_tracker/server.py` — streaming token/chunk billing (~1890–1921)\n- `packages/tracker/meshnet_tracker/server.py` — non-streaming `_usage_total_tokens` (~1938–1943)\n- `packages/tracker/meshnet_tracker/billing.py` — `charge_request` node_work split (~104–151)\n\nAccounting fraud = inflating tokens or shard span. Per ADR-0018 §5.\n\n## Test-first\n\n1. Red: mock upstream returns inflated `usage.total_tokens` in body but tracker bills that value — test expects the tracker to cap billable tokens from observed stream chunks or request bounds.\n2. Red: node registers false `shard_end`; billing uses tracker route span, not registration field alone.\n3. Green: authoritative counters; ignore node-reported work units on charge path.\n\n## Acceptance criteria\n\n- [ ] Streaming token count uses tracker-observed chunks/tokens; upstream `usage.total_tokens` can only lower or match that observed count, never inflate it\n- [ ] Non-streaming token count caps upstream `usage.total_tokens` by tracker-known request bounds (`max_tokens`, and prompt estimate if available); exact tokenizer-backed counts are deferred unless already available locally\n- [ ] Work units = tracker-computed layer span per hop at route build time (~1781–1782)\n- [ ] Nodes cannot increase payout by lying about shard range mid-request\n- [ ] Integration test: malicious node metadata does not inflate `charge_request` shares\n\n## ADR links\n\n- [ADR-0018](../../docs/adr/0018-fraud-detection-verification-and-reputation.md) §5\n\n## Blocked by\n\n- `02-a2-unified-auth-boundary.md`",
"acceptanceCriteria":[
"Streaming token count uses tracker-observed chunks/tokens; upstream `usage.total_tokens` can only lower or match that observed count, never inflate it",
"Non-streaming token count caps upstream `usage.total_tokens` by tracker-known request bounds (`max_tokens`, and prompt estimate if available); exact tokenizer-backed counts are deferred unless already available locally",
"Work units = tracker-computed layer span per hop at route build time (~1781\u20131782)",
"Work units = tracker-computed layer span per hop at route build time (~1781–1782)",
"Nodes cannot increase payout by lying about shard range mid-request",
"Integration test: malicious node metadata does not inflate `charge_request` shares",
"Keep the implementation scoped to this issue; do not refactor unrelated server.py areas",
@@ -84,63 +82,63 @@
"Run relevant pytest tests; run the full suite when practical or document why not"
"description":"# 11 \u2014 C6: Wallet binding ownership proof + binding overwrite safety\n\n## What to build\n\n`POST /v1/wallet/register` binds a client Solana wallet to an API key for deposit attribution. Today any Bearer key can bind any wallet string without proving ownership. Prevent hijack and accidental overwrite.\n\n**Code refs:**\n\n- `packages/tracker/meshnet_tracker/server.py` \u2014 `_handle_wallet_register` (~2625\u20132648)\n- `packages/tracker/meshnet_tracker/billing.py` \u2014 `bind_wallet` (~153+), `_wallet_bindings` / direct overwrite on apply (~351)\n\nRequire signed message from wallet pubkey (ed25519 via `cryptography` / solders). Reject rebinding without admin or signed release. Use explicit overwrite policy \u2014 today `~351` overwrites binding directly; gossip apply must reject conflicting binds instead of silently clobbering.\n\n## Test-first\n\n1. Red: bind wallet A with only API key, no signature \u2014 must fail after fix.\n2. Red: wallet already bound to key1; key2 cannot steal without proof.\n3. Green: valid signature binds; deposit watcher credits correct API key.\n\n## Acceptance criteria\n\n- [ ] Wallet binding requires cryptographic proof of pubkey ownership\n- [ ] One wallet \u2192 one API key (or documented admin override)\n- [ ] Gossip `bind` events cannot overwrite existing binding via direct overwrite at `~351`\n- [ ] Tests with deterministic keypairs (local adapter)\n\n## ADR links\n\n- [ADR-0017](../../docs/adr/0017-tracker-authentication-and-authorization.md) \u00a75\n- [ADR-0015](../../docs/adr/0015-usdt-custodial-settlement.md)\n\n## Blocked by\n\n- `02-a2-unified-auth-boundary.md`\n- `03-c5-starting-credit-zero.md`",
"description":"# 11 — C6: Wallet binding ownership proof + binding overwrite safety\n\n## What to build\n\n`POST /v1/wallet/register` binds a client Solana wallet to an API key for deposit attribution. Today any Bearer key can bind any wallet string without proving ownership. Prevent hijack and accidental overwrite.\n\n**Code refs:**\n\n- `packages/tracker/meshnet_tracker/server.py` — `_handle_wallet_register` (~2625–2648)\n- `packages/tracker/meshnet_tracker/billing.py` — `bind_wallet` (~153+), `_wallet_bindings` / direct overwrite on apply (~351)\n\nRequire signed message from wallet pubkey (ed25519 via `cryptography` / solders). Reject rebinding without admin or signed release. Use explicit overwrite policy — today `~351` overwrites binding directly; gossip apply must reject conflicting binds instead of silently clobbering.\n\n## Test-first\n\n1. Red: bind wallet A with only API key, no signature — must fail after fix.\n2. Red: wallet already bound to key1; key2 cannot steal without proof.\n3. Green: valid signature binds; deposit watcher credits correct API key.\n\n## Acceptance criteria\n\n- [ ] Wallet binding requires cryptographic proof of pubkey ownership\n- [ ] One wallet → one API key (or documented admin override)\n- [ ] Gossip `bind` events cannot overwrite existing binding via direct overwrite at `~351`\n- [ ] Tests with deterministic keypairs (local adapter)\n\n## ADR links\n\n- [ADR-0017](../../docs/adr/0017-tracker-authentication-and-authorization.md) §5\n- [ADR-0015](../../docs/adr/0015-usdt-custodial-settlement.md)\n\n## Blocked by\n\n- `02-a2-unified-auth-boundary.md`\n- `03-c5-starting-credit-zero.md`",
"acceptanceCriteria":[
"Wallet binding requires cryptographic proof of pubkey ownership",
"One wallet \u2192 one API key (or documented admin override)",
"One wallet → one API key (or documented admin override)",
"Gossip `bind` events cannot overwrite existing binding via direct overwrite at `~351`",
"Tests with deterministic keypairs (local adapter)",
"Keep the implementation scoped to this issue; do not refactor unrelated server.py areas",
@@ -240,38 +238,37 @@
"Run relevant pytest tests; run the full suite when practical or document why not"
"notes":"Source issue: .scratch/alpha-hardening/issues/12-c2-on-chain-idempotency.md\nRalph skip: Source issue is ready-for-human/deferred; skipped by unattended Ralph auto.",
"dependsOn":[],
"status_reason":"Source issue is ready-for-human/deferred; skipped by unattended Ralph auto."
"completionNotes":"Source issue is ready-for-human/deferred; skipped by unattended Ralph auto."
"notes":"Source issue: .scratch/alpha-hardening/issues/13-c3-c4-consensus-gated-settlement.md\nRalph skip: Source issue is ready-for-human/deferred; skipped by unattended Ralph auto.",
"dependsOn":[
"AH-012",
"AH-014"
],
"status_reason":"Source issue is ready-for-human/deferred; skipped by unattended Ralph auto."
"completionNotes":"Source issue is ready-for-human/deferred; skipped by unattended Ralph auto."
},
{
"id":"AH-014",
"title":"14 \u2014 A3: Durable Raft term and vote state (deferred)",
"description":"# 14 \u2014 A3: Durable Raft term and vote state (deferred)\n\n## What to build\n\nPersist Raft `currentTerm`, `votedFor`, and log metadata to disk. In-memory-only term (~26) risks split leadership after tracker restart \u2192 duplicate settlement epochs.\n\n**Code refs:**\n\n- `packages/tracker/meshnet_tracker/raft.py` \u2014 `LogEntry.term` (~25\u201327), election state in `RaftNode`\n\n## Acceptance criteria\n\n- [ ] Term/vote persisted alongside tracker data dir\n- [ ] Restart resumes as follower/candidate with monotonic term\n- [ ] Test: kill leader mid-settlement, restart, no duplicate payout batch\n\n## ADR links\n\n- [ADR-0019](../../docs/adr/0019-money-path-consistency-multi-tracker.md) \u00a73\n\n## Blocked by\n\nAlpha single-settlement posture",
"title":"14 — A3: Durable Raft term and vote state (deferred)",
"description":"# 14 — A3: Durable Raft term and vote state (deferred)\n\n## What to build\n\nPersist Raft `currentTerm`, `votedFor`, and log metadata to disk. In-memory-only term (~26) risks split leadership after tracker restart → duplicate settlement epochs.\n\n**Code refs:**\n\n- `packages/tracker/meshnet_tracker/raft.py` — `LogEntry.term` (~25–27), election state in `RaftNode`\n\n## Acceptance criteria\n\n- [ ] Term/vote persisted alongside tracker data dir\n- [ ] Restart resumes as follower/candidate with monotonic term\n- [ ] Test: kill leader mid-settlement, restart, no duplicate payout batch\n\n## ADR links\n\n- [ADR-0019](../../docs/adr/0019-money-path-consistency-multi-tracker.md) §3\n\n## Blocked by\n\nAlpha single-settlement posture",
"acceptanceCriteria":[
"Term/vote persisted alongside tracker data dir",
"Restart resumes as follower/candidate with monotonic term",
@@ -304,16 +300,15 @@
"Run relevant pytest tests; run the full suite when practical or document why not"
"notes":"Source issue: .scratch/alpha-hardening/issues/14-a3-raft-durable-term-vote.md\nRalph skip: Source issue is ready-for-human/deferred; skipped by unattended Ralph auto.",
"dependsOn":[],
"status_reason":"Source issue is ready-for-human/deferred; skipped by unattended Ralph auto."
"completionNotes":"Source issue is ready-for-human/deferred; skipped by unattended Ralph auto."
"notes":"Source issue: .scratch/alpha-hardening/issues/15-h1-commutative-forfeit.md\nRalph skip: Source issue is ready-for-human/deferred; skipped by unattended Ralph auto.",
"dependsOn":[
"AH-013"
],
"status_reason":"Source issue is ready-for-human/deferred; skipped by unattended Ralph auto."
"completionNotes":"Source issue is ready-for-human/deferred; skipped by unattended Ralph auto."
"description":"# 17 \u2014 DOC: Duplicate US-020 issue dedup\n\n## What to build\n\nTwo files share the US-020 number with different slugs:\n\n- `docs/issues/20-memory-budget-shard-slots-and-dropout-relocation.md` (ready-for-agent)\n- `docs/issues/20-tracker-node-hardening.md` (done)\n\nResolve numbering collision without losing history.\n\n## Acceptance criteria\n\n- [ ] Document canonical mapping in this issue's Comments or a short `docs/issues/README.md` note\n- [ ] Renumber or prefix disambiguation (e.g. keep done item as US-020a, renumber memory-budget to next slot) \u2014 **human approval before git mv**\n- [ ] Update any prd.json / cross-links that reference US-020 ambiguously\n- [ ] No production code changes\n\n## Blocked by\n\nHuman approval for renumbering. An agent may prepare the mapping note, but must not run `git mv` or rewrite cross-links until the canonical number is approved.",
"title":"17 — DOC: Duplicate US-020 issue dedup",
"description":"# 17 — DOC: Duplicate US-020 issue dedup\n\n## What to build\n\nTwo files share the US-020 number with different slugs:\n\n- `docs/issues/20-memory-budget-shard-slots-and-dropout-relocation.md` (ready-for-agent)\n- `docs/issues/20-tracker-node-hardening.md` (done)\n\nResolve numbering collision without losing history.\n\n## Acceptance criteria\n\n- [ ] Document canonical mapping in this issue's Comments or a short `docs/issues/README.md` note\n- [ ] Renumber or prefix disambiguation (e.g. keep done item as US-020a, renumber memory-budget to next slot) — **human approval before git mv**\n- [ ] Update any prd.json / cross-links that reference US-020 ambiguously\n- [ ] No production code changes\n\n## Blocked by\n\nHuman approval for renumbering. An agent may prepare the mapping note, but must not run `git mv` or rewrite cross-links until the canonical number is approved.",
"acceptanceCriteria":[
"Document canonical mapping in this issue's Comments or a short `docs/issues/README.md` note",
"Renumber or prefix disambiguation (e.g. keep done item as US-020a, renumber memory-budget to next slot) \u2014 **human approval before git mv**",
"Renumber or prefix disambiguation (e.g. keep done item as US-020a, renumber memory-budget to next slot) — **human approval before git mv**",
"Update any prd.json / cross-links that reference US-020 ambiguously",
"No production code changes",
"Keep the implementation scoped to this issue; do not refactor unrelated server.py areas",
@@ -367,16 +361,15 @@
"Run relevant pytest tests; run the full suite when practical or document why not"
"notes":"Source issue: .scratch/alpha-hardening/issues/17-doc-duplicate-us020-dedup.md\nRalph skip: Source issue is ready-for-human/deferred; skipped by unattended Ralph auto.",
"dependsOn":[],
"status_reason":"Source issue is ready-for-human/deferred; skipped by unattended Ralph auto."
"completionNotes":"Source issue is ready-for-human/deferred; skipped by unattended Ralph auto."
"title":"19 \u2014 DOC: Cryptography dependency + test environment note",
"description":"# 19 \u2014 DOC: Cryptography dependency + test environment note\n\n## What to build\n\nDocument and verify test/dev environment setup for wallet crypto paths. `packages/node/meshnet_node/wallet.py` uses `cryptography`; failures occur when `.venv` lacks deps. `cryptography>=41` is already declared in `packages/node/pyproject.toml`, so this issue should focus on documenting the editable-install path and only add root/dev extras if tests still import the node wallet without installing the node package.\n\n**Code refs:**\n\n- `packages/node/pyproject.toml` \u2014 `cryptography>=41` (verify declared)\n- `packages/node/meshnet_node/wallet.py`\n- Handoff: tests fail without `cryptography`, `openai`, `langchain` in `.venv`\n\n## Acceptance criteria\n\n- [ ] Confirm `cryptography>=41` remains in node package deps; add to root/dev extras only if tests import wallet without node install\n- [ ] Add short **Test environment** section to `docs/dev/test-env.md` (or `CONTRIBUTING.md` if created): use `.venv/Scripts/python.exe`, `pip install -e packages/node ...`, optional dep skips\n- [ ] Note which tests require optional deps (`--ignore=test_openai_gateway,...`)\n- [ ] No unrelated production code changes\n\n## Blocked by\n\nNone",
"title":"19 — DOC: Cryptography dependency + test environment note",
"description":"# 19 — DOC: Cryptography dependency + test environment note\n\n## What to build\n\nDocument and verify test/dev environment setup for wallet crypto paths. `packages/node/meshnet_node/wallet.py` uses `cryptography`; failures occur when `.venv` lacks deps. `cryptography>=41` is already declared in `packages/node/pyproject.toml`, so this issue should focus on documenting the editable-install path and only add root/dev extras if tests still import the node wallet without installing the node package.\n\n**Code refs:**\n\n- `packages/node/pyproject.toml` — `cryptography>=41` (verify declared)\n- `packages/node/meshnet_node/wallet.py`\n- Handoff: tests fail without `cryptography`, `openai`, `langchain` in `.venv`\n\n## Acceptance criteria\n\n- [ ] Confirm `cryptography>=41` remains in node package deps; add to root/dev extras only if tests import wallet without node install\n- [ ] Add short **Test environment** section to `docs/dev/test-env.md` (or `CONTRIBUTING.md` if created): use `.venv/Scripts/python.exe`, `pip install -e packages/node ...`, optional dep skips\n- [ ] Note which tests require optional deps (`--ignore=test_openai_gateway,...`)\n- [ ] No unrelated production code changes\n\n## Blocked by\n\nNone",
"acceptanceCriteria":[
"Confirm `cryptography>=41` remains in node package deps; add to root/dev extras only if tests import wallet without node install",
"Add short **Test environment** section to `docs/dev/test-env.md` (or `CONTRIBUTING.md` if created): use `.venv/Scripts/python.exe`, `pip install -e packages/node ...`, optional dep skips",
@@ -406,18 +399,18 @@
"Run relevant pytest tests; run the full suite when practical or document why not"
"title":"20 \u2014 Validator service token for `/v1/billing/forfeit`",
"description":"# 20 \u2014 Validator service token for `/v1/billing/forfeit`\n\n## What to build\n\nDefine and implement a **validator service token** distinct from client API keys and admin sessions. The validator process must authenticate when calling `POST /v1/billing/forfeit`; arbitrary Bearer strings and client API keys must be rejected. This is a checklist subtask for issue 02 and should normally land in the same PR as the unified auth middleware.\n\nPer [ADR-0017 \u00a74](../../docs/adr/0017-tracker-authentication-and-authorization.md): forfeit accepts **validator service identity or admin session** only.\n\n## Configuration\n\n| Item | Alpha default |\n|---|---|\n| Env var | `MESHNET_VALIDATOR_SERVICE_TOKEN` (tracker + validator) |\n| Config flag | `--validator-service-token` / tracker config file equivalent |\n| Header format | `Authorization: Bearer <service-token>` with a dedicated prefix or separate header scheme documented in runbooks (e.g. `Authorization: Service <token>` \u2014 pick one and test consistently) |\n| Rotation | Manual: set new token on tracker + validator, restart both; document zero-downtime rotation as post-alpha |\n\n## Rejection rules\n\n- Client API keys (`sk-mesh-\u2026`) \u2192 **403** on forfeit (even if valid for inference)\n- Non-empty garbage Bearer \u2192 **401/403**\n- Missing auth \u2192 **401**\n- Valid validator service token \u2192 **200** (existing forfeit semantics)\n- Admin session \u2192 **200** (operator override)\n\n## Test-first\n\n1. Red: validator (or test client) posts forfeit with a valid API key \u2014 must fail after fix.\n2. Red: `Authorization: Bearer garbage` \u2014 must fail (covered by issue 02; this issue defines the accepted token).\n3. Green: configured service token succeeds; wrong token fails.\n\n## Acceptance criteria\n\n- [ ] Service token configurable via env/flag on tracker and validator\n- [ ] Unified auth middleware resolves service token \u2192 `validator` role (issue 02)\n- [ ] API keys explicitly rejected on forfeit path\n- [ ] Integration test: validator client with service token forfeit succeeds; API key forfeit fails\n- [ ] Runbook stub: rotation procedure (manual alpha)\n\n## ADR links\n\n- [ADR-0017](../../docs/adr/0017-tracker-authentication-and-authorization.md) \u00a74\n\n## Related\n\n- `02-a2-unified-auth-boundary.md` \u2014 middleware + role checks\n\n## Blocked by\n\n- `02-a2-unified-auth-boundary.md`",
"title":"20 — Validator service token for `/v1/billing/forfeit`",
"description":"# 20 — Validator service token for `/v1/billing/forfeit`\n\n## What to build\n\nDefine and implement a **validator service token** distinct from client API keys and admin sessions. The validator process must authenticate when calling `POST /v1/billing/forfeit`; arbitrary Bearer strings and client API keys must be rejected. This is a checklist subtask for issue 02 and should normally land in the same PR as the unified auth middleware.\n\nPer [ADR-0017 §4](../../docs/adr/0017-tracker-authentication-and-authorization.md): forfeit accepts **validator service identity or admin session** only.\n\n## Configuration\n\n| Item | Alpha default |\n|---|---|\n| Env var | `MESHNET_VALIDATOR_SERVICE_TOKEN` (tracker + validator) |\n| Config flag | `--validator-service-token` / tracker config file equivalent |\n| Header format | `Authorization: Bearer <service-token>` with a dedicated prefix or separate header scheme documented in runbooks (e.g. `Authorization: Service <token>` — pick one and test consistently) |\n| Rotation | Manual: set new token on tracker + validator, restart both; document zero-downtime rotation as post-alpha |\n\n## Rejection rules\n\n- Client API keys (`sk-mesh-…`) → **403** on forfeit (even if valid for inference)\n- Non-empty garbage Bearer → **401/403**\n- Missing auth → **401**\n- Valid validator service token → **200** (existing forfeit semantics)\n- Admin session → **200** (operator override)\n\n## Test-first\n\n1. Red: validator (or test client) posts forfeit with a valid API key — must fail after fix.\n2. Red: `Authorization: Bearer garbage` — must fail (covered by issue 02; this issue defines the accepted token).\n3. Green: configured service token succeeds; wrong token fails.\n\n## Acceptance criteria\n\n- [ ] Service token configurable via env/flag on tracker and validator\n- [ ] Unified auth middleware resolves service token → `validator` role (issue 02)\n- [ ] API keys explicitly rejected on forfeit path\n- [ ] Integration test: validator client with service token forfeit succeeds; API key forfeit fails\n- [ ] Runbook stub: rotation procedure (manual alpha)\n\n## ADR links\n\n- [ADR-0017](../../docs/adr/0017-tracker-authentication-and-authorization.md) §4\n\n## Related\n\n- `02-a2-unified-auth-boundary.md` — middleware + role checks\n\n## Blocked by\n\n- `02-a2-unified-auth-boundary.md`",
"acceptanceCriteria":[
"Service token configurable via env/flag on tracker and validator",
"Unified auth middleware resolves service token \u2192 `validator` role (issue 02)",
"Unified auth middleware resolves service token → `validator` role (issue 02)",
"API keys explicitly rejected on forfeit path",
"Integration test: validator client with service token forfeit succeeds; API key forfeit fails",
"description":"# 21 \u2014 Honest-noise TOPLOC calibration corpus\n\n## What to build\n\nBefore enabling production TOPLOC audit thresholds, collect an **honest-noise baseline** across the heterogeneous volunteer fleet. Run identical inference jobs on every active node/GPU combo; measure the divergence envelope (TOPLOC exponent/mantissa deltas, logprob-rank spread) under real hardware variance.\n\nPer [ADR-0018 consequences](../../docs/adr/0018-fraud-detection-verification-and-reputation.md): threshold calibration requires an honest-noise corpus across the fleet before production thresholds.\n\nResearch anchor: `.scratch/alpha-hardening/research-verifiable-inference.md` \u00a78 layer 3 \u2014 \"collect this first \u2014 run identical jobs across the current node fleet to measure the honest divergence envelope before setting thresholds.\"\n\n## Deliverables\n\n- [ ] Scripted benchmark job (fixed prompt, model preset, seed policy) runnable on all nodes\n- [ ] Aggregated corpus artifact (per node: GPU model, dtype, TOPLOC deltas vs reference)\n- [ ] Recommended tolerance thresholds documented (p99 honest envelope + safety margin)\n- [ ] Gate checklist: production audit enable blocked until corpus covers \u2265N distinct hardware profiles (define N in runbook, suggest \u22653)\n\n## Acceptance criteria\n\n- [ ] Corpus collected from current fleet (or documented subset + extrapolation note)\n- [ ] Threshold constants in validator config derived from corpus, not guessed\n- [ ] False-positive rate estimate documented at chosen thresholds\n- [ ] README / runbook cross-link: **do not enable production audits** until this issue closes\n\n## ADR links\n\n- [ADR-0018](../../docs/adr/0018-fraud-detection-verification-and-reputation.md) \u2014 Consequences (honest-noise corpus)\n\n## Blocked by\n\n- `06-fraud-toploc-integration.md` (TOPLOC wired; calibration uses same primitive)\n\n## Blocks (prod gate)\n\n- Production enable of adaptive audit thresholds (issues 09\u201310 in prod)",
"description":"# 21 — Honest-noise TOPLOC calibration corpus\n\n## What to build\n\nBefore enabling production TOPLOC audit thresholds, collect an **honest-noise baseline** across the heterogeneous volunteer fleet. Run identical inference jobs on every active node/GPU combo; measure the divergence envelope (TOPLOC exponent/mantissa deltas, logprob-rank spread) under real hardware variance.\n\nPer [ADR-0018 consequences](../../docs/adr/0018-fraud-detection-verification-and-reputation.md): threshold calibration requires an honest-noise corpus across the fleet before production thresholds.\n\nResearch anchor: `.scratch/alpha-hardening/research-verifiable-inference.md` §8 layer 3 — \"collect this first — run identical jobs across the current node fleet to measure the honest divergence envelope before setting thresholds.\"\n\n## Deliverables\n\n- [ ] Scripted benchmark job (fixed prompt, model preset, seed policy) runnable on all nodes\n- [ ] Aggregated corpus artifact (per node: GPU model, dtype, TOPLOC deltas vs reference)\n- [ ] Recommended tolerance thresholds documented (p99 honest envelope + safety margin)\n- [ ] Gate checklist: production audit enable blocked until corpus covers ≥N distinct hardware profiles (define N in runbook, suggest ≥3)\n\n## Acceptance criteria\n\n- [ ] Corpus collected from current fleet (or documented subset + extrapolation note)\n- [ ] Threshold constants in validator config derived from corpus, not guessed\n- [ ] False-positive rate estimate documented at chosen thresholds\n- [ ] README / runbook cross-link: **do not enable production audits** until this issue closes\n\n## ADR links\n\n- [ADR-0018](../../docs/adr/0018-fraud-detection-verification-and-reputation.md) — Consequences (honest-noise corpus)\n\n## Blocked by\n\n- `06-fraud-toploc-integration.md` (TOPLOC wired; calibration uses same primitive)\n\n## Blocks (prod gate)\n\n- Production enable of adaptive audit thresholds (issues 09–10 in prod)",
"acceptanceCriteria":[
"Corpus collected from current fleet (or documented subset + extrapolation note)",
"Threshold constants in validator config derived from corpus, not guessed",
@@ -448,21 +440,20 @@
"Run relevant pytest tests; run the full suite when practical or document why not"
"notes":"Source issue: .scratch/alpha-hardening/issues/21-honest-noise-calibration-corpus.md\nRalph skip: Source issue is ready-for-human/deferred; skipped by unattended Ralph auto.",
"dependsOn":[
"AH-006"
],
"status_reason":"Source issue is ready-for-human/deferred; skipped by unattended Ralph auto."
"completionNotes":"Source issue is ready-for-human/deferred; skipped by unattended Ralph auto."
pauses until the leader recovers; no funds are at risk since payouts require
the loaded keypair.
## Secrets handling
- Never commit `.env.devnet`, `--hive-secret` / `MESHNET_HIVE_SECRET`,
`--validator-service-token`, or the treasury keypair file as part of a
deploy/config change. Deploy scripts should read these from the existing
secrets store, not from a file checked into the repo.
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.