Skip to content

Releases: BitMind-AI/bitmind-subnet

Release v4.8.6

14 Jun 20:07
802cad4

Choose a tag to compare

Release 4.8.6

Prompt Diversity Pipeline

The validator's prompt generation pipeline has been rebuilt to break a
single-register monoculture. Previously the composer LLM defaulted to polished
cinematography for every prompt regardless of source — "a close up" opened 36% of
video prompts, "as if" appeared in 72%, and every prompt shared the same literary
voice. The new pipeline samples visual register, shot structure, length, and
style outside the LLM and injects them as authoritative constraints the model
must render.

Architecture

  • Register sampling — 12 visual registers (phone_casual, cctv_surveillance,
    drone_aerial, cinema_polished, etc.) with weighted probabilities. Each prompt
    gets a committed spec dictating register, camera motion, length band, event
    count, and whether literary phrasing is banned ("plain" registers).

  • Scene remixing — 25% of scenes are semi-synthetic variants where contextual
    fields (time of day, weather, setting, mood) are resampled while preserving the
    grounded subject. Breaks scraper distributional bias without artificial scene
    generation.

  • QC gate — every composed prompt is validated against its committed spec.
    Rejects LLM meta-chatter, bullet lists, length-band violations, and banned
    phrases in plain registers. One retry with the rejection reason appended; a
    second failure drops the sample.

  • Near-duplicate guard — 5-gram Jaccard similarity checked against the last
    200 same-modality prompts at write time. Paraphrase-level repeats are rejected.

  • Diversity reportscripts/prompt_diversity_report.py quantifies opener
    trigram share, phrase tells, near-duplicate rates, and register distribution.
    --check mode exits non-zero on threshold breaches — suitable for CI canaries.

Measured impact

Metric Old New Change
Video opener max share 35.9% ("a close up") 20.6% ("the phone camera") −15.3 pp
Image opener max share 32.4% ("a close up") 11.2% ("a close up") −21.2 pp
"as if" per video prompt 0.724 0.206 −0.52 pp
"stillness" per video prompt 0.407 0.150 −0.26 pp
Registers in use 1 (cinematic) 12 distinct
Cinema_polished share 100% 13%

All 12 registers are in active rotation. phone_casual leads at 21.5%;
cinema_polished — the old pipeline's de-facto only register — accounts for
only 13% of new prompts.

C2PA Model Audit

Two models removed from the validator's accepted set after live C2PA testing:

  • bytedance/seedance-1-5-pro — produces no C2PA/JUMBF data in output.
    Miners generating from this model were getting verified as "no upload" and
    penalized. Only Seedance 2.0 variants are validator-eligible.

  • Runway gen4.5 — C2PA manifest present but claimSignature.mismatch.
    Runway's signing infrastructure bug. Re-enable when Runway fixes their
    signing.

Seedance 2.0 pricing corrected: seedance-2-0-fast $0.05 → $0.12,
seedance-2-0 $0.12 → $0.15 (previous figures were inaccurate).

Seedance on Runway

seedance2 and seedance2_fast are now available through the Runway service
in addition to OpenRouter. Both produce C2PA-signed content (Byteplus Pte.
Ltd. signature) and are validator-eligible. Supports 4–15 second durations,
up to 1080p, and square (1:1) through 21:9 aspect ratios.

Modality Reward Weights

Generator reward weights adjusted:

Modality Before After
Video 0.94 0.88
Image 0.06 0.12

Doubles the image contribution to miner rewards while keeping video dominant.

Release 4.7.0

01 May 04:13
bfa1d01

Choose a tag to compare

Release Notes

Validator: prompt generation overhaul

The validator's prompt generator (gas/generation/prompts/prompt_generator.py) has been re-architected from a rule-based motion / camera planner with hardcoded option lists into a VLM-grounded, LLM-composed pipeline.

New architecture

source image
    │
    ▼
Qwen3-VL-4B-Instruct  ───►  SceneDescription (structured JSON: subjects, setting, lighting, dynamic_candidates, observed_motion_cues, ...)
                                    │
                                    ▼
                       Qwen3-30B-A3B-Instruct-2507 (bf16 MoE)
                                    │
                                    ▼
                  cinematographic image prompt + video prompt (single LLM pass each)
  • VLM (Qwen/Qwen3-VL-4B-Instruct) extracts a structured SceneDescription from the source image — explicit subjects, setting, lighting, and crucially dynamic_candidates / observed_motion_cues so subject motion stays grounded in what the source actually depicts.
  • LLM (Qwen/Qwen3-30B-A3B-Instruct-2507, bf16) composes the final image and video prompts in a single pass each. The hardcoded motion / camera / lens enumeration that previously gated prompt construction is gone.
  • Both models are dropped from VRAM after each batch (clear_gpu()); total resident VRAM during a batch is ~69 GB, sized for an 80 GB card.

Diversity & bias mitigation

Adversarial inspection of an earlier output batch surfaced three biases (every prompt opened with similar phrases, the same ~5 camera moves recurred, lens choices clustered around 35mm). All three were addressed:

  1. Removed anchoring examples from the system prompts. Concrete phrases like "slow dolly-in on a 35mm lens" were replaced with abstract guidance that asks the LLM to range across the full vocabulary.
  2. Per-modality prior-prompt feedback loop. The LLM is shown the most recent _PRIOR_WINDOW = 12 prompts from the same batch and instructed to make the next composition demonstrably different across shot framing, camera movement, focal length, lighting, palette, mood, opening sentence, and vocabulary. Backed by a collections.deque(maxlen=12) so the in-context history is bounded — earlier we saw CUDA OOMs at --prompt-batch-size 50 with unbounded history.
  3. Higher sampling temperature / top_p for both modalities (video 0.85 → 1.0, image 0.8 → 0.95, top_p 0.92 → 0.95).

Additionally:

  • Length cap restated in the user message (Strict length: maximum N words.) to combat the LLM's tendency to mirror prior-prompt length when context grew.
  • Cinematographer persona dropped. The system prompt previously framed the LLM as a "senior prompt engineer for state-of-the-art text-to-video models (Sora, Veo 3, Kling 2.x, Runway Gen-4, Pika 2.x, Luma Ray 2, ...)" — that framing alone was responsible for most of the cinematography monoculture. New framing is neutral ("You write text-to-video prompts." / "You write text-to-image prompts.") and explicitly asks the LLM to match the visual register implied by the source — snapshot, surveillance, documentary, home video, animation, press, editorial, polished narrative cinema, etc. — rather than always defaulting to polished cinematography.
  • Per-attribute requirements relaxed. Camera framing, movement, lens, depth of field, color palette, and pacing are now optional and only included when they fit the source register. A surveillance frame may have no notion of "lens"; a polished shot will.

Observability

  • _log_generated_prompts(...) emits structured INFO logs for every generated (image, video) pair (token counts, scene fingerprint, full prompts) so prompt quality can be triaged from pm2 logs without sampling the cache db.

CUDA fragmentation

  • PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True exported in docker/entrypoint.sh and validator.config.js so the 30B-A3B MoE LLM doesn't OOM on the second batch from fragmentation.

Vestigial cleanup

  • prompt_composer.py is gone; adapt_for_local_model moved to gas/generation/prompts/model_prompt_styles.py.
  • The generate_search_query codepath, motion_planner rule tables, and a chunk of data_service / content_manager dead code were removed.

Validator: per-modality upload thresholds

Single combined --upload-threshold replaced with two:

  • --upload-image-threshold (default 1000) — image generation is fast, hold for full shards.
  • --upload-video-threshold (default 10) — video generation is slow, ship as soon as a small batch is ready.

OR-triggered per modality. When only image hits its threshold, only image gets uploaded that cycle; the video queue keeps accumulating. Prevents one-video parquet shards. (gas/config.py, neurons/validator/services/data_service.py, gas/cache/content_manager.upload_batch_to_huggingface(modalities=...).)

Validator: misc fixes

  • Missing dep: imageio-ffmpeg==0.6.0 added to pyproject.toml. Diffusers' export_to_video was silently failing for AnimateDiff / CogVideoX / Wan because imageio was present but its bundled ffmpeg shim wasn't.
  • DeepFloyd/IF pipeline bug in gas/generation/util/model.pynegative_prompt and negative_prompt_embeds were both being forwarded; negative_prompt is now popped when use_prompt_embeds / save_prompt_embeds is active.

Generative miner: Runway text-to-video service

New service neurons/generator/services/runway_service.py wraps the official runwayml Python SDK so generative miners can serve Runway's text-to-video API.

  • Models: accepts gen4.5, veo3.1, veo3.1_fast, veo3 (case-insensitive). Default veo3.1. Unknown values raise with the accepted list — no silent fallback.
  • Per-model parameter validation: ratio (1280:720, 720:1280, 1080:1920, 1920:1080 for veo3.1/veo3; 1280:720/720:1280 for gen4.5), duration clamped to per-model rules (4/6/8 for Veo, 2–10 for gen4.5), audio (Veo only), seed (gen4.5 only).
  • Polling timeout default raised to 2400s (was 900s) — Runway often runs ≥15 minutes when the API is busy. Override per task via parameters.runway_poll_timeout or parameters.timeout. <=0 = wait indefinitely.
  • Credentials (resolve_runway_api_key()): prefers RUNWAYML_API_KEY (matches the repo's *_API_KEY convention), falls back to RUNWAYML_API_SECRET so copies from Runway docs still work.
  • Registered in service_registry.SERVICE_MAP. runwayml>=4.13.0 added to pyproject.toml.

Generative miner: task checkpointing & restart resume

Previously, a miner restart while a long-running text-to-video job was in flight (e.g. mid-Runway poll) would silently drop the task: the validator had received a 201 with the task id and was waiting on a webhook that would never come.

New durable task store neurons/generator/task_checkpoint_store.py + process_with_checkpoint(...) hook on the service base class:

  • One JSON record per task, written atomically under MINER_STATE_DIR (default <output_dir>/.gen_miner_state/).
  • GenerationTask gained a checkpoint: Optional[Dict[str, Any]] field that stores the external job id ({"kind": "runway", "runway_task_id": "..."}); large binary fields (input_data, result_data) are explicitly not persisted.
  • On startup _restore_checkpointed_tasks():
    • re-queues pending tasks
    • rehydrates processing tasks only if they have an external checkpoint (Runway task id), otherwise the task is dropped (we have nothing to resume from)
  • RunwayService._text_to_video resume path uses the SDK's NewTaskCreatedResponse + inject_sync_wait_method(...) to re-attach to the in-flight Runway task and call wait_for_task_output() again.
  • After completion / failure / webhook send, the checkpoint file is unlinked.

Net effect: pm2 restart mid-generation no longer loses long-running Runway / Veo jobs. The miner reattaches to the in-flight job, downloads the output when ready, and posts the success webhook to the validator.

Release 4.6.0

02 Apr 22:11
4f7519e

Choose a tag to compare

feat: Discriminator Verticals & Miner Performance CLI

Summary

Introduces the vertical axis to the discriminator competition (starting with image:human), adds a self-service performance endpoint for miners, adjusts incentive allocations, and cleans up deprecated code.

Changes

Vertical support in model uploads

  • gascli d push now accepts --vertical human (only valid with --modality image).
  • Client-side guards in both gas/cli.py and push_model.py prevent invalid vertical+modality combinations before hitting the API.
  • gas/protocol/model_uploads.py renamed to gas/protocol/miner_requests.py to reflect its broader scope.

Miner performance CLI (gascli d perf)

  • New gascli d perf command lets miners query their own benchmark results via an Epistula-authenticated GAS API endpoint.
  • Output shows SN34 score (with progress bar), MCC, Brier, modality, vertical, and elapsed time for running/queued jobs.
  • Supports --modality and --vertical filters.
  • Core HTTP logic lives in gas/protocol/miner_requests.py; CLI handles wallet loading, auth, and formatting.

Incentive allocation adjustment

  • Image allocation increased from 23% → 28.5% to accommodate the new image:human vertical (~17.5% of image rewards).
  • Video allocation adjusted from 29% → 23.5%.
  • Burn, audio, and generator percentages unchanged.

Fix: Non-resumable upload flow and silent 409 handling

The upload flow (push_model.py / upload_single_modality) had no way to distinguish a genuine failure from "your model was already accepted by the server." Miners who re-ran the script after a partial failure (e.g. chain registration timed out) would hit a 409 from /upload/presigned and receive an opaque error, unable to proceed.

What was wrong: The server returns 409 when the file hash is already in an accepted state (uploaded, downloading, validating, examining, or confirmed). Both 409 and genuine errors were handled identically — print("FAILED") and exit — even when the model was already on the server and the miner just needed to retry blockchain registration.

Fix: upload_single_modality in gas/protocol/miner_requests.py now returns an already_uploaded: True flag on 409. push_separate_models in push_model.py checks this per modality and prints a clear, actionable message:

⚠️  Video model already uploaded — skipping (model is already accepted by the server)
❌  Cannot retrieve r2_key for already-uploaded video model. Re-run with the
    original r2_key or wait for the current upload to be processed.

Note: The miner still can't fully auto-resume from a 409 because the server doesn't return the original r2_key on conflict (by design — information leak). A full solution would require a "status lookup by hash" endpoint that returns the r2_key for models owned by the requesting hotkey.

Release 4.5.6

21 Mar 20:01
298a9e5

Choose a tag to compare

C2PA trust anchor validation for c2pa-python >= 0.29.0

Background

c2pa-python==0.29.0 (released March 17 2026) changed c2pa-rs so that trust anchor checking is always enforced by default. Any certificate not chaining to a root CA in c2pa-rs's built-in store fires signingCredential.untrusted and hard-fails validation.

We had pinned c2pa-python>=0.25.0,<0.29.0 as a short-term workaround. This restores previous behavior but leaves a cert-forgery gap: our code only string-matched on the issuer field, which an attacker could fake with a self-signed cert set to "Stability AI Ltd".

What this PR does

1. Extracts and saves trust anchor PEM files

Scanned miner-submitted media in /workspace/.cache/sn34 (88k entries, source_type = 'miner') to find one sample file per AI provider. Extracted the full DER certificate chains embedded in each file's C2PA JUMBF box and saved them to gas/verification/trust_anchors/.

All major providers turned out to use private or non-CAI root CAs that are not in c2pa-rs's built-in store:

Provider Cert chain root File
Stability AI GlobalSign Root CA - R6 (via GlobalSign GCC R6 SMIME CA 2023) stability_ai.pem
Runway (pre-Gen4) GlobalSign Root CA - R6 (same intermediate) runway.pem
Runway Gen4 CN=Stability AI, O=Stability AI, C=US (private self-signed) runway_gen4.pem
Black Forest Labs GlobalSign Root CA - R6 (same intermediate) black_forest_labs.pem
Microsoft CN=Microsoft Supply Chain RSA Root CA 2022 (private self-signed) microsoft.pem
OpenAI (via Truepic) CN=Truepic WebClaimSigningCA chain openai_truepic.pem
Adobe CN=Adobe Product Services G4 chain adobe.pem
Google CN=Google C2PA Root CA G3 (fetched from pki.goog/c2pa/root-g3.crt) google_c2pa.pem

GlobalSign Root CA - R6 was sourced from the certifi bundle. Google's C2PA Root CA G3 was fetched directly from Google's PKI at http://pki.goog/c2pa/root-g3.crt.

2. Updates c2pa_verification.py

Three additions at the top of the file, zero changes to existing verification logic:

  • _load_trust_anchors() — concatenates all PEM files at import time into a single string
  • _TRUST_ANCHORS_PEM / _C2PA_HAS_CONTEXT_API — cached module-level constants (no per-call file I/O)
  • _open_c2pa_reader(file_path) — transparent wrapper around c2pa.Reader:
    • On >= 0.29.0: builds a Settings object with user_anchors (additive — preserves the built-in store) and wraps the reader in a Context
    • On < 0.29.0: returns a bare c2pa.Reader, identical to previous behaviour

The one-line change in verify_c2pa():

# before
with c2pa.Reader(file_path) as reader:

# after
with _open_c2pa_reader(file_path) as reader:

3. Bumps the version pin

# pyproject.toml
- "c2pa-python>=0.25.0",
+ "c2pa-python>=0.29.0",

Verification

Ran gascli g verify-c2pa against one sample file per provider on c2pa-python 0.29.0:

Adobe         (Adobe Inc.)
Black Forest Labs  (Black Forest Labs Inc.)
Runway Gen4   (Runway — private Stability AI root)
Google        (Google LLC — Google C2PA Root CA G3)
Google        (Google C2PA Core Generator Library)
Microsoft     (Microsoft Corporation — video)
Microsoft     (Microsoft Corporation — image)
OpenAI        (Sora / Truepic chain)
OpenAI        (OpenAI-API)
Runway        (RUNWAY AI, INC. — pre-Gen4)

One Stability AI sample showed assertion.dataHash.mismatch — the cached file was modified after signing, which is correct behaviour (hash mismatch ≠ trust failure).

Security improvement

With c2pa-python>=0.25.0,<0.29.0, c2pa-rs skipped the trust chain check entirely. Our code fell back to string-matching on issuer, meaning an attacker could submit media signed with their own self-signed cert and set issuer = "Stability AI Ltd" to pass validation.

With this PR, c2pa-rs performs full cryptographic chain verification against the extracted root certs. Forging a cert that chains to CN=Stability AI, O=Stability AI (Runway Gen4's root) or GlobalSign Root CA - R6 requires breaking RSA-2048/PSS — the issuer string alone is no longer sufficient.

Release 4.5.2

20 Feb 21:12
f3f504c

Choose a tag to compare

Validators can now run as Docker containers alongside the existing pm2 setup. pm2 remains the default — Docker is an explicit opt-in via --docker on all gascli validator commands.

Running with pm2 (default)

gascli v start
gascli v stop
gascli v logs

Running with Docker

gascli v start  --docker
gascli v stop   --docker
gascli v delete --docker
gascli v status --docker
gascli v logs   --docker

Or directly via Compose:

docker compose --env-file .env.validator up -d

Architecture

Three containers share a single bitmind-subnet-validator image, each running one service (controlled by the SERVICE env var):

Container Role
validator Core validation loop, FastAPI callback server
generator Image/video generation (GPU-enabled)
data Dataset downloads and cache management

Configuration

.env.validator is the single source of truth for both container environment and Compose variable substitution (bind-mount paths, ports, etc.). Copy the template and fill in your details:

cp .env.validator.template .env.validator

Key fields:

Variable Description
WALLET_PATH Host path to your Bittensor wallets directory
WALLET_NAME / WALLET_HOTKEY Only that wallet's subdirectory is bind-mounted
BT_LOGGING_LOGGING_DIR Base path for validator state and Bittensor logs
SN34_CACHE_DIR Model/data cache — shared between pm2 and Docker, no re-downloading when switching
HF_HOME Hugging Face cache — also shared with pm2
CALLBACK_PORT Port miners call back to; exposed via network_mode: host
NETUID Auto-derived from CHAIN_ENDPOINT; set explicitly to override

Persistent state

Scores, challenge tasks, and Bittensor logs are bind-mounted from the host under BT_LOGGING_LOGGING_DIR/WALLET_NAME/WALLET_HOTKEY/ and survive container restarts.

Automatic updates

A cron-friendly docker/autoupdate.sh script pulls the latest image and restarts containers:

*/5 * * * * /path/to/bitmind-subnet/docker/autoupdate.sh >> /var/log/bitmind-docker-update.log 2>&1

Viewing logs

# via gascli
gascli v logs --docker

# directly (per service)
docker compose logs -f validator   # or generator, data

Release 4.4.7

12 Feb 06:33

Choose a tag to compare

Release notes: 4.4.1 – 4.4.7

4.4.1 (30 Jan 2026)

  • Validator weight normalization: Only assign non-zero weights to UIDs in the active set; explicitly zero out weights for inactive generative miners before scaling.

4.4.2 (31 Jan 2026)

  • C2PA verification: Broader C2PA issuer handling so more certificate issuers are accepted as trusted.
  • Allow all certificate issuers to be treated as CA issuers where appropriate.
  • Remove redundant issuer logic and consolidate issuer handling.
  • Increase C2PA options and clean up multiple issuer versions.

4.4.3 (3 Feb 2026)

  • New upload endpoint for discriminative miner model uploads (neurons/discriminator/push_model.py).
  • Validator: Decrease burn (burn rate reduced).

4.4.4 (4 Feb 2026)

  • Generator prompt sampling: Keep remove=False so prompts are not removed when sampled; use threading.Lock for thread-safe prompt sampling.
  • Content cache and uploads: New content DB and content manager support plus a new upload path used by the data service.
  • Config: New options for content/upload behavior (see .env.validator.template).
  • Docs: Updates to Discriminative-Mining, ONNX, and Validating.

4.4.5 (6 Feb 2026)

  • Generator rewards: Use a 2-hour lookback window for recent verified miner media when computing generator base rewards (eligibility and score stability).
  • Content cache: Content DB and content manager extended to support the new lookback and verification stats.

4.4.6 (6 Feb 2026)

  • Stability AI service: Fix request headers for the Stability AI miner service so API calls succeed.
  • Webhooks / logging: Remove unused has_c2pa key from logging; keep useful logging; reduce log noise.
  • Metagraph / validator: Minor adjustments for consistency and clarity.

4.4.7 (8 Feb 2026)

  • Verification stats: Add support for failed miner media in the lookback window.
    • New get_recent_failed_miner_media() in content DB and content manager (default 2-hour lookback).
    • New get_verification_stats_last_n_hours() to compute pass/fail counts and pass rate per miner over the last N hours (for generator base rewards and eligibility).
  • Content manager: New helper _build_verification_stats_from_verified_and_failed() to build per-miner verification stats from both verified and failed media.
  • Validator: Uses the new verification stats and failed-media lookback for more accurate generator rewards and eligibility.

Release 4.4.0

31 Jan 06:30
6e593ac

Choose a tag to compare

Summary

  • Fix generator reward logic for sharper penalties when generative miners stop responding to queries
  • Improve media format detection with proper ftyp-based MP4 detection and support for additional formats
  • Fix file format handling - detect actual format from magic bytes instead of hardcoding extensions/content-types
  • Add dynamic escrow address fetching from the API with fallback to defaults
  • Add prompt modality tracking to ensure prompts are matched to their intended modality
  • Add prompt cache cleanup to delete prompts after use, preventing unbounded cache growth
  • Add detailed error responses for generative callback failures so miners can debug issues
  • Add webhook stats tracking for miners to monitor success/failure rates per validator
  • Add C2PA verification CLI for testing C2PA credentials on local files
  • Remove LocalService from registry (since it doesn't produce C2PA-signed content)

Changes

Generator Miner Rewards Fix

  • Fixed bug where include_all=True was double-counting already-rewarded media in verification stats
  • Changed reward eligibility from union to intersection: miners must now have both local verified submissions and benchmark results to receive rewards
  • Changed base reward default from 1e-4 to 0 for generators without verified submissions
  • Increased EMA alpha from 0.2 to 0.5 for more aggressive score decay on inactive miners
  • Added hard cutoff to zero out scores for generators not active within the liveness window

Media Format Detection (#311)

  • Fixed MP4 detection by checking for ftyp at offset 4 instead of hardcoded box sizes
  • Consolidated all ftyp-based format detection to fix unreachable M4A code path
  • Added support for new image formats: HEIC/HEIF, AVIF, GIF, BMP, TIFF
  • Added support for new video formats: MOV, 3GP, AVI
  • Added support for audio formats: MP3, WAV, FLAC, OGG, M4A
  • Added length checks to prevent index errors on short data
  • Improved JPEG detection with 3-byte signature

Dynamic Escrow Addresses (#322)

  • Added new get_escrow_addresses() function to fetch escrow addresses from the gas-api to provide the option to punish weight copiers.
  • Validators now dynamically fetch video_escrow, image_escrow, and audio_escrow addresses
  • Escrow address eligibility based validators provably running latest SN code.

Prompt Modality Support

  • Added modality column to prompts table (image, video, audio)
  • Database migration automatically adds column to existing installations
  • Fixed database migration order to prevent "no such column: modality" errors on existing databases
  • Prompts are now stored with their intended modality when generated
  • Challenge manager selects modality first, then samples matching prompts

Prompt Cache Cleanup

  • Fixed remove parameter in prompt sampling to actually delete prompts instead of just incrementing used_count
  • Added min_prompts_threshold (default 100) to prevent running out of prompts
  • Enabled prompt cleanup in generative challenge manager - prompts are now deleted after use (if enough remain)

Generative Callback Error Responses

  • Miners now receive detailed error messages when their submissions are rejected:
    • 400 - Empty binary payload for empty uploads
    • 400 - Corrupted or unreadable media for invalid media files
    • 400 - Duplicate content detected for perceptual hash matches
    • 400 - C2PA verification failed: no C2PA manifest for missing credentials
    • 400 - C2PA verification failed: untrusted issuer for non-trusted sources
  • Previously, all validation failures returned 200 OK silently, making it impossible for miners to debug issues
  • Made c2pa a hard dependency - removed conditional C2PA_AVAILABLE checks
  • Cleaned up inline imports, moved all imports to module scope

Miner Webhook Stats Tracking

  • Added WebhookStatsTracker to track webhook success/failure rates per validator IP
  • Stats are persisted to ~/.bitmind/webhook_stats.json and survive restarts
  • Automatic behaviors:
    • Saves to disk every 60 seconds (debounced to avoid excessive I/O)
    • Prints summary to logs every 5 minutes
    • Daily rotation at midnight - archives to webhook_stats_archive/webhook_stats_YYYY-MM-DD.json
    • Auto-cleanup of archives older than 7 days
  • Failure types are categorized:
    • empty_payload - validator received empty data
    • connection_timeout - connection timed out
    • connection_error - network/connection issues
    • http_400, http_401, http_404, http_500 - HTTP status codes
  • Helper functions available:
    • print_webhook_stats() - print summary to logs
    • get_webhook_stats_json() - get stats as dict
    • reset_webhook_stats() - clear all stats

LocalService Removed from Service Registry

  • LocalService (local Stable Diffusion/AnimateDiff) removed from SERVICE_MAP
  • Reason: LocalService doesn't produce C2PA-signed content, which validators now require
  • The service class remains in the codebase for potential future use
  • Valid services are now: openai, openrouter, stabilityai, or none
  • If no valid service is configured for a modality, requests for that modality will be rejected
  • Miners still free to generate whatever

C2PA Verification CLI

  • Added gascli generator verify-c2pa (standalone helper script at neurons/generator/helper/verify_c2pa.py) command for gen miners to test local files for C2PA credentials
  • Useful for miners to verify their generated content has valid C2PA before sending to validators
  • Supports --verbose for detailed output and --json for machine-readable output

Release 4.3.0

04 Dec 05:29
a017663

Choose a tag to compare

This release introduces c2pa verification for miner submissions (+ the relevant support on the generative miner side), an upgraded prompt generation pipeline, and fixes for weight normalization in the validator.

Generative Miner Data Verification

New modules in gas/verification/:

  • c2pa_verification.py: Validates C2PA content credentials from miner-submitted media. Checks that content originates from trusted AI generators (OpenAI, Google, Adobe, Microsoft, Stability AI, Midjourney, Anthropic). Content without valid C2PA from trusted issuers is rejected.

  • duplicate_detection.py: Implements perceptual hashing (pHash) for images and frame-based hashing for videos. Includes crop-resistant hash segments. Configurable Hamming distance threshold (default: 8) for near-duplicate matching.

Miner Service Configuration

  • Added IMAGE_SERVICE and VIDEO_SERVICE environment variables to specify which generation service to use per modality
  • Valid options: openai, openrouter, local, none
  • Setting none disables that modality entirely
  • Prevents loading unnecessary local models when using API services
  • Conditional model loading based on configured services

Prompt Generation Pipeline

  • Replaced BLIP-2 with Qwen2.5-VL-3B-Instruct for image annotation
  • Optional flash attention support for faster inference
  • New modules for model-specific prompt styles (model_prompt_styles.py) and prompt modifiers (prompt_modifiers.py)
  • Increased max token output from 20 to 256 for richer descriptions

Challenge Manager Improvements

  • Pre-storage validation rejects: duplicate content, content without valid C2PA, corrupted/unreadable media
  • Duplicate checks now scoped to same prompt ID to reduce overhead
  • Only validated content is stored and eligible for HuggingFace upload

Service Improvements

  • C2PA metadata preservation in OpenRouter and OpenAI services
  • Option to save generated media locally (MINER_SAVE_LOCALLY)
  • Default model changed to nanobanana pro for OpenRouter

Validator Weight Normalization Fix

  • Fixed burn rate calculation by excluding special UIDs (burn, image escrow, video escrow) from weight normalization (previously burning more than .7)
  • Added null check for empty generator rewards
  • Added logging for actual vs target burn rate verification

Dependencies

  • Added: c2pa-python>=0.25.0, imagehash>=4.3.1, qwen-vl-utils>=0.0.14
  • Updated: async-substrate-interface>=1.5.1
  • Optional: flash-attn>=2.7.0 (commented, for manual installation)

Breaking Changes

  • For now, miners must now produce content with valid C2PA credentials from trusted AI generators.
  • Duplicate submissions within the same prompt are rejected
  • Until we harden verification of open source model outputs, miners using local generation without C2PA embedding will have submissions rejected
  • Current services supported in c2pa checks:
OpenAI
Google
Adobe
Microsoft
Meta 
Shutterstock
Canva
Runway
Stability AI
Pika Labs

Reach out to the BitMind team if there are more services you'd like to have supported.

Release 4.2.0

31 Oct 01:28
172bb6d

Choose a tag to compare

  • Added Chrom1-HD to validator generation pipeline
  • Replaced local eval code with a gasbench wrapper
  • Single modality uploads for gascli d push (docs)
  • Better state/task tracking for generative tasks in validator

Release 4.1.7

31 Oct 01:26
626e0e8

Choose a tag to compare

Bugfix: Need to use the union of generator uids present in verification stats and fool rate stats to set weights for new generative miners