Status: done

# 11 — C6: Wallet binding ownership proof + binding overwrite safety

## What to build

`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.

**Code refs:**

- `packages/tracker/meshnet_tracker/server.py` — `_handle_wallet_register` (~2625–2648)
- `packages/tracker/meshnet_tracker/billing.py` — `bind_wallet` (~153+), `_wallet_bindings` / direct overwrite on apply (~351)

Require 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.

## Test-first

1. Red: bind wallet A with only API key, no signature — must fail after fix.
2. Red: wallet already bound to key1; key2 cannot steal without proof.
3. Green: valid signature binds; deposit watcher credits correct API key.

## Acceptance criteria

- [x] Wallet binding requires cryptographic proof of pubkey ownership
- [x] One wallet → one API key (or documented admin override)
- [x] Gossip `bind` events cannot overwrite existing binding via direct overwrite at `~351`
- [x] Tests with deterministic keypairs (local adapter)

## ADR links

- [ADR-0017](../../docs/adr/0017-tracker-authentication-and-authorization.md) §5
- [ADR-0015](../../docs/adr/0015-usdt-custodial-settlement.md)

## Blocked by

- `02-a2-unified-auth-boundary_completed.md`
- `03-c5-starting-credit-zero_completed.md`
