TAO—Block—Subnets85 / 128active
No description set on the registered repo.
what the project says about itself — the input for semantic labels
No description set on the repo.
scope of the data
Taonets fetches the repo's public metadata (description, topics, language, stars, last push) and the head of its README. Commit-count histories, contributor lists and release notes require the GitHub commits/releases endpoints, which the worker doesn't consume yet — those panels are coming with the ingest upgrade and are not simulated in the meantime.
first lines of the default branch README, unedited
# Bitcast V2 — Decentralized Creator Economy (SN93)
Reengineered Bitcast subnet: a clean, lean, async-first Python codebase on Bittensor 10.x.
## Overview
Bitcast connects brands with audiences through content creators. Creators (miners) publish
YouTube videos satisfying brand briefs; validators verify engagement via the YouTube
Analytics API, score videos in USD terms, and convert those scores into on-chain weights.
- **Miners** serve YouTube OAuth access tokens for the channels they operate.
- **Validators** fetch active briefs, evaluate every miner's channels/videos against them
(LLM-assisted brief matching), and set weights so emissions track earned USD value.
- Unallocated emission is absorbed by the burn UID (0).
## Two mechanisms, one subnet
SN93 runs two mechanisms on finney. This repository implements **mechanism 0** — the
YouTube creator-economy mechanism (~2% of subnet emission). The **mechanism 1**
X/Twitter mechanism (~98% of emission) is implemented in
[bitcast-network/bitcast-x](https://github.com/bitcast-network/bitcast-x).
## Verifying subnet liveness
SN93 uses commit-reveal validators, so raw on-chain `last_update` does not reflect miner
activity. See [AGENTS.md](./AGENTS.md) for read-only commands to verify validator, miner,
and emission activity directly against chain state.
## Layout
```
bitcast/
├── protocol.py # AccessTokenSynapse — the single wire message
├── config.py # ALL configuration: consensus constants, env settings, CLI
├── neuron.py # Shared wallet/subtensor/metagraph lifecycle
├── miner/ # Token management (OAuth refresh) + axon server
├── validator/
│ ├── base.py # Score state, EMA updates, weight-setting cadence
│ ├── forward.py # Main loop: briefs → query → evaluate → score → weights
│ ├── weights.py # Weight processing and chain submission
│ ├── reward/ # Orchestrator, models, scaling math, pricing
│ ├── youtube/ # YouTube API client, vetting, scoring, brief matching
│ └── llm/ # OpenRouter/Chute client + verbatim prompts
└── utils/ # Briefs client, signed dashboard publisher, TTL cache
```
## Setup
```bash
python3.12 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env # fill in API keys
```
## Run
```bash
# Miner
python -m neurons.miner --netuid 93 --subtensor.network finney \
--wallet.name miner --wallet.hotkey default
# Validator
python -m neurons.validator --netuid 93 --subtensor.network finney \
--wallet.name validator --wallet.hotkey default
```
Miner credentials: put one JSON file per YouTube account in `~/.bitcast/secrets/`, each
containing `client_id`, `client_secret` and `refresh_token` (or set `TOKEN_SOURCE=api`).
API-backed miners coalesce concurrent refreshes and cache the response for at most
60 seconds (`API_TOKEN_CACHE_SECONDS`, 0–60; default 60). This is a bounded reuse
window, not an inferred OAuth expiry: the current API returns freshly exchanged
roughly one-hour tokens but does not expose expiry metadata. New connections,
removals and recovery from refresh failure become visible within that window.
Expired cache entries are never served on refresh failure. The wire protocol and
validator scoring formulas are unchanged. Local credential expiry caching is unchanged.
Validators only query axons advertising public unicast IP addresses, including
on local test networks; private, loopback, link-local and multicast axons are
rejected before any request. Run integration fixtures with a mocked dendrite.
The default publication endpoint is `https://ingestion.bitcast.network`; operators
using a separate ingestion deployment must set `DATA_CLIENT_URL`.
Deployment note: the hosted miner's September 2026 V1 image is not built by this
repository's validator-only deploy workflow. Promoting this miner requires an
explicit miner image release together with the command `python -m neurons.miner`
and removal of the V1-only `--neuron.disable_auto_update` flag. Do not remove that
flag while retaining V1: it enables V1 automatic updates. No validator protocol
cutover is needed for the token cache.
## Development
```bash
pytest # offline test suite (chain/YouTube/LLM all mocked)
ruff check . # lint
ruff format . # format
```
## Observability: Loki score audit telemetry
Loki is enabled by default for the shared Bitcast dataset. Operators can set
`LOKI_URL`, `LOKI_USERNAME`, and `LOKI_TOKEN` together to use another Grafana
Cloud stack. Do not add credentials to labels or log lines. Logging is batched
and best-effort, so a telemetry failure cannot affect validation, EMA state,
weight calculation, or chain submission.
Each reward cycle emits one JSON `miner_score` line per miner. Its safe schema
is limited to `validator_uid`, `cycle_id`, `cycle_step`, `miner_uid`,
`raw_reward` (pre-EMA), `ema_before`, and `ema_after`. When a weight submission
succeeds in the following sync, that same line also contains
`submitted_weight` (the processed float submitted to the SDK) and
`onchain_weight_uint16` (the converted chain value). These are intentionally
different values: raw reward feeds EMA; EMA scores are normalized/processed into
the submitted weight; the uint16 is the final on-chain encoding.
Validator identity stays in bounded Loki labels (`uid`, `hotkey`, `netuid`,
`neuron`, `version`); miner UID is a JSON field, never a label. For example, to
compare miner UID 68 on owner UID 0 and canary UID 60:
```logql
{neuron="validator", netuid="93", uid=~"0|60"} | json | event="miner_score" | miner_uid="68"
```
Plot raw reward and EMA across both validators:
```logql
avg_over_time({neuron="validator", netuid="93", uid=~"0|60"} | json | event="miner_score" | miner_uid="68" | unwrap ema_after [6h]) by (uid)
```
Inspect only actual successful weight submissions (records without these fields
were score cycles where no weights were submitted):
```lo