Skip to content

feat: model poisoning detection and BFT ensemble voting#1

Merged
Anichris-koded merged 1 commit into
mainfrom
feat/027-model-poisoning
Jun 20, 2026
Merged

feat: model poisoning detection and BFT ensemble voting#1
Anichris-koded merged 1 commit into
mainfrom
feat/027-model-poisoning

Conversation

@Anichris-koded

Copy link
Copy Markdown
Owner

Summary

Implements issue Ledger-Lenz#42 in full — hardens the LedgerLens inference stack against model poisoning, artifact substitution, and ensemble manipulation.

Changes

Artifact Integrity (Ed25519 trust chain)

  • ModelArtifact.verify_chain() — 4-step chain: SHA-256, Ed25519 signature, key fingerprint, training data SHA-256
  • sign_metrics() — signs metrics.json after every training run
  • RiskScorer._load_models() calls verify_chain immediately after every joblib.load

BFT Ensemble Voting

  • Replaces simple average with trimmed-mean / median scheme
  • Divergence flag + Prometheus counter (bft_divergence_detected_total) when |max-min| > BFT_SCORE_DIVERGENCE_THRESHOLD
  • consensus_failure response (score=100, confidence=0) when < BFT_MIN_CONSENSUS models agree

Training Data Provenance

  • sha256_dataframe() — deterministic SHA-256 of row-sorted input parquet
  • detect_label_poisoning() — aborts training and writes reports/poisoning_alert_{timestamp}.json when wash-trade ratio shifts > 15%

Annotation Queue Integrity

  • HMAC-SHA256 over wallet|label|annotator_id|annotated_at keyed by ANNOTATION_HMAC_SECRET
  • export_labelled() verifies every HMAC, logs WARNING and excludes tampered entries

CI + Docs

  • grep check in CI enforces verify_chain follows every joblib.load in detection/
  • docs/security.md — threat model, key generation, rotation procedure
  • README Security section updated

Testing

make test   # 74 passed, 0 failed
make lint   # 0 violations

Definition of Done

  • verify_chain raises ModelIntegrityError for all 4 failure types
  • BFT trimmed mean produces median for 3 inputs
  • detect_label_poisoning returns True when ratio shifts > 15%
  • HMAC verification rejects any tampered annotation field
  • Ed25519 private key never logged or included in metrics.json
  • MODEL_SIGNING_PRIVATE_KEY_PATH and ANNOTATION_HMAC_SECRET documented in .env.example
  • verify_chain called on every model load path + CI grep check
  • docs/security.md merged alongside code
  • BFT voting active in RiskScorer.score by default

Closes Ledger-Lenz#42

- Add Ed25519 artifact trust chain (ModelArtifact.verify_chain)
- Add sign_metrics() for post-training artifact signing
- Replace simple average with BFT trimmed-mean voting in RiskScorer
- Add detect_label_poisoning() with training abort and alert report
- Add annotation HMAC-SHA256 integrity (annotation_queue.py)
- Add CI grep check: verify_chain must follow every joblib.load
- Add docs/security.md with threat model and key rotation procedure
- Update README Security section
- Add tests: test_persistence, test_model_inference, test_model_training,
  test_annotation_queue (74 passing)

Closes Ledger-Lenz#42
@Anichris-koded Anichris-koded merged commit 32f2b0b into main Jun 20, 2026
2 checks passed
Anichris-koded pushed a commit that referenced this pull request Jun 20, 2026
- Added --dry-run argument to skip DB persist and on-chain submission
- Updated README.md with the new flag
- Added tests to cover the dry-run functionality
Anichris-koded pushed a commit that referenced this pull request Jun 20, 2026
feat: Add --dry-run flag to run_pipeline.py (#1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement Model Poisoning Detection and Byzantine-Fault-Tolerant Ensemble Voting to Harden the Inference Stack

1 participant