Skip to main content

Priva Architecture

High-Level Overview

                          ┌─────────────────────────────┐
│ privanet.dev │
│ (Caddy reverse proxy) │
│ HTTPS (Let's Encrypt auto) │
└───────┬──────┬───────┬────────┘
│ │ │
┌───────┘ ┌───┘ ┌──┘
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────────┐
│ Web App │ │ API │ │ Docs (static)│
│ Next.js │ │ Node.js │ │ /docs/* │
│ :3001 │ │ :3000 │ │ /srv/docs │
└──────────┘ └────┬─────┘ └──────────────┘

┌────────────┴───────────┐
▼ ▼
┌────────────┐ ┌───────────────┐
│ PostgreSQL │ │ Redis │
│ :5432 │ │ :6379 │
│ (pgdata) │ │ (sessions, │
└────────────┘ │ cache) │
└───────────────┘

┌──────────────────────────────────────────┐
│ P2P Network Layer │
│ │
│ ┌────────────┐ ┌────────────────┐ │
│ │ node-client│ │ node-client │ │
│ │ (your VPS)│◄───►│ (other nodes) │ │
│ │ :8336 │ │ :8336 │ │
│ └─────┬──────┘ └────────────────┘ │
│ │ libp2p QUIC/TCP │
└────────┼─────────────────────────────────┘

▼ reports uptime + work proofs
┌────────────────┐
│ Priva API │
│ (reward calc) │
└────────────────┘

Components

Caddy (Reverse Proxy)

Caddy is the public-facing entry point, running on ports 80 and 443. It handles:

  • Automatic HTTPS via Let's Encrypt (zero configuration)
  • Routing/api/* → API, /docs/* → static files, everything else → Next.js web app
  • Security headers — HSTS, X-Frame-Options, Content-Type-Options
  • www → apex redirectwww.privanet.dev redirects to privanet.dev

API (Node.js / Express)

The backend API provides:

  • Authentication — JWT-based (access + refresh tokens)
  • Node registry — stores registered node metadata, verifies P2P reachability
  • Reward calculation — tracks uptime proofs and calculates PRIVA earnings
  • Points system — daily farming, streak tracking, referral bonuses
  • Database migrations — managed by Drizzle ORM with automatic migration on startup

Tech stack: Node.js 20, TypeScript, Express, Drizzle ORM, PostgreSQL, Redis

Web App (Next.js)

The user-facing dashboard built with Next.js 14:

  • App Router architecture
  • Server-side rendering for authenticated pages
  • Web3 wallet integration via WalletConnect / wagmi
  • Real-time updates via WebSocket/polling

Deployed with: output: 'standalone' for minimal Docker image size

node-client (Go)

The Priva node, written in Go:

  • libp2p for P2P networking (QUIC transport over UDP, TCP fallback)
  • LevelDB for local peer state persistence
  • Prometheus metrics on /metrics endpoint
  • Keystore — encrypted wallet identity (unlocked via passphrase)
  • API heartbeat — reports uptime proofs to the Priva API every minute

Built with: Go 1.25, libp2p, LevelDB

PostgreSQL

Relational database storing:

  • User accounts and authentication
  • Node registrations and metadata
  • Reward history and ledger
  • Governance proposals and votes
  • Referral relationships

Migrations run automatically at API startup via Drizzle.

Redis

In-memory store for:

  • JWT refresh token blacklist
  • Rate limiting counters
  • Session cache
  • API response caching

Docker Network

All services communicate over an internal Docker bridge network named privanet. Only Caddy and node-client expose ports publicly:

ServicePublic PortsInternal Port
caddy80, 443
node-client8336 (TCP+UDP)9090 (metrics)
api3000
web3001
postgres5432
redis6379

Data Flow: Daily Farming

User → Browser → privanet.dev (Caddy)
→ /api/v1/farming/claim (API)
→ Validate JWT
→ Check last claim time (Redis)
→ Calculate points (streak × multiplier)
→ Update PostgreSQL
→ Return new balance

Data Flow: Node Reward

node-client → API /api/v1/nodes/heartbeat (every 60s)
→ Verify node signature
→ Calculate uptime %
→ Update node metrics in PostgreSQL
→ (Reward distribution runs via scheduled job)

Security Model

  • TLS everywhere — all public traffic over HTTPS (Let's Encrypt)
  • No public database — PostgreSQL and Redis are internal-only
  • JWT with short expiry — access tokens expire in 15 minutes, refresh tokens in 7 days
  • Argon2 password hashing — used for user passwords
  • Node identity via cryptographic keypair — nodes can't impersonate each other
  • Admin API protected — separate ADMIN_SECRET header, not user-accessible