Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
7cdbe02
auth: add HMAC + bearer-token primitives alongside Epistula
claude May 19, 2026
9d34a79
miner_template: prompts store + GEPA/random_mutate optimizer plugins
claude May 19, 2026
db0fe29
miner: CLI subcommands for results / optimize / prompts (GEPA hook)
claude May 19, 2026
a467c59
feedback API: /miners/me/* + prompt_id round-trip closes the GEPA loop
claude May 19, 2026
941789c
non-competitive mode: HMAC cutover, operator CLI, validator chain bypass
claude May 19, 2026
6d15214
strip bittensor: optional import + NONCOMPETITIVE early-returns
claude May 19, 2026
58ef67e
strip bittensor: lazy-import shared/substrate.py so validator loads c…
claude May 19, 2026
42795ca
migrations: 005_miner_registry_and_prompt_id
claude May 19, 2026
61530dd
migrations: guard 005 ALTER experiments behind information_schema check
claude May 19, 2026
10ce5d0
Merge pull request #157 from TensorLink-AI/claude/non-competitive-rad…
tensorlink-dev May 19, 2026
0c06162
ecosystem.config.js: pm2 config for non-competitive miner + GEPA loop
claude May 20, 2026
119ad97
Merge pull request #159 from TensorLink-AI/claude/check-radar-miner-c…
tensorlink-dev May 20, 2026
615cfdc
operator_cli: import config so .env is honored
claude May 20, 2026
c8b871e
Merge pull request #160 from TensorLink-AI/claude/check-radar-miner-c…
tensorlink-dev May 20, 2026
60c8146
validator: drop metagraph lookup in _my_uid, return -1
claude May 20, 2026
7d5dae4
validator: strip metagraph / subtensor / wallet end-to-end
claude May 20, 2026
e07a19e
Merge pull request #161 from TensorLink-AI/claude/fix-artifact-dual-w…
tensorlink-dev May 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ __pycache__/
experiments/
*.backup
data/
logs/
prompts/
27 changes: 27 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,33 @@ add `HIPPIUS_*` once your Hippius keys are issued — the client will pick up
the new backend automatically. To cut over fully, also set
`HIPPIUS_BUCKET` (or unset `R2_BUCKET`) so dispatched URLs point at Hippius.

## Non-competitive Mode

Set `RADAR_NONCOMPETITIVE=true` for a non-subnet deployment:

- Validator skips wallet, subtensor, metagraph, weight-setting, on-chain
commitments. Existing competitive deployments leave it false.
- Auth uses HMAC service key (`RADAR_SERVICE_KEY`) for validator ↔ trainer
↔ DB. Headers: `X-Radar-{Signature,Timestamp,Key-Id}`.
- Miner identity is bearer tokens issued via the operator CLI:
```
python -m database.operator_cli register --name alice [--hotkey ss58]
python -m database.operator_cli issue-key --miner-id <id> --label prod
python -m database.operator_cli rotate-service-key
python -m database.operator_cli list-miners | list-keys --miner-id <id>
python -m database.operator_cli revoke-key --key-id <id>
```
- Miners pull scored history + evolve prompts locally via the miner CLI:
```
RADAR_DB_URL=http://db:8090 RADAR_MINER_API_KEY=rdrk_... \
python miner/neuron.py optimize --optimizer gepa --seed --watch
python miner/neuron.py results --json
python miner/neuron.py prompts list | history | rollback <gen>
```
- `prompts/active.json` (atomic-write) drives which prompt variant the
agent picks each round; `prompt_id` round-trips back via
`experiments.prompt_id` so Phase C scores attribute to the variant.

## Neuron Mode Environment Variables

| Var | Default | Purpose |
Expand Down
17 changes: 17 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,23 @@ class Config:
# all — Everything on one process (legacy / dev default).
NEURON_MODE: str = os.getenv("RADAR_NEURON_MODE", "all").lower()

# ── Non-competitive mode ────────────────────────────────────
# When true, validators + miners SKIP all chain interactions:
# * no metagraph sync, no Subtensor connection, no wallet
# * no weight setting, no on-chain commitments
# * outbound auth uses HMAC service key (RADAR_SERVICE_KEY)
# * miner identity comes from bearer API keys, not hotkeys
# Existing competitive deployments leave this false and run unchanged.
NONCOMPETITIVE: bool = os.getenv(
"RADAR_NONCOMPETITIVE", "false",
).lower() in ("1", "true", "yes")

# Shared HMAC secret for service-to-service auth in non-competitive
# mode (validator -> trainer, validator -> DB). Required when
# NONCOMPETITIVE is true.
SERVICE_KEY: str = os.getenv("RADAR_SERVICE_KEY", "")
SERVICE_KEY_ID: str = os.getenv("RADAR_SERVICE_KEY_ID", "operator")

# CORS origins for the public JSON API. Comma-separated, empty = no CORS
# (safe default for validator mode, which doesn't speak to browsers).
# In production dashboard mode set this to "https://radarnet.io".
Expand Down
77 changes: 77 additions & 0 deletions database/migrations/005_miner_registry_and_prompt_id.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
-- Miner registry + bearer API keys for the non-competitive cutover,
-- plus the ``experiments.prompt_id`` column that closes the GEPA
-- feedback loop on the miner CLI.
--
-- Non-competitive deployments use operator-issued bearer tokens
-- (rdrk_*) in place of Epistula/metagraph identity. A ``miner_id``
-- is an opaque operator-issued identifier (UUID by default) that
-- decouples the feedback API from any on-chain hotkey. During the
-- dual-stack period a miner row carries both: ``hotkey`` is the
-- bittensor SS58 (nullable, only set for chain-registered miners)
-- and ``miner_id`` is the bearer-auth identifier scoped to
-- /miners/me/*.
--
-- ``prompt_id`` on experiments lets miners correlate Phase C scores
-- with the prompt variant that produced the architecture. Opaque
-- to the operator; only the issuing miner interprets it. Two
-- partial indexes keep the table footprint sparse for rows without
-- a prompt_id (every existing row + every competitive-mode write).
--
-- All DDL is idempotent — safe under partial re-runs. The
-- experiments ALTER + its indexes are wrapped in an information_schema
-- guard (same pattern as 002_substrate_cids.sql) because CI exercises
-- the migration runner against an empty schema before init_schema()
-- has bootstrapped the experiments table. On a fresh deploy the base
-- CREATE TABLE in shared/pg_schema.py already declares prompt_id, so
-- the guarded block is a no-op there.

CREATE TABLE IF NOT EXISTS CURRENT_SCHEMA.miners (
miner_id TEXT PRIMARY KEY,
name TEXT NOT NULL DEFAULT '',
hotkey TEXT NOT NULL DEFAULT '',
contact TEXT NOT NULL DEFAULT '',
created_at DOUBLE PRECISION NOT NULL DEFAULT extract(epoch from now()),
revoked_at DOUBLE PRECISION
);

CREATE INDEX IF NOT EXISTS idx_miners_hotkey
ON CURRENT_SCHEMA.miners(hotkey)
WHERE hotkey <> '';

CREATE TABLE IF NOT EXISTS CURRENT_SCHEMA.miner_api_keys (
key_id TEXT PRIMARY KEY,
key_hash TEXT NOT NULL,
miner_id TEXT NOT NULL REFERENCES CURRENT_SCHEMA.miners(miner_id)
ON DELETE CASCADE,
scope TEXT NOT NULL DEFAULT 'miner',
label TEXT NOT NULL DEFAULT '',
created_at DOUBLE PRECISION NOT NULL DEFAULT extract(epoch from now()),
last_used_at DOUBLE PRECISION,
revoked_at DOUBLE PRECISION
);

CREATE INDEX IF NOT EXISTS idx_miner_api_keys_hash
ON CURRENT_SCHEMA.miner_api_keys(key_hash)
WHERE revoked_at IS NULL;

CREATE INDEX IF NOT EXISTS idx_miner_api_keys_miner
ON CURRENT_SCHEMA.miner_api_keys(miner_id);

DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_schema = 'CURRENT_SCHEMA'
AND table_name = 'experiments'
) THEN
ALTER TABLE CURRENT_SCHEMA.experiments
ADD COLUMN IF NOT EXISTS prompt_id TEXT;
CREATE INDEX IF NOT EXISTS idx_experiments_prompt_id
ON CURRENT_SCHEMA.experiments(prompt_id)
WHERE prompt_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_experiments_miner_prompt
ON CURRENT_SCHEMA.experiments(miner_hotkey, prompt_id)
WHERE prompt_id IS NOT NULL;
END IF;
END $$;
Loading
Loading