Architecture
identity (Keycloak)
https://identity.bitview.club
realms: bitview, bitview-ops
│ │
OIDC PKCE bearer verify
(browsers) (JWKS, cached)
│ │
▼ ▼
┌──────────────────────────┐ ┌──────────────────────────┐
│ public-web │ │ bot │
│ (bitview-app) │◀── REST + Swagger ── │
│ Next.js 16, App Router │ │ Rust, actix-web 4 │
│ │ │ │
│ • viewer dashboard │ │ • Twitch IRC oracle │
│ • streamer studio │ │ • Accrual loop │
│ • claim + swap UX │ │ • Merkle assembly │
│ • token launch wizard │ │ • Unsigned-tx builders │
│ • i18n (next-intl) │ │ • Stripe billing │
└────────────┬─────────────┘ │ • tx_log + watcher │
│ └─┬──────────────────────┬─┘
│ │ │
▼ ▼ ▼
┌──────────────────────────┐ ┌─────────────┐ ┌───────────────────┐
│ admin-web │ │ MongoDB │ │ distributor/api │
│ (bitview-admin) │ │ (state, │ │ (axum, proofs) │
│ Next.js 16, App Router │ │ 18 colls) │ └─────────┬─────────┘
│ │ └─────────────┘ ▲
│ • KPI dashboard │ │ tree files
│ • distribution / pool / │ │ produced by
│ treasury / fraud / KYC │ │ bot's
│ • publish wizard │ │ snapshot job
│ • network config │ │
│ • Keycloak gate │ │
└────────────┬─────────────┘ │
│ │
└────── wallet.signTx ────┐ │
│ │
▼ │
┌───────────────────────────────────────────┐
│ Solana cluster │
│ │
│ Three Anchor programs (this workspace): │
│ • merkle-distributor (SPL + NFT claims)│
│ • streamer-vault (85% supply lock) │
│ • launchpad-vault (BTV match-fund) │
│ │
│ External programs: │
│ • Meteora DLMM v2 (BTV/SOL, BTV/USDC, │
│ STREAM/BTV pools) │
│ • Meteora DAMM v2 (Identity-tier launch)│
│ • Metaplex Core (NFT collections) │
│ • Metaplex Token Metadata │
│ • Streamflow (vesting streams) │
│ • SPL Token / Token-2022 │
└───────────────────────────────────────────┘
Components
| Component | Tech | Responsibility |
|---|---|---|
bitview-app (public-web) | Next.js 16 + React 19 + Tailwind v4 + next-intl + Solana wallet-adapter + Metaplex (Core / Bubblegum) + Meteora @meteora-ag/cp-amm-sdk | Consumer surface — streamer studio (/studio/*), viewer dashboard, claim UX (SPL + NFT), public swap, marketing pages, locale-scoped routing. |
bitview-admin (admin-web) | Next.js 16 + React 19 + Tailwind v4 + shadcn-style + Tremor-style + ECharts + TanStack + iron-session over Keycloak OIDC + dormant SIWS fallback | Internal operator console — KPI dashboard, distribution / pool / treasury / fraud / sponsorship monitors, KYC application review, network config, publish wizard, audit log. |
bitview-bot (bot) | Rust 1.75+ + actix-web 4 + tokio + MongoDB + twitch-irc | Backend — Twitch presence oracle, accrual loop, merkle assembly, REST API consumed by both web apps, unsigned-tx builders for the entire on-chain product, Stripe billing, OpenAPI/Swagger, Prometheus metrics. |
distributor (Cargo workspace) | Anchor 1.0.2 + solana-program 3.0 + axum + Rust CLI | Three Anchor programs (merkle-distributor, streamer-vault, launchpad-vault) + axum proof API + merkle tree builder + operator CLI. Audited at v0.30; re-audit required after the 1.0 / 3.0 bump. |
identity (supporting infra) | Keycloak (latest LTS) + PostgreSQL + Kubernetes (Helm) + cert-manager | Single sign-on at identity.bitview.club. Two realms (bitview users / streamers, bitview-ops staff). Twitch / GitHub / Google federation. MFA for ops. |
checkpoint | Docusaurus | This documentation site. |
Data flow
Accrual
Every ACCRUAL_TICK_SECONDS (default 60s) the bot:
- Loads
distributionswith statusActive. - For each, ensures the Twitch IRC listener is up for the channel.
- Snapshots present logins from the in-memory presence map.
- Resolves linked viewers (
userscollection) and upserts anaccrualsrow per (distribution, twitch_login):+amount,+1 tick. - If the gap since the last tick exceeds 1.5× the interval, catches
up
min(gap_ticks, MAX_CATCHUP_TICKS)at once. - Skips viewers above
max_per_viewer. Stops when the pool is exhausted. - Updates
accrual_live_snapshotsrollups consumed by the admin dashboard.
A separate watcher polls Twitch Helix /streams and auto-finalizes
distributions whose stream has been offline for more than 3 minutes.
Snapshot / finalize
When a distribution ends (or the operator forces it):
POST /distributions-api/{id}/finalizeflips status tosnapshotting.bitview-snapshotreads accrual rows, builds the SHA256 merkle tree, writes the root + per-leaf proofs to MongoDB (merkle_snapshots+merkle_snapshot_nodes), and the tree file for the axum proof API.- The bot returns an unsigned
init_distributor(bundled withstreamer-vault::withdraw_for_distributionfor Identity-tier STREAM distributions); the streamer's wallet signs and submits. - Status flips to
Claimableonce the root is on-chain.
Claim
- The viewer hits the rewards page. The frontend calls
GET /claims-api/summary/{wallet}. - For each claimable entry, the frontend calls
GET /claims-api/proof/{distribution_id}/{wallet}. The bot proxies the distributor proof API. - The bot returns an unsigned
claim(SPL) orclaim_nft_core(NFT) transaction via/distributor-tx-api/build-claim*. - The viewer's wallet signs and submits.
- Tokens land in the viewer's ATA, or a fresh Metaplex Core asset lands in their wallet.
Streamer-token launch
- Public-web's
/studio/tokenwalks the streamer through 8 phases: create Token-2022 mint, mint supply, attach metadata, revoke mint authority, initialize thestreamer-vaultPDA with 85% of supply, create the Meteora DLMMlb_pair, seed the pool atomically withstreamer-vault::withdraw_for_pool_seeding+add_liquidity_by_strategy2, create two Streamflow vesting streams (15% reserve + 5% protocol). - Each phase signs its own transaction; the bot persists signatures
and PDAs to
streamer_tokens.launch_progressso the wizard resumes on return.
Deployment shape
| Process | Ports | Hosting |
|---|---|---|
bitview-bot | 4477 (REST + Swagger), 9100 (Prometheus) | Kubernetes deployment, active/passive across replicas (only one attaches to Twitch IRC at a time). |
bitview-app (public-web) | 3000 | Kubernetes deployment behind Cloudflare / nginx ingress; multi-region capable. |
bitview-admin (admin-web) | 3000 | Kubernetes deployment behind Cloudflare Access or an equivalent identity-aware proxy. Keycloak gate is the primary auth. |
distributor/api | 7001 | Single process or scaled horizontally; stateless once tree files are loaded. |
| MongoDB | 27017 | Replica set (3 nodes). |
| Solana RPC | — | Managed providers in failover order (Helius / Triton / Quicknode). |
| Keycloak | — | HA Helm deployment on the same Kubernetes cluster. |
MongoDB is the only persistence layer for off-chain product state. The bot's relationship with the chain is read-only (RPC reads + tx status polling). On-chain writes are signed by user wallets.
There is intentionally no separate "frontend backend" — the bot is the backend for both web apps. Adding a second service would just split state.
Per-component deep dives
- public-web (bitview-app) — consumer frontend (streamer studio, viewer dashboard, claim UX)
- admin-web (bitview-admin) — operator console
- bot (bitview-bot) — Rust backend (Twitch presence, accrual, REST API, merkle assembly, unsigned-tx builders)
- distributor — three Anchor programs + axum proof API + Rust CLI
- identity (Keycloak) — supporting infrastructure for human auth
The Technical overview page is the entry point.