vivraDocumentationV1How voting works

Appendix F · Project setup

Everything needed to start development on day one: repository structure, accounts and keys, environment variables, local startup, workers and schedules, and deployment.

Repository structure#

One repository (pnpm + Turborepo monorepo), TypeScript everywhere except contracts.

text
vivra/
├── apps/
│   ├── web/            # Next.js: landing + dapp (4 screens)
│   ├── admin/          # Next.js: back-office (SIWE)
│   ├── api/            # HTTP + WebSocket server (Fastify)
│   └── workers/        # BullMQ: analysis, tracking, orchestrator, agents, buyback, treasury, bots
├── packages/
│   ├── db/             # schema, migrations, client (Appendix A)
│   ├── core/           # pure business logic: statuses, recovery score, voting rules
│   ├── chain/          # chain configuration, ABIs, viem reads and writes (Appendix D)
│   ├── agents/         # YAML specs, prompts, output schemas, tools (Appendix C)
│   ├── templates/      # website, visual, and mini-app templates
│   └── ui/             # shared components, design tokens
├── contracts/          # Foundry : BuybackBurner + tests
├── infra/              # Dockerfiles, docker-compose, deployment configuration
└── .github/workflows/  # CI

Accounts and keys to create (milestone M0)#

ServicePurposeVariable
Alchemy (Robinhood Chain)primary mainnet and testnet RPCRPC_URL_MAINNET, RPC_URL_TESTNET
Second RPC providerfallbackRPC_URL_MAINNET_FALLBACK
Surplus Intelligenceagent inferenceSURPLUS_API_KEY, SURPLUS_BASE_URL
Image generator (through Surplus if available, otherwise a dedicated provider)DesignerIMAGE_API_KEY
X API (paid write access in the source plan)Vivra and CTO token postsX_API_KEY, X_API_SECRET, then one OAuth token per account stored encrypted in the database
Telegram (@BotFather)Vivra bot and TG BotTELEGRAM_BOT_TOKEN, TELEGRAM_ALERTS_CHAT_ID
Email service (Resend or Postmark)magic links and codesEMAIL_API_KEY, EMAIL_FROM
Invisible captcha (Cloudflare Turnstile)signupTURNSTILE_SECRET
Cloudflare Pages (or Vercel)CTO token websitesPAGES_API_TOKEN, PAGES_ACCOUNT_ID
Vivra domain DNS (Cloudflare)<ticker>.<domain> subdomainsDNS_ZONE_ID
Object storage (Cloudflare R2 or S3)visuals and memesS3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY, S3_SECRET_KEY
Container host (Railway, Fly.io, or Render)API and workersmanaged by host
Managed Postgres + managed Redisdata and queuesDATABASE_URL, REDIS_URL
Secrets vault (host-provided or Doppler)hot-wallet keyssee below
SentryerrorsSENTRY_DSN

Environment variables#

Commit .env.example without any secret values. Actual values belong in the secrets vault.

bash
# General
NODE_ENV=development
APP_URL=http://localhost:3000
API_URL=http://localhost:4000
CHAIN_ENV=testnet                     # testnet | fork | mainnet

# Data
DATABASE_URL=postgres://vivra:vivra@localhost:5432/vivra
REDIS_URL=redis://localhost:6379

# Security
SESSION_SECRET=                       # 32 random bytes
EMAIL_HMAC_KEY=                       # for users.email_hash
EMAIL_ENC_KEY=                        # AES-256-GCM for users.email_enc
SLICE_SCHEDULE_KEY=                   # encrypts slice times

# Chain
RPC_URL_TESTNET=
RPC_URL_MAINNET=
RPC_URL_MAINNET_FALLBACK=
BUYBACK_BURNER_ADDRESS=
BUYBACK_HOT_WALLET_KEY_REF=           # vault reference, never the key itself
AI_HOT_WALLET_KEY_REF=
TREASURY_OPERATOR_KEY_REF=            # signs escrow claims (Collection and CTO Fees wallets)

# Inference and services
SURPLUS_BASE_URL=https://api.surplusintelligence.ai/v1
SURPLUS_API_KEY=
IMAGE_API_KEY=
X_API_KEY=
X_API_SECRET=
TELEGRAM_BOT_TOKEN=
TELEGRAM_ALERTS_CHAT_ID=
EMAIL_API_KEY=
EMAIL_FROM=vote@<vivra-domain>
TURNSTILE_SECRET=
PAGES_API_TOKEN=
PAGES_ACCOUNT_ID=
DNS_ZONE_ID=
S3_ENDPOINT=
S3_BUCKET=
S3_ACCESS_KEY=
S3_SECRET_KEY=
SENTRY_DSN=

Hot-wallet private keys must never appear in environment variables readable by all services. Only the relevant worker reads its key from the vault at startup.

Running locally#

bash
pnpm install
docker compose -f infra/docker-compose.yml up -d     # Postgres 16 + Redis 7
pnpm db:migrate && pnpm db:seed                      # seed: 30 fictional tokens, 1 cycle, 1 demo operation
pnpm dev                                             # web :3000, admin :3001, api :4000, workers
cd contracts && forge test                           # BuybackBurner tests
forge test --fork-url $RPC_URL_MAINNET               # mainnet-fork tests

pnpm dev starts workers with CHAIN_ENV=testnet: no mainnet write is possible unless CHAIN_ENV is mainnet.

Queues and scheduled jobs (BullMQ)#

QueueTriggerPurpose
projects.analyzeteam address submission or reruncomplete Scanner analysis of one token
tracking.candidatesevery 15 min, the 5 candidates in the current votelive market data and recovery score
tracking.livecontinuous, active CTO token only (pool or curve events)chart, slices, war room
tracking.followuphourly, tokens taken over through Day 30follow-up, verdict, handover
cycles.clockevery minuteopen and close voting, create operation at Day 0
orchestrator.tickevery minutestart due steps (Appendix C)
agents.runorchestrator requestrun an agent, concurrency 3
tg.updatesTelegram webhookTG Bot anti-spam and answers
buyback.slicesdelayed job per slice, at its selected timeexecute a slice
buyback.refilldailyrefill hot wallet from reserve
treasury.dailydaily at 00:30 UTCsweep, claim, split, Surplus top-up, ledger
social.publishevery minutepublish approved posts whose scheduled time has passed
alerts.checkevery minutethresholds from the Security page

Each job is idempotent: retrying after a crash must not repeat a transaction already sent. Check tx_hash in the database before sending.

Deployment#

EnvironmentWhereWhen
Previewone frontend URL per pull requestevery PR
Stagingcontainer host + Robinhood Chain testnetevery merge to main
Pre-productionstaging pointed at a mainnet forkbefore launch (milestone M5)
Productioncontainer host + mainnetmanually triggered v* tag

CI (GitHub Actions) on every PR: lint, types, unit tests, forge test, application builds, and secret scan (gitleaks). A failed step blocks merging.

The development roadmap defines milestones. Within the code, this order minimizes blocked work:

  1. packages/db and packages/chain (schema, configuration, ABIs).
  2. On-demand analysis and recovery score, plus submission and review back-office (M1).
  3. Orchestrator with stub agents returning fixed outputs, then real agents one at a time (M2).
  4. BuybackBurner on testnet, then Buyback Bot and treasury (M3).
  5. Voting, back-office, complete dapp, bots (M4).
  6. Full-cycle rehearsal on a mainnet fork (M5).
↑ ↓ to navigate · Enter to openVIVRA DOCS