Skip to main content

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

ComponentTechResponsibility
bitview-app (public-web)Next.js 16 + React 19 + Tailwind v4 + next-intl + Solana wallet-adapter + Metaplex (Core / Bubblegum) + Meteora @meteora-ag/cp-amm-sdkConsumer 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 fallbackInternal 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-ircBackend — 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 CLIThree 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-managerSingle sign-on at identity.bitview.club. Two realms (bitview users / streamers, bitview-ops staff). Twitch / GitHub / Google federation. MFA for ops.
checkpointDocusaurusThis documentation site.

Data flow

Accrual

Every ACCRUAL_TICK_SECONDS (default 60s) the bot:

  1. Loads distributions with status Active.
  2. For each, ensures the Twitch IRC listener is up for the channel.
  3. Snapshots present logins from the in-memory presence map.
  4. Resolves linked viewers (users collection) and upserts an accruals row per (distribution, twitch_login): +amount, +1 tick.
  5. If the gap since the last tick exceeds 1.5× the interval, catches up min(gap_ticks, MAX_CATCHUP_TICKS) at once.
  6. Skips viewers above max_per_viewer. Stops when the pool is exhausted.
  7. Updates accrual_live_snapshots rollups 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):

  1. POST /distributions-api/{id}/finalize flips status to snapshotting.
  2. bitview-snapshot reads 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.
  3. The bot returns an unsigned init_distributor (bundled with streamer-vault::withdraw_for_distribution for Identity-tier STREAM distributions); the streamer's wallet signs and submits.
  4. Status flips to Claimable once the root is on-chain.

Claim

  1. The viewer hits the rewards page. The frontend calls GET /claims-api/summary/{wallet}.
  2. For each claimable entry, the frontend calls GET /claims-api/proof/{distribution_id}/{wallet}. The bot proxies the distributor proof API.
  3. The bot returns an unsigned claim (SPL) or claim_nft_core (NFT) transaction via /distributor-tx-api/build-claim*.
  4. The viewer's wallet signs and submits.
  5. Tokens land in the viewer's ATA, or a fresh Metaplex Core asset lands in their wallet.

Streamer-token launch

  1. Public-web's /studio/token walks the streamer through 8 phases: create Token-2022 mint, mint supply, attach metadata, revoke mint authority, initialize the streamer-vault PDA with 85% of supply, create the Meteora DLMM lb_pair, seed the pool atomically with streamer-vault::withdraw_for_pool_seeding + add_liquidity_by_strategy2, create two Streamflow vesting streams (15% reserve + 5% protocol).
  2. Each phase signs its own transaction; the bot persists signatures and PDAs to streamer_tokens.launch_progress so the wizard resumes on return.

Deployment shape

ProcessPortsHosting
bitview-bot4477 (REST + Swagger), 9100 (Prometheus)Kubernetes deployment, active/passive across replicas (only one attaches to Twitch IRC at a time).
bitview-app (public-web)3000Kubernetes deployment behind Cloudflare / nginx ingress; multi-region capable.
bitview-admin (admin-web)3000Kubernetes deployment behind Cloudflare Access or an equivalent identity-aware proxy. Keycloak gate is the primary auth.
distributor/api7001Single process or scaled horizontally; stateless once tree files are loaded.
MongoDB27017Replica set (3 nodes).
Solana RPCManaged providers in failover order (Helius / Triton / Quicknode).
KeycloakHA 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

The Technical overview page is the entry point.