ZapBB is a modern, high-performance bulletin board platform built with Next.js 16 and Rust (Axum). It features NextAuth.js sessions for web authentication, JWT-based API tokens, and role-based access control (RBAC).
- Features
- Tech Stack
- Project Structure
- Prerequisites
- Development Setup
- Production Deployment
- Configuration
- Documentation
- Contributing
- License
- Modern Forum: Categories, threads, posts, reactions, polls, and rich text editing
- Authentication: NextAuth.js sessions + JWT API tokens with refresh flow
- Authorization: Role-based access control (Admin, Moderator, Member, Guest)
- Search: Full-text search powered by OpenSearch
- Real-time: WebSocket support for live updates (planned)
- Plugin System: Build-time plugins with UI slots and event hooks
- Theme System: Configurable themes with light/dark mode support
- Performance: Rust backend with sub-200ms cached responses
| Layer | Technology |
|---|---|
| Frontend | Next.js 16, React 19, TypeScript, Tailwind CSS 4 |
| Backend | Rust, Axum, sqlx (compile-time checked queries) |
| Database | PostgreSQL 16 |
| Cache | Redis 7 |
| Search | OpenSearch 2.13 |
| Auth | NextAuth.js, JWT (jsonwebtoken crate) |
| API | OpenAPI 3.1 (utoipa) → TypeScript hooks (OpenAPI-Qraft) |
| Tooling | Bun, Biome, Cargo |
ZapBB/
├── backend/ # Rust API server
│ ├── src/ # Application source code
│ ├── migrations/ # SQL migrations
│ ├── Cargo.toml # Rust dependencies
│ └── .env.example # Environment template
├── frontend/ # Next.js web application
│ ├── app/ # App Router pages
│ ├── components/ # React components
│ ├── lib/ # Utilities and API client
│ ├── plugins.config.ts # Plugin registrations
│ ├── theme.config.ts # Theme definitions
│ └── .env.example # Environment template
├── docs/ # Documentation
├── specs/ # Technical specifications
├── scripts/ # Utility scripts
├── docker-compose.dev.yml
└── README.md
- Rust 1.82+ (rustup.rs)
- Bun 1.2+ (bun.sh)
- PostgreSQL 16+ (postgresql.org)
- Redis 7+ (memurai.com or WSL2)
- OpenSearch 2.13+ (opensearch.org)
- Docker Desktop 4.0+ (docker.com)
- Docker Compose v2+
This is the recommended approach for active development with hot-reload.
git clone https://github.com/your-org/zapbb.git
cd zapbbStart PostgreSQL, Redis, and OpenSearch. You can use Docker for just the services:
docker compose -f docker-compose.dev.yml up -d postgres redis opensearchOr install them natively on Windows.
Backend:
cd backend
Copy-Item .env.example .env
# Edit .env with your local settingsKey backend variables:
DATABASE_URL=postgres://zapbb:devpassword@localhost:5432/zapbb_dev
REDIS_URL=redis://localhost:6379
OPENSEARCH_URL=http://localhost:9200
JWT_SECRET=your-dev-secret-key
RUST_LOG=debug,zapbb=traceFrontend:
cd frontend
Copy-Item .env.example .env.local
# Edit .env.local with your local settingsKey frontend variables:
NEXT_PUBLIC_API_URL=http://localhost:8080
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-nextauth-secretcd backend
cargo install sqlx-cli --no-default-features --features postgres
sqlx database create
sqlx migrate runcd backend
cargo runThe API server starts at http://localhost:8080.
cd frontend
bun install
bun run devThe web app starts at http://localhost:3000.
Use this for a consistent, isolated environment.
git clone https://github.com/your-org/zapbb.git
cd zapbbdocker compose -f docker-compose.dev.yml up --buildThis starts:
- PostgreSQL on port 5432
- Redis on port 6379
- OpenSearch on port 9200
- Backend on port 8080
- Frontend on port 3000
# All services
docker compose -f docker-compose.dev.yml logs -f
# Specific service
docker compose -f docker-compose.dev.yml logs -f backenddocker compose -f docker-compose.dev.yml down
# Remove volumes (reset data)
docker compose -f docker-compose.dev.yml down -vCreate docker-compose.prod.yml:
version: '3.8'
services:
postgres:
image: postgres:16-alpine
container_name: zapbb-postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: zapbb-redis
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
opensearch:
image: opensearchproject/opensearch:2.13.0
container_name: zapbb-opensearch
restart: unless-stopped
environment:
- discovery.type=single-node
- plugins.security.disabled=true
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
volumes:
- opensearch_data:/usr/share/opensearch/data
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1"]
interval: 30s
timeout: 10s
retries: 5
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: zapbb-backend
restart: unless-stopped
environment:
DATABASE_URL: postgres://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379
OPENSEARCH_URL: http://opensearch:9200
JWT_SECRET: ${JWT_SECRET}
RUST_LOG: info,zapbb=debug
ports:
- "8080:8080"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
opensearch:
condition: service_healthy
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: zapbb-frontend
restart: unless-stopped
environment:
NEXT_PUBLIC_API_URL: ${API_URL}
NEXTAUTH_URL: ${SITE_URL}
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
ports:
- "3000:3000"
depends_on:
- backend
nginx:
image: nginx:alpine
container_name: zapbb-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- frontend
- backend
volumes:
postgres_data:
redis_data:
opensearch_data:Backend Dockerfile (backend/Dockerfile):
# Stage 1: Builder
FROM rust:1.82-slim as builder
WORKDIR /app
RUN apt-get update && apt-get install -y \
pkg-config \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs
RUN cargo build --release
RUN rm -rf src
COPY . .
RUN cargo build --release
# Stage 2: Runtime
FROM debian:bookworm-slim
WORKDIR /app
RUN apt-get update && apt-get install -y \
ca-certificates \
libssl3 \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/zapbb /usr/local/bin/
RUN useradd -r -u 1001 zapbb && chown -R zapbb:zapbb /app
USER zapbb
EXPOSE 8080
CMD ["zapbb"]Frontend Dockerfile (frontend/Dockerfile):
# Stage 1: Dependencies
FROM oven/bun:1.2 as deps
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
# Stage 2: Builder
FROM oven/bun:1.2 as builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN bun run build
# Stage 3: Runner
FROM oven/bun:1.2-slim as runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["bun", "server.js"]Create .env.prod:
# Database
DB_USER=zapbb
DB_PASSWORD=secure-production-password
DB_NAME=zapbb_prod
# Redis
REDIS_PASSWORD=secure-redis-password
# Authentication
JWT_SECRET=your-production-jwt-secret-min-32-chars
NEXTAUTH_SECRET=your-production-nextauth-secret
# URLs
API_URL=https://api.yourdomain.com
SITE_URL=https://yourdomain.com# Load environment
export $(cat .env.prod | xargs)
# Build and start
docker compose -f docker-compose.prod.yml up -d --build
# Run migrations
docker compose -f docker-compose.prod.yml exec backend sqlx migrate run
# View logs
docker compose -f docker-compose.prod.yml logs -fFor VPS or bare-metal servers without Docker.
# Build release binary
cd backend
cargo build --release
# Copy binary to server
scp target/release/zapbb user@server:/opt/zapbb/
# Create systemd service
sudo tee /etc/systemd/system/zapbb-backend.service << EOF
[Unit]
Description=ZapBB Backend
After=network.target postgresql.service redis.service
[Service]
Type=simple
User=zapbb
WorkingDirectory=/opt/zapbb
ExecStart=/opt/zapbb/zapbb
Restart=always
EnvironmentFile=/opt/zapbb/.env
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable zapbb-backend
sudo systemctl start zapbb-backend# Build production bundle
cd frontend
bun install
bun run build
# Copy to server
rsync -avz .next/standalone/ user@server:/opt/zapbb/frontend/
rsync -avz .next/static/ user@server:/opt/zapbb/frontend/.next/static/
rsync -avz public/ user@server:/opt/zapbb/frontend/public/
# Create systemd service
sudo tee /etc/systemd/system/zapbb-frontend.service << EOF
[Unit]
Description=ZapBB Frontend
After=network.target
[Service]
Type=simple
User=zapbb
WorkingDirectory=/opt/zapbb/frontend
ExecStart=/usr/local/bin/bun server.js
Restart=always
Environment=NODE_ENV=production
Environment=PORT=3000
EnvironmentFile=/opt/zapbb/frontend/.env
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable zapbb-frontend
sudo systemctl start zapbb-frontend| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Required |
REDIS_URL |
Redis connection string | Required |
OPENSEARCH_URL |
OpenSearch URL | Required |
JWT_SECRET |
Secret for JWT signing (min 32 chars) | Required |
RUST_LOG |
Log level | info |
HOST |
Bind address | 0.0.0.0 |
PORT |
Listen port | 8080 |
| Variable | Description | Default |
|---|---|---|
NEXT_PUBLIC_API_URL |
Backend API URL | Required |
NEXTAUTH_URL |
Canonical site URL | Required |
NEXTAUTH_SECRET |
NextAuth.js secret | Required |
NEXT_PUBLIC_SITE_NAME |
Site display name | ZapBB Forum |
See backend/.env.example and frontend/.env.example for full variable lists.
| Document | Description |
|---|---|
| PRD | Product requirements and MVP scope |
| Technical Architecture | System design overview |
| Database Schema | All tables and relationships |
| API Specification | Endpoint contracts |
| Security & Auth | Authentication flows, RBAC |
| Deployment | Full deployment guide |
| Plugin Authoring | Build custom plugins |
| Theme Authoring | Create custom themes |
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please read our contributing guidelines and code of conduct.
This project is licensed under the MIT License. See LICENSE for details.