A modern, type-safe Web3 backend application built with Bun, Hono, and Drizzle ORM. Supports wallet signature authentication and GitHub OAuth integration.
ZakoBox Backend is a lightweight API service designed for decentralized applications (DApps). It provides:
- Wallet Signature Authentication - Sign-In with Ethereum (SIWE) pattern
- GitHub OAuth Integration - Link GitHub accounts to wallet addresses
- Session Management - JWT-based authentication with httpOnly cookies
- Type-Safe Database Operations - Powered by Drizzle ORM
-
Bun
v1.x- High-performance JavaScript runtime- Native TypeScript support
- Built-in test runner and package manager
- Compiles to standalone binary
-
Hono
v4.10.0- Ultra-fast web framework- Lightweight and edge-optimized
- Middleware support (CORS, Logger, JWT)
- Multi-runtime compatible
- PostgreSQL
v17- Primary database - Drizzle ORM
v0.44.6- Type-safe ORM- Zero runtime overhead
- Integrated migration tool
- Redis
v7- In-memory cache- Session message storage (60s TTL)
- Connected via
ioredis
- viem
v2.38.3- TypeScript Ethereum library- Signature verification (
verifyMessage) - Address validation (
isAddress) - Type-safe Ethereum interactions
- Signature verification (
-
Wallet Signature Authentication - SIWE pattern
- Generate challenge messages
- Verify wallet signatures
- 60-second message expiration
-
JWT Authentication - Token-based sessions
- HttpOnly cookies
- SameSite: Strict
- 24-hour validity
- TypeScript
v5.9.3- Strict mode enabled - ESLint
v9.37.0- Code linting with@antfu/eslint-config - Bun Test - Built-in testing framework
- 80% coverage threshold
- Transaction-based test isolation
-
Docker - Multi-stage build
- Builder:
oven/bun:1 - Runner:
ubuntu:24.04 - Output: Standalone binary
- Builder:
-
GitHub Actions - CI/CD pipeline
- Automated linting, testing, type-checking
- Container registry deployment (GHCR)
- SSH-based production deployment
zako-box-be/
├── src/
│ ├── db/ # Database layer
│ │ ├── index.ts # DB/Redis initialization
│ │ └── schema.ts # Drizzle schema definitions
│ ├── handlers/ # API route handlers
│ │ ├── sessions/ # Session management
│ │ ├── session-message/ # Challenge message generation
│ │ └── github-oauth/ # OAuth integration (WIP)
│ ├── middlewares/ # Custom middlewares
│ │ └── jwt.ts # JWT verification
│ ├── utils/ # Utility functions
│ │ ├── redis.ts # Redis key generators
│ │ └── session-message.ts # Message generation
│ ├── bun.d.ts # Bun environment types
│ └── index.ts # Application entry point
├── drizzle/ # Database migrations
├── .github/workflows/ # CI/CD workflows
└── docker-compose.yml # Local development services
All endpoints are prefixed with /api/v1:
POST /api/v1/session-messages # Generate challenge message for signing
POST /api/v1/sessions # Verify signature and create session
GET /api/v1/sessions # Get current session
DELETE /api/v1/sessions # Logout (clear session)
GET /api/v1/github-oauth # GitHub OAuth flow (WIP)
Create a .env.local file with the following variables:
PORT=3001 # Server port
JWT_SECRET=your_secret_key # JWT signing secret
JWT_ISSUER=your_issuer # JWT issuer identifier
DATABASE_URL=postgresql://... # PostgreSQL connection string
REDIS_URL=redis://... # Redis connection string
BUN_ENV=development # Environment: development/production/testingCopy environment file and fill values to what you want, of course you can use default values.
cp .env.example .env.localdocker compose down
docker compose up -d --remove-orphans
bun db:migrate
bun devStart the database and redis containers.
docker compose down
docker compose up -d --remove-orphansRun the tests.
bun db:migrate
bun testStop the database and redis containers.
docker compose downBuild the standalone binary locally:
bun buildThis creates a single executable file ./zako-box-be with no runtime dependencies.
Before deploying to your VPS, ensure you have:
- A VPS with Docker installed (Ubuntu 20.04+ recommended)
- Domain name configured (optional, for HTTPS)
- PostgreSQL and Redis instances (can be on the same VPS or external)
- GitHub Personal Access Token (for private registry access)
SSH into your VPS and install Docker if not already installed:
# Update package list
sudo apt update
# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# Add your user to docker group (optional, to run without sudo)
sudo usermod -aG docker $USERCreate a docker-compose.yml on your VPS for PostgreSQL and Redis:
mkdir -p ~/zako-box-be
cd ~/zako-box-beCreate docker-compose.yml:
version: '3.8'
services:
postgres:
image: postgres:17
restart: always
environment:
POSTGRES_USER: zakobox
POSTGRES_PASSWORD: your_secure_password
POSTGRES_DB: zakobox
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- '5432:5432'
redis:
image: redis:7
restart: always
command: redis-server --requirepass your_redis_password
volumes:
- redis_data:/data
ports:
- '6379:6379'
volumes:
postgres_data:
redis_data:Start the services:
docker compose up -dOption A: Build on VPS directly
# Clone repository
git clone https://github.com/Zako-DAO/zako-box-be.git
cd zako-box-be
# Build Docker image
docker build -t zako-box-be:latest .Option B: Build locally and push to registry
# Build for your VPS architecture
docker buildx build --platform linux/amd64 -t ghcr.io/zako-dao/zako-box-be:latest .
# Login to GitHub Container Registry
echo $GITHUB_TOKEN | docker login ghcr.io -u YOUR_USERNAME --password-stdin
# Push to registry
docker push ghcr.io/zako-dao/zako-box-be:latestCreate .env.production file on your VPS:
PORT=3000
JWT_SECRET=your_very_secure_random_string_here
JWT_ISSUER=zakobox-backend
DATABASE_URL=postgresql://zakobox:your_secure_password@localhost:5432/zakobox
REDIS_URL=redis://default:your_redis_password@localhost:6379
GITHUB_OAUTH_CLIENT_ID=your_github_oauth_client_id
GITHUB_OAUTH_CLIENT_SECRET=your_github_oauth_client_secret
BASE_URL=https://your-domain.com
BUN_ENV=productionGenerate secure JWT secret:
openssl rand -base64 32Before starting the application, run migrations:
# Pull the image (if using registry)
docker pull ghcr.io/zako-dao/zako-box-be:latest
# Run migrations (one-time setup)
docker run --rm \
--env-file .env.production \
--network host \
ghcr.io/zako-dao/zako-box-be:latest \
bun db:migratedocker run -d \
--name zako-box-be \
--restart always \
--env-file .env.production \
-p 3000:3000 \
--network host \
ghcr.io/zako-dao/zako-box-be:latestUsing local build:
docker run -d \
--name zako-box-be \
--restart always \
--env-file .env.production \
-p 3000:3000 \
--network host \
zako-box-be:latestCheck if the container is running:
docker ps | grep zako-box-beView logs:
docker logs -f zako-box-beTest the API:
curl http://localhost:3000/api/v1/sessionsInstall Nginx:
sudo apt install nginx certbot python3-certbot-nginxCreate Nginx configuration /etc/nginx/sites-available/zako-box-be:
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}Enable the site and obtain SSL certificate:
sudo ln -s /etc/nginx/sites-available/zako-box-be /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
sudo certbot --nginx -d your-domain.comThe repository includes a GitHub Actions workflow for automated deployment on version tags.
In your GitHub repository, go to Settings > Secrets and variables > Actions and add:
SSH_HOST: Your VPS IP addressSSH_USERNAME: SSH username (e.g.,ubuntu,root)SSH_KEY: Private SSH key for authenticationSSH_PORT: SSH port (default:22)
- Tag a release:
git tag v1.0.0
git push origin v1.0.0-
GitHub Actions will automatically:
- Build the Docker image
- Push to GitHub Container Registry (
ghcr.io/zako-dao/zako-box-be:v1.0.0) - SSH into your VPS
- Stop and remove old container
- Pull new image
- Start new container
-
Monitor deployment:
Visit Actions tab in GitHub to watch the deployment progress.
On your VPS, ensure:
# Docker is installed
docker --version
# User has docker permissions
sudo usermod -aG docker $USER
# GitHub Container Registry authentication (one-time)
echo $GITHUB_TOKEN | docker login ghcr.io -u YOUR_USERNAME --password-stdin
# Create .env file in home directory
nano ~/.env.production
# (paste your environment variables)Modify the GitHub Actions workflow to use your env file:
Edit .github/workflows/deploy.yml line 54:
sudo docker run -dit --restart=always --env-file ~/.env.production -p 3000:3000 --name zako-box-be ghcr.io/zako-dao/zako-box-be:${{ github.ref_name }}View logs:
docker logs -f zako-box-beRestart container:
docker restart zako-box-beStop container:
docker stop zako-box-beUpdate to new version:
docker stop zako-box-be
docker rm zako-box-be
docker pull ghcr.io/zako-dao/zako-box-be:latest
docker run -d --name zako-box-be --restart always --env-file .env.production -p 3000:3000 ghcr.io/zako-dao/zako-box-be:latestAccess container shell:
docker exec -it zako-box-be shContainer won't start:
# Check logs for errors
docker logs zako-box-be
# Verify environment variables
docker exec zako-box-be envDatabase connection issues:
# Test PostgreSQL connection
docker exec zako-box-be pg_isready -h localhost -p 5432
# Test Redis connection
docker exec zako-box-be redis-cli -h localhost pingPort already in use:
# Find process using port 3000
sudo lsof -i :3000
# Kill the process or use different port
docker run -p 3001:3000 ...Run migrations manually:
docker exec zako-box-be bun db:migrateCreate a simple health check script:
#!/bin/bash
# health-check.sh
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/api/v1/sessions)
if [ $RESPONSE -eq 401 ]; then
echo "✅ API is healthy (returned expected 401)"
exit 0
else
echo "❌ API health check failed (returned $RESPONSE)"
exit 1
fiAdd to crontab for monitoring:
*/5 * * * * /path/to/health-check.sh || docker restart zako-box-beDatabase backup:
# Backup PostgreSQL
docker exec postgres pg_dump -U zakobox zakobox > backup-$(date +%Y%m%d).sql
# Restore from backup
docker exec -i postgres psql -U zakobox zakobox < backup-20250126.sqlRedis backup:
# Trigger save
docker exec redis redis-cli --pass your_redis_password SAVE
# Copy backup file
docker cp redis:/data/dump.rdb ./redis-backup-$(date +%Y%m%d).rdb