- Shell 57.7%
- Rust 38.3%
- Makefile 4%
| .forgejo/workflows | ||
| docs | ||
| scripts | ||
| sdk/rust | ||
| src | ||
| .gitignore | ||
| buildenv.sh | ||
| Cargo.lock | ||
| Cargo.toml | ||
| Makefile | ||
| README.md | ||
Hero Launcher
JSON-RPC server that provisions and manages hero backends in Our Cyberspace. It receives requests from the www_herozero frontend, launches containerized hero_zero instances, and handles admin operations like DNS name registration.
System Flow
┌──────────────┐ JSON-RPC ┌────────────────┐ podman/docker ┌──────────────┐
│ www_herozero │ ──────────────► │ hero_launcher │ ─────────────────► │ hero_zero │
│ (Dioxus UI) │ hero.create │ (Axum server) │ container run │ (container) │
└──────────────┘ └────────┬───────┘ └──────┬───────┘
│ │
┌────────▼───────┐ ┌────────▼───────┐
│ Redis │ │ hero_ledger │
│ (hero state) │ │ (NEAR chain) │
└────────────────┘ └────────────────┘
- User fills out the join form on
www_herozero - Frontend calls
hero.createon hero_launcher - Launcher generates a mycelium keypair (x25519 + BLAKE3 address derivation)
- Launcher starts a hero_zero container with env vars (
HERO_ID,HERO_PASSWORD, etc.) - hero_zero auto-initializes: creates NEAR account, encrypts key, registers on ledger
- Launcher registers DNS name on the ledger (admin-only, triggered after payment)
Quick Start
# Build
cargo build --release
# Run (requires podman or docker, optionally Redis)
./target/release/hero_launcher
# With options
./target/release/hero_launcher \
--listen 0.0.0.0:4000 \
--runtime podman \
--image hero_zero:latest \
--redis-url redis://127.0.0.1:6379
CLI Options
| Flag | Default | Description |
|---|---|---|
--listen |
0.0.0.0:4000 |
Server listen address |
--redis-url |
redis://127.0.0.1:6379 |
Redis URL for state persistence |
--runtime |
podman |
Container runtime (podman or docker) |
--image |
hero_zero:latest |
Hero Zero container image |
--mycelium-peers |
tcp://188.40.132.242:9651 |
Mycelium peer endpoints (comma-separated) |
--ledger-network |
local |
NEAR ledger network (local, dev, test, main) |
--ledger-activation-url |
http://localhost:8080 |
Faucet/relayer endpoint |
--log-level |
info |
Log level |
JSON-RPC API
Endpoint: POST /rpc
All methods use JSON-RPC 2.0 over HTTP.
hero.create
Create a new hero. Generates a mycelium identity, persists keys, and launches a hero_zero container.
{
"jsonrpc": "2.0",
"method": "hero.create",
"params": {
"password": "user-chosen-password",
"name": "heroname",
"tier": 1
},
"id": 1
}
Params:
| Field | Type | Required | Description |
|---|---|---|---|
password |
string | yes | Password for encrypting the hero's private key |
name |
string | no | Requested hero name (DNS registration done separately) |
tier |
number | no | Tier: 0=Explorer (free), 1=Pioneer ($10/mo), 2=Founder ($50/mo) |
Response:
{
"hero_id": "uuid",
"mycelium_address": "4xx:xxxx:...",
"mycelium_pubkey": "hex64",
"status": "launching"
}
The container launches in the background. Poll hero.status to track progress through: launching → initializing → running.
hero.status
Get current status and details for a hero.
{
"jsonrpc": "2.0",
"method": "hero.status",
"params": { "hero_id": "uuid" },
"id": 1
}
Response: Full HeroRecord object with fields: hero_id, container_id, mycelium_addr, mycelium_pubkey, account_id, name, tier, status, created_at, error.
hero.list
List all heroes managed by this launcher.
{
"jsonrpc": "2.0",
"method": "hero.list",
"params": {},
"id": 1
}
Response: Array of HeroRecord objects.
hero.stop
Stop a running hero container.
{
"jsonrpc": "2.0",
"method": "hero.stop",
"params": { "hero_id": "uuid" },
"id": 1
}
hero.logs
Get recent logs from a hero container.
{
"jsonrpc": "2.0",
"method": "hero.logs",
"params": { "hero_id": "uuid", "tail": 100 },
"id": 1
}
hero.register_name
Admin-only. Register a DNS name on the ledger identity contract. This is separated from hero.create because it requires payment verification — only the launcher (not the hero container) has admin access to the DNS contract.
{
"jsonrpc": "2.0",
"method": "hero.register_name",
"params": {
"hero_id": "uuid",
"name": "heroname"
},
"id": 1
}
Response:
{
"hero_id": "uuid",
"name": "heroname",
"status": "registered"
}
Architecture
src/
├── main.rs # CLI entry point (clap)
├── config.rs # LauncherConfig + path helpers
├── server.rs # Axum JSON-RPC server + dispatch
├── store.rs # Redis-backed hero state (HeroRecord, HeroStatus)
├── rpc/
│ ├── mod.rs
│ └── hero.rs # RPC method handlers
├── mycelium/
│ ├── mod.rs
│ └── keygen.rs # x25519 keypair + BLAKE3 address derivation
└── container/
├── mod.rs
└── manager.rs # podman/docker lifecycle (launch, stop, logs)
Mycelium Key Generation
Each hero gets a unique mycelium identity for cryptographically-verified E2E communication:
- Generate x25519 keypair (Diffie-Hellman)
- BLAKE3 hash the public key → 16 bytes via XOF
- Set first byte to
0x04or0x05(parity) → IPv6 address in400::/7
The secret key is written to disk and mounted into the container at /hero/mycelium/priv_key.bin. The container's mycelium daemon uses this key, giving the hero its own network identity.
Container Launch
Each hero runs in a container with:
--network host— shares host network (mycelium needs TUN)--cap-add NET_ADMIN+--device /dev/net/tun— for mycelium- Volume mounts: mycelium key (read-only), hero data dir
- Environment variables:
HERO_ID,HERO_PASSWORD,HERO_MYCELIUM_ADDR,HERO_LEDGER_NETWORK,HERO_LEDGER_ACTIVATION_URL
The container runs hero_zero which auto-initializes on first boot (see hero_zero docs).
State Storage
Hero records are stored in Redis:
hero:{id}— JSON-serializedHeroRecordheroes— Set of all hero IDs
If Redis is unavailable, the server starts with a noop store (state won't persist across restarts).
Host Data Layout
~/hero_launcher/
└── heroes/
└── {hero_id}/
├── mycelium/
│ └── priv_key.bin # 32-byte x25519 secret key
└── var/ # Mounted as /hero/var in container
Dependencies
- Runtime: podman or docker
- Optional: Redis (for persistent state)
- Network: hero_ledger (NEAR blockchain, for account activation + DNS)
License
Apache-2.0