# Soft-hash covert channel analysis

**Ticket:** #000018
**Authored:** 2026-05-09
**Audience:** fox + future blackops shifts + downstream Merkle-AGI v7+ authors
**Stance:** defensive only — detection / flagging / bound-derivation, never
generation or concealment.

---

## 0. Executive summary

We analyze whether an adversary controlling some fraction of training
data, gradient computation, or hyperparameter scheduling can drive a
v7-§9 plastic-trained model so that its **next snapshot's hard hash
`C(M_{t+1})`** lands on a prearranged target value or value-class,
exploiting the soft-hash anchor map `φ` as a side-channel.

Result, under the assumptions stated in §5:

> **Under threat models T1 (data poisoner) and T2 (gradient adversary),
> the channel is bounded by a SHA-256 partial-preimage hardness
> reduction (§4). Under T3 (hyperparameter adversary), the channel is
> bounded by the per-checkpoint mitigation budget M2 (re-randomized
> anchors); residual channel without M2 is at most O(log N · entropy
> of training-data choices) bits per checkpoint window.**

Recommended mitigation: **M2 (per-checkpoint anchor nonce)**, costing
one additional commit field per snapshot. Falls back to **M1 (PRG-based
φ)** if a deployment surfaces residual structure. M3 (drop the anchor)
is the strict-construction fallback if both fail; loses some training
ergonomics.

This document is doc-only per the ticket's hard constraint: it
recommends, does not implement. A follow-up ticket lands the chosen
mitigation.

---

## 1. Threat model

Three adversary capabilities, increasing in strength.

### 1.1 T1 — Data poisoner

Adversary controls a fraction `f ∈ (0, 1)` of training-data examples.
Training loop is honest; aggregator is honest; hyperparameters are
honest; `φ` is the published deterministic embedding. Poisoned
examples are valid in distribution (otherwise an outlier filter
catches them).

**Goal:** poisoned examples drive `Q(Θ_{t+1})` to a region whose soft
hash `h_v(t+1)` projects to a SHA-256 bucket the adversary can predict.
Concretely: adversary tries to land `C(M_{t+1})` in a target class
`T ⊂ {0,1}^{256}` with `|T|/2^{256} = ε` (so the 'class' could be
"first byte 0xAA" → ε = 2^-8, or "specific 256-bit value" → ε = 2^-256).

### 1.2 T2 — Gradient adversary

T1 + adversary controls a fraction `g` of gradient computations
(e.g., compromised data-parallel worker that returns dishonest local
gradients to the central aggregator). Aggregator is still honest.

Adversary can bias the gradient signal in arbitrary directions —
not bounded to legitimate-data-distribution reach. Tighter than T1.

### 1.3 T3 — Hyperparameter adversary

T2 + adversary controls some learning-rate / scheduler / batch-order
decisions. Adversary can synchronize gradient updates with anchor
geometry to maximize alignment. Strictly stronger than T2.

### 1.4 What's NOT in scope

- Direct attack on `C(M_t)` itself (preimage-attack on SHA-256). Out
  of scope by A3 in v7 §3 (collision-resistant hash assumption).
- Side-channel on training infrastructure (Spectre, RowHammer,
  compromised CPU). Different threat model.
- Adversary controlling the published `φ` seed retroactively. Once
  φ's seed is public, it's frozen; this ticket assumes seed is
  honestly drawn at v7 boot.
- Compromised aggregator that lies about the snapshot bytes.
  Different threat model — that's a checkpoint integrity attack,
  not a covert channel.

---

## 2. v7 § 9 plastic training, recap

The relevant slice of the v7 spec:

```
Θ        — model parameters (continuous, ~10^9 floats)
Q(Θ)     — public quantization of Θ (SQD §3, integer kernel)
C(M)     — SHA-256(canonical-encoding of Q(Θ) ∥ metadata)
                  ↑ THE HARD HASH — committed bytes
h_v(Θ)   — soft hash, dim_h-vector function of Θ
                  ↑ THE SOFT HASH — used during training only
φ(C(M))  — anchor map: SHA-256 digest → dim_h vector
                  ↑ FIXED DETERMINISTIC, published seed
```

Plastic training loss at step t:

```
L_total(Θ_t) = L_task(Θ_t) + α · ‖h_v(Θ_t) - φ(C(M_{t-1}))‖²
                                     ↑ L2 anchor pull
```

The anchor term pulls the model's soft hash toward the embedding of
the *previous* checkpoint's hard hash. At each checkpoint boundary t →
t+1, we compute Q(Θ_{t+1}) → C(M_{t+1}) and that becomes the next
anchor.

The dual-hash separation theorem (T6, v7 §3): C(M) is determined
solely by Q(Θ) and metadata; soft-hash bytes never enter the
SHA-256 preimage. **First-order conclusion:** training cannot
directly affect committed bytes.

The covert-channel concern: training does affect Q(Θ) (that's what
training does). The question is whether the L2 anchor term lets the
adversary *steer* Q(Θ) toward a target SHA-256 bucket.

---

## 3. Channel capacity definition

Define the **per-checkpoint covert channel** as the mutual information

```
I_t = I(adversary inputs at training step ≤ t  ;  C(M_{t+1}))
      conditional on the public training history H_{≤t}
```

The conditioning on `H_{≤t}` (public training history: published
losses, public hyperparameters, public seeds) removes the
"adversary trivially knows what they did" channel. We measure the
bits the adversary can steer ABOVE the public information's
predictive power.

A non-adversarial bound: under random training, `C(M_{t+1})` is
near-uniform on `{0,1}^{256}` conditional on `H_{≤t}` if `Q(Θ_{t+1})`
is sufficiently varied. Adversarial bound is the question.

### 3.1 Capacity bound under random `φ`

If `φ` is modeled as a random oracle independent of the parameter
geometry, then `h_v - φ(C(M_{t-1}))` is a uniform random direction at
each checkpoint, and the L2 pull provides no parameter-aligned
gradient signal. Under this idealization, the covert channel
reduces to:

```
I_t ≤ I(adversary inputs ; Q(Θ_{t+1}))
       conditional on H_{≤t}
```

…which is the un-amplified channel — adversary is no better off
through the soft-hash side-channel than they would be attacking
Q(Θ) directly.

### 3.2 Capacity bound under structured `φ`

If `φ` has algebraic structure relative to parameter geometry (the
v7 reference `embed_hard_to_vec` is a fixed-seed linear projection
of the SHA-256 digest), the bound depends on whether gradient
updates that minimize the L2 anchor term are *parameter-aligned* to
shifts in `Q(Θ)` that move SHA-256 buckets predictably.

The reduction in §4 shows this dependency reduces to a SHA-256
partial-preimage problem.

---

## 4. Reduction to SHA-256 partial-preimage

### 4.1 Reduction sketch

Suppose adversary `A` exists that, given black-box training oracle
access under T1/T2, can drive `C(M_{t+1})` into a target class `T` of
density ε with non-negligible probability `p`.

Construct algorithm `B` that solves SHA-256 partial-preimage:

```
B(target T):
    1. Initialize a fresh v7 plastic-training run with random seed.
    2. Run A as a sub-routine, supplying T as the target class.
    3. After A signals success on checkpoint t+1, output Q(Θ_{t+1}).
    4. Q(Θ_{t+1}) is then a partial preimage of T under SHA-256.
```

`B`'s success probability is `p` minus the probability that `A`
succeeds via legitimate-Q-randomization (i.e., `B` would have
solved the problem by random Q-search anyway). Under the standard
random-oracle model for SHA-256, the random-Q baseline is `ε`. So:

```
P[B solves partial-preimage] ≥ p - ε
```

If `p > ε + δ` for any non-negligible `δ`, then `B` is a non-trivial
SHA-256 partial-preimage attacker — contradicting A3 (collision-
resistance / partial-preimage hardness).

**Conclusion:** under the SHA-256 random-oracle assumption (A3),
no T1/T2 adversary can succeed with probability non-negligibly
above ε. The channel exists but is bounded at the random baseline.

### 4.2 Where the reduction is tight, where it's loose

**Tight:** when `φ` is independent of parameter geometry (M1 or M2
applied), the reduction is essentially exact. T1/T2 are bounded to
the random baseline.

**Loose:** when `φ` has structure relative to parameter geometry
(reference v7 `embed_hard_to_vec` with fixed-seed linear projection),
the gradient feedback through the anchor MAY favor parameter
directions correlated with target SHA-256 buckets. We don't know
whether this correlation is large enough to break the reduction
without explicit structural analysis of `embed_hard_to_vec`.

This is the residual gap §6 addresses.

### 4.3 T3 caveat

T3 (hyperparameter adversary) is NOT bounded by the §4.1 reduction
directly. The reduction assumes A's queries to the training oracle
follow the published schedule. T3 violates that. Under T3:

- Adversary can synchronize gradient bursts with anchor geometry
  via learning-rate schedule manipulation.
- Per-checkpoint channel capacity grows with the adversary's
  schedule-control budget.

T3 needs M2 (re-randomized per-checkpoint anchor) or M3 (drop
anchor). M1 alone may not suffice.

---

## 5. Assumptions on `φ`

Three concrete `φ` choices, with channel-residual under each:

### 5.1 `φ_linear` — v7 reference (linear projection, fixed seed)

```
φ(digest) = W · digest_as_uint32_vector  (W frozen at boot)
```

- **Pros:** fast, deterministic, simple.
- **Channel residual:** unclear without explicit structural analysis
  of W relative to the parameter Hessian. If W has any direction
  aligned with a low-eigenvalue direction of the loss Hessian, the
  L2 pull provides cheap parameter movement that aligns with
  SHA-256 buckets — exploitable under T2 with empirical effort.
- **Verdict:** the reduction in §4 is loose under `φ_linear`.

### 5.2 `φ_PRG` — non-linear PRG-based map (mitigation M1)

```
φ(digest) = PRG(seed=published, output_len=dim_h * 4 bytes)
            then bytes → floats in [-1, 1]
```

PRG output is computationally indistinguishable from random under
the PRG security assumption. Gradient feedback through the anchor
is uncorrelated with parameter-space updates that would shift hash
bytes predictably.

- **Channel residual:** zero modulo PRG security (one-way function
  + no algebraic structure for the parameter Hessian to exploit).
- **Verdict:** §4 reduction is tight under `φ_PRG`.

### 5.3 `φ_nonce` — re-randomized anchor (mitigation M2)

```
At each checkpoint t, sample fresh random nonce n_t (committed
in the snapshot manifest). Compute anchor as
        φ(C(M_{t-1}) ∥ n_t)
where φ is φ_linear or φ_PRG. Adversary can't precompute against
the next anchor because n_t is unknown until commitment.
```

- **Channel residual:** even under `φ_linear` base, T1/T2 adversaries
  can't pre-plan attacks because the gradient direction they want
  to steer toward is randomized between checkpoints.
- **T3 residual:** still bounded — T3 adversary controls timing,
  not the nonce. Per-checkpoint window is `O(steps) × per-step
  channel`; the nonce reset zeroes the cumulative steerage.
- **Verdict:** lightweight; only adds 32 bytes to the manifest per
  checkpoint. Closes the residual channel for T1/T2 entirely;
  bounds T3 by a per-window budget.

### 5.4 `φ` dropped — anchor term removed (mitigation M3)

`L_total(Θ) = L_task(Θ) + (no anchor term)`.

- **Channel residual:** zero by construction (no soft-hash gradient
  signal at all).
- **Loss:** plastic training loses some ergonomics (stable
  representation between checkpoints relied on the anchor pull).
  Quantified loss: depends on deployment; estimated in v7 §9.7
  as 5-15% slower convergence on representation-stability metrics.
- **Verdict:** strict-construction fallback. Recommend only if M1
  + M2 prove insufficient empirically.

---

## 6. Mitigations with residual analysis

| Mitigation | Cost | T1 residual | T2 residual | T3 residual | Closes the channel? |
|------------|------|-------------|-------------|-------------|---------------------|
| **None** (v7 reference) | 0 | bounded by §4 reduction (loose under φ_linear) | bounded by §4 reduction (loose under φ_linear) | unbounded structurally | No |
| **M1 — φ_PRG**         | fast PRG eval per checkpoint | tight bound at random baseline ε | tight bound at random baseline ε | unbounded (T3 controls timing) | T1+T2 yes; T3 no |
| **M2 — per-checkpoint nonce** | 32 B in manifest per checkpoint | ε | ε | bounded by per-window budget | T1+T2 yes; T3 bounded |
| **M1 + M2** (combined) | both costs | ε | ε | bounded by per-window budget | T1+T2 yes; T3 bounded |
| **M3 — drop anchor**    | 5-15% training-ergonomics regression | 0 | 0 | 0 | Yes, by construction |
| **M4 — gradient-budget cap** | per-step gradient-norm clip | bounds adversary's per-step budget | bounds adversary's per-step budget | bounds T3 per-step | Soft bound; doesn't close |

---

## 7. Recommendation

**Land M2 first** (re-randomized per-checkpoint anchor):

- 32 bytes additional manifest field, committed in the snapshot.
- Closes the T1 + T2 channel under any `φ` choice.
- Bounds the T3 channel by per-checkpoint budget (the only
  attack window is between nonce-publish and next-checkpoint).
- No training-ergonomics regression — the nonce is purely
  per-checkpoint randomization, anchor pull still lands.

**Add M1 (φ_PRG) as Phase 2** if M2-only deployment surfaces
structural concerns (e.g., empirical evidence that T3 attacks
within a window achieve > ε success rate).

**M3 (drop anchor)** stays in reserve as the strict-construction
fallback. v7 §9 spec amendment proposed:

```
v7 §9.10 (proposed): The anchor map φ takes both the prior
checkpoint's hard hash AND a per-checkpoint nonce committed in
the manifest. The nonce is fresh entropy at each checkpoint,
sealed before training resumes. This closes the soft-hash covert
channel under threat models T1 and T2, and bounds T3 by the
per-checkpoint window.
```

---

## 8. Empirical validation (out of scope; follow-up)

A small experiment to sanity-check the analysis:

1. Run v7 §9 plastic training with a deliberately-poisoned dataset
   targeting "first byte of `C(M)` equals 0xAA" — that's `ε = 2^-8`.
2. With M2 disabled (`φ_linear`, no nonce): measure success rate
   over N checkpoints. Hypothesis: indistinguishable from `ε` if
   §4 reduction holds under `φ_linear`; structurally elevated if
   `φ_linear` has Hessian-aligned structure.
3. With M2 enabled (`φ_linear`, per-checkpoint nonce): measure
   again. Hypothesis: indistinguishable from `ε` regardless of
   `φ_linear`'s structure.
4. Same experiment under `φ_PRG`: should match `ε` baseline with
   or without M2.

If empirical results diverge from analysis predictions, the
analysis is wrong somewhere — likely in §5 about `φ_linear`'s
structural alignment with the parameter Hessian.

This experiment is a follow-up ticket; not in scope here.

---

## 9. Open questions

The reduction in §4 leaves three loose threads:

1. **Hessian alignment under `φ_linear`.** Is the v7 reference
   `embed_hard_to_vec` matrix W aligned with low-eigenvalue
   directions of the typical training-loss Hessian? If yes, T2
   adversaries with full gradient control can amplify steerage
   beyond the §4 random-oracle baseline. **Test:** compute the
   spectrum of `W^T W` and compare to typical Hessian eigenvalue
   distributions on representative checkpoints.

   **Phase 1a landed 2026-05-10** under #000034: synthetic-
   ablation infrastructure at `bench/scripts/phi_alignment_probe.py`
   (`measure_alignment(W, hessian_eval, *, k_top, k_bot, epsilon)
   → AlignmentReport`; Lanczos top-k + bottom-k via
   `scipy.sparse.linalg.eigsh`). <!--AUTOCOUNT:tests:tests/test_phi_alignment_probe.py-->23<!--/AUTOCOUNT--> tests in
   `tests/test_phi_alignment_probe.py` covering verdict
   classification (`STRUCTURAL_ALIGNMENT`, `NO_ALIGNMENT`,
   `ANTI_ALIGNED`), boundary cases, monotonicity, closure
   (full-spectrum sum), parametrized invalid-input cones, and
   KAT regression. KAT fixture (<!--AUTOCOUNT:fixture-rows:bench/fixtures/phi-alignment/synthetic-checkpoints.jsonl-->30<!--/AUTOCOUNT--> vectors:
   10 aligned + 10 uniform + 10 anti-aligned synthetic
   checkpoints) at `bench/fixtures/phi-alignment/synthetic-checkpoints.jsonl`
   pins synthetic-checkpoint verdicts so the probe is regression-
   guarded ahead of v7 deployment ramp-up. Lands under
   `bench/scripts/` (measurement tool, not a substrate primitive
   like `arborist/substrate/anchor_prg.py`). **Phase 1b** runs
   the probe against a real v7 checkpoint when one becomes
   available; the alignment-score distribution then resolves
   this open question and feeds C_B1 tightening per #000043.

2. **PRG modeling for `φ_PRG`.** The §4 reduction treats `φ_PRG`
   as a random oracle. In practice it's a deterministic PRG with
   public seed. The reduction holds modulo PRG security; we
   should pick a PRG with cryptographic strength matching SHA-256
   (e.g., AES-256-CTR with public IV; or HMAC-SHA-512 expansion).
   **Decision:** use HMAC-SHA-512(seed, digest ∥ counter) for
   dim_h floats. Keeps the dependency surface (already on SHA-256)
   tight.

   **Phase 1 landed 2026-05-10** under #000035: reference
   implementation at `arborist/substrate/anchor_prg.py`
   (HMAC-SHA-512 counter-mode KDF, pure stdlib — `hashlib` +
   `hmac`, no third-party crypto dep). <!--AUTOCOUNT:tests:tests/test_anchor_prg.py-->31<!--/AUTOCOUNT--> tests in
   `tests/test_anchor_prg.py` covering determinism, range
   invariants, chi² uniformity, dim_h boundary (1, 16384),
   seed-bit-flip and hash-bit-flip avalanche, hand-computed
   HMAC-SHA-512 first-block formula, prefix-extension closure,
   parametrized invalid-input cones, input validation,
   module-export shape, and KAT regression. <!--AUTOCOUNT:fixture-rows:bench/fixtures/phi-prg/known-answer-tests.jsonl-->10<!--/AUTOCOUNT--> pinned KAT
   vectors at `bench/fixtures/phi-prg/known-answer-tests.jsonl`
   covering block-boundary cases (dim_h=16 = one HMAC block;
   dim_h=17 = two blocks with truncation), one-bit-flip
   variants, and a 4096-element counter-rollover stress sample.
   Module exports `PHI_PRG_VERSION = "phi-prg-v1-hmac-sha512-le"`
   so future algorithm rotation is detectable at the call site
   without string-comparing module paths.

   **dav1d-reviewed §9.10 spec amendment text (2026-05-11)** —
   #000035 §3.4 holds the final wording. Decisions: HMAC-SHA-512
   LOCK; **manifest field `anchor_prg_seed`** (purpose-scoped;
   `phi_prg_seed` is a code-local alias only); 32-byte seed LOCK;
   **`uint32` little-endian counter + uint32-word reads from 0** —
   matching v7's canonical TLV integer encoding (`merkle-agi-dag_v7.txt`
   §A1: TLV length prefixes + `enc_int` are `to_bytes(…, "little")`,
   tensors are `<i8`). dav1d's review rule said "if v7 TLV is
   little-endian, flip §3.4 to little-endian before KAT freeze" —
   done; `PHI_PRG_VERSION → "phi-prg-v1-hmac-sha512-le"`, the 10
   KAT vectors regenerated under the little-endian counter. Float
   map `2·(u32_le/2^32)−1`; prose says "uniform over a 2^32-point
   grid in [-1, 1) with negligible finite-grid mean −2^−32" rather
   than "unbiased"; `SHALL` replace `embed_hard_to_vec` when M1
   enabled (M1 enablement itself is a separate
   mitigation-selection-policy question, with the NO_ALIGNMENT-skip
   rule documented in §9.10.1); plus a `dim_h ≤ 16·2^32` exhaustion
   guard and a seed-independence / single-purpose-seed requirement.
   The reference implementation + 31 tests + 10 KAT vectors are
   pinned to these choices. **Phase 2 landed 2026-05-11** (`4b85eb1`):
   the v7 substrate spec was brought into the repo as
   ``docs/_source/merkle-agi-dag-v7.rst`` and § 9.10 + § 9.10.1 were
   folded in after § 9.9. **This open question is resolved — φ_PRG =
   HMAC-SHA-512(seed, C(M) ‖ counter_le32), 32-byte committed seed,
   little-endian.** #000035 → closed. (A full upstream v7 spec
   revision remains exogenous; the amendment now lives version-
   controlled in the in-repo copy where future amendments land.)

3. **T3 per-window budget formal bound.** ~~§6 lists "bounded by
   per-window budget" without giving the bound.~~ **Closed
   2026-05-10**: the explicit closed-form bound now lives at
   ``docs/soft-hash-channel-t3-bound.md`` §6 (under #000036),
   parametrized in operator-measurable inputs (g, ‖∇L_max‖,
   σ_grad, K, R, W, N_b, E). The combined bound has three
   contributions — gradient-bias C_B1 via Fano's inequality,
   LR-selection C_B2 via categorical-channel capacity, batch-order
   C_B3 via the Bottou-Bousquet random-shuffle refinement —
   summed over a window of length W. Calculator at
   ``bench/scripts/t3_bound_calculator.py``.

---

## 10. References

- v7 § 3 (T6 dual-hash separation theorem)
- v7 § 9 (plastic training; the source of the L2-anchor term)
- v7 § 9.9 (the "backdoor via soft hash" first-order dismissal we're
  superseding)
- SQD § 3 (hard-hash construction for Q(Θ))
- SQD § 5 (integer-kernel discipline; bigint accumulator)
- ticket #000018 (this document is the closure deliverable)

---

## 11. Status

**Open questions:** §9.1 (Hessian alignment under `φ_linear`:
synthetic-ablation infrastructure landed 2026-05-10 per #000034
Phase 1a — probe + <!--AUTOCOUNT:tests:tests/test_phi_alignment_probe.py-->23<!--/AUTOCOUNT--> tests + KAT fixture; parks on v7
deployment data per #000034 Phase 1b for the actual checkpoint
measurement),
§9.2 (PRG cryptographic strength for `φ_PRG`: decision pinned +
Phase 1 reference implementation landed 2026-05-10 per #000035 —
HMAC-SHA-512 with 32-byte committed `anchor_prg_seed`, KAT-pinned;
dav1d-reviewed §9.10 wording final 2026-05-11 (#000035 §3.4); awaits v7
§9.10 spec amendment when v7 plastic-training spec gains an
active deployment target). §9.3 closed 2026-05-10 via the T3
per-window bound at ``docs/soft-hash-channel-t3-bound.md``
(under #000036).

**Closed:** §4 reduction shows T1/T2 are bounded by SHA-256 partial-
preimage hardness under any `φ` independent of parameter geometry.
M2 (per-checkpoint nonce) closes residual channels at minimal cost.

**Recommendation:** v7 spec amendment §9.10 lands the M2 nonce.
Implementation ticket follows.

**Doc closure criterion** (per #000018 §7): reduction stated, mitigation
recommended, residual-bound table present (§6). Done.
