# 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)

| Service | Purpose | Variable |
| --- | --- | --- |
| Alchemy (Robinhood Chain) | primary mainnet and testnet RPC | `RPC_URL_MAINNET`, `RPC_URL_TESTNET` |
| Second RPC provider | fallback | `RPC_URL_MAINNET_FALLBACK` |
| Surplus Intelligence | agent inference | `SURPLUS_API_KEY`, `SURPLUS_BASE_URL` |
| Image generator (through Surplus if available, otherwise a dedicated provider) | Designer | `IMAGE_API_KEY` |
| X API (paid write access in the source plan) | Vivra and CTO token posts | `X_API_KEY`, `X_API_SECRET`, then one OAuth token per account stored encrypted in the database |
| Telegram (@BotFather) | Vivra bot and TG Bot | `TELEGRAM_BOT_TOKEN`, `TELEGRAM_ALERTS_CHAT_ID` |
| Email service (Resend or Postmark) | magic links and codes | `EMAIL_API_KEY`, `EMAIL_FROM` |
| Invisible captcha (Cloudflare Turnstile) | signup | `TURNSTILE_SECRET` |
| Cloudflare Pages (or Vercel) | CTO token websites | `PAGES_API_TOKEN`, `PAGES_ACCOUNT_ID` |
| Vivra domain DNS (Cloudflare) | `<ticker>.<domain>` subdomains | `DNS_ZONE_ID` |
| Object storage (Cloudflare R2 or S3) | visuals and memes | `S3_ENDPOINT`, `S3_BUCKET`, `S3_ACCESS_KEY`, `S3_SECRET_KEY` |
| Container host (Railway, Fly.io, or Render) | API and workers | managed by host |
| Managed Postgres + managed Redis | data and queues | `DATABASE_URL`, `REDIS_URL` |
| Secrets vault (host-provided or Doppler) | hot-wallet keys | see below |
| Sentry | errors | `SENTRY_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)

| Queue | Trigger | Purpose |
| --- | --- | --- |
| `projects.analyze` | team address submission or rerun | complete Scanner analysis of one token |
| `tracking.candidates` | every 15 min, the 5 candidates in the current vote | live market data and recovery score |
| `tracking.live` | continuous, active CTO token only (pool or curve events) | chart, slices, war room |
| `tracking.followup` | hourly, tokens taken over through Day 30 | follow-up, verdict, handover |
| `cycles.clock` | every minute | open and close voting, create operation at Day 0 |
| `orchestrator.tick` | every minute | start due steps (Appendix C) |
| `agents.run` | orchestrator request | run an agent, concurrency 3 |
| `tg.updates` | Telegram webhook | TG Bot anti-spam and answers |
| `buyback.slices` | delayed job per slice, at its selected time | execute a slice |
| `buyback.refill` | daily | refill hot wallet from reserve |
| `treasury.daily` | daily at 00:30 UTC | sweep, claim, split, Surplus top-up, ledger |
| `social.publish` | every minute | publish approved posts whose scheduled time has passed |
| `alerts.check` | every minute | thresholds 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

| Environment | Where | When |
| --- | --- | --- |
| Preview | one frontend URL per pull request | every PR |
| Staging | container host + Robinhood Chain testnet | every merge to `main` |
| Pre-production | staging pointed at a mainnet fork | before launch (milestone M5) |
| Production | container host + mainnet | manually 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.

## Recommended implementation order

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).
