- Add React landing page with TailwindCSS + Framer Motion for synor.cc - Add Docker deployment configs for explorer-web and website - Create comprehensive zero-cost deployment plan using: - Vercel (free tier) for all frontends - Supabase (free tier) for PostgreSQL - Upstash (free tier) for Redis rate limiting - Oracle Cloud Always Free for blockchain nodes (24GB RAM) - Update Phase 7 documentation with current status Total estimated cost: $0-1/month for production deployment
610 lines
16 KiB
Markdown
610 lines
16 KiB
Markdown
# Synor Production Deployment Plan
|
|
|
|
> Zero-cost deployment using Vercel, Supabase, and Oracle Cloud Free Tier
|
|
|
|
**Status**: Ready
|
|
**Target**: Q1 2026
|
|
**Last Updated**: January 10, 2026
|
|
**Monthly Cost**: $0
|
|
|
|
---
|
|
|
|
## Architecture Overview
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ VERCEL (Edge Network) │
|
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
│ │ synor.cc │ │ wallet. │ │ explorer. │ │ docs. │ │
|
|
│ │ (Website) │ │ synor.cc │ │ synor.cc │ │ synor.cc │ │
|
|
│ │ React │ │ React │ │ React │ │ Vitepress │ │
|
|
│ └─────────────┘ └─────────────┘ └──────┬──────┘ └─────────────┘ │
|
|
│ FREE │ FREE │
|
|
└───────────────────────────────────────────┼─────────────────────────────────┘
|
|
│
|
|
┌───────────────────────┼───────────────────────┐
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
│ SUPABASE │ │ ORACLE CLOUD │ │ UPSTASH │
|
|
│ PostgreSQL │ │ Free Tier │ │ Redis │
|
|
│ FREE │ │ ARM Servers │ │ FREE │
|
|
└─────────────────┘ └────────┬────────┘ └─────────────────┘
|
|
│
|
|
┌───────────────────────────┼───────────────────────────┐
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
│ ARM Instance 1 │ │ ARM Instance 2 │ │ ARM Instance 3 │
|
|
│ Seed + API │◄───────►│ Seed Node │◄───────►│ Seed Node │
|
|
│ 6GB RAM │ │ 6GB RAM │ │ 6GB RAM │
|
|
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
ORACLE CLOUD ALWAYS FREE
|
|
```
|
|
|
|
---
|
|
|
|
## Zero-Cost Service Stack
|
|
|
|
| Service | Platform | Tier | Cost |
|
|
|---------|----------|------|------|
|
|
| Marketing Website | Vercel | Hobby | **$0** |
|
|
| Web Wallet | Vercel | Hobby | **$0** |
|
|
| Block Explorer | Vercel | Hobby | **$0** |
|
|
| Documentation | Vercel | Hobby | **$0** |
|
|
| Database (PostgreSQL) | Supabase | Free (500MB) | **$0** |
|
|
| Rate Limiting (Redis) | Upstash | Free (10K/day) | **$0** |
|
|
| Seed Nodes (3x ARM) | Oracle Cloud | Always Free | **$0** |
|
|
| Domain | Freenom/GitHub | .tk/.ml/.cc | **$0-1** |
|
|
| **Total** | | | **$0/month** |
|
|
|
|
---
|
|
|
|
## Platform Details
|
|
|
|
### 1. Vercel (Frontend Hosting)
|
|
|
|
**Free Tier Includes:**
|
|
- Unlimited static sites
|
|
- 100GB bandwidth/month
|
|
- Automatic SSL
|
|
- Global CDN
|
|
- GitHub integration
|
|
- Preview deployments
|
|
|
|
**Limitations:**
|
|
- 100 deployments/day
|
|
- Serverless function timeout: 10s
|
|
- No commercial use without Pro
|
|
|
|
### 2. Supabase (Database)
|
|
|
|
**Free Tier Includes:**
|
|
- 500MB PostgreSQL database
|
|
- 1GB file storage
|
|
- 50,000 monthly active users
|
|
- Unlimited API requests
|
|
- Real-time subscriptions
|
|
|
|
**Limitations:**
|
|
- Paused after 1 week inactivity (can be resumed)
|
|
- 2 projects max
|
|
|
|
### 3. Upstash (Redis)
|
|
|
|
**Free Tier Includes:**
|
|
- 10,000 commands/day
|
|
- 256MB storage
|
|
- Global edge caching
|
|
- REST API
|
|
|
|
**Limitations:**
|
|
- Single region (choose closest to Oracle)
|
|
|
|
### 4. Oracle Cloud Always Free
|
|
|
|
**This is the key to zero-cost blockchain hosting!**
|
|
|
|
**Always Free Includes:**
|
|
- **4 ARM Ampere A1 OCPUs** (can split across VMs)
|
|
- **24GB RAM total** (can split across VMs)
|
|
- **200GB block storage**
|
|
- **10TB outbound data/month**
|
|
- 2 AMD VMs (1 OCPU, 1GB each) - backup option
|
|
|
|
**Recommended Configuration:**
|
|
| VM | OCPUs | RAM | Storage | Purpose |
|
|
|----|-------|-----|---------|---------|
|
|
| synor-1 | 2 | 12GB | 100GB | Seed + Explorer API |
|
|
| synor-2 | 1 | 6GB | 50GB | Seed Node |
|
|
| synor-3 | 1 | 6GB | 50GB | Seed Node |
|
|
|
|
---
|
|
|
|
## Setup Instructions
|
|
|
|
### Phase 1: Account Setup (Day 1)
|
|
|
|
#### 1.1 Create Accounts
|
|
|
|
```bash
|
|
# Required accounts (all free)
|
|
1. Vercel → vercel.com/signup
|
|
2. Supabase → supabase.com (GitHub login)
|
|
3. Upstash → upstash.com (GitHub login)
|
|
4. Oracle Cloud → cloud.oracle.com/free
|
|
5. GitHub → github.com (for CI/CD)
|
|
```
|
|
|
|
#### 1.2 Domain Options
|
|
|
|
**Free:**
|
|
- GitHub Pages subdomain: `synor.github.io`
|
|
- Vercel subdomain: `synor.vercel.app`
|
|
- Freenom: `.tk`, `.ml`, `.ga`, `.cf` (unreliable)
|
|
|
|
**Cheap ($1-10/year):**
|
|
- Namecheap: `.cc` domain ~$10/year
|
|
- Cloudflare: At-cost pricing
|
|
- Porkbun: Often cheapest
|
|
|
|
---
|
|
|
|
### Phase 2: Oracle Cloud Setup (Day 1-2)
|
|
|
|
#### 2.1 Create ARM Instances
|
|
|
|
1. Go to Oracle Cloud Console → Compute → Instances
|
|
2. Click "Create Instance"
|
|
3. Select:
|
|
- **Image**: Oracle Linux 8 (or Ubuntu 22.04)
|
|
- **Shape**: VM.Standard.A1.Flex (ARM)
|
|
- **OCPUs**: 2 (for main), 1 (for others)
|
|
- **Memory**: 12GB (main), 6GB (others)
|
|
- **Boot Volume**: 50-100GB
|
|
|
|
#### 2.2 Configure Security Lists
|
|
|
|
```bash
|
|
# Required ingress rules
|
|
Port 22 → SSH (your IP only)
|
|
Port 80 → HTTP (0.0.0.0/0)
|
|
Port 443 → HTTPS (0.0.0.0/0)
|
|
Port 17511 → P2P (0.0.0.0/0)
|
|
Port 17110 → RPC (internal only)
|
|
Port 17111 → WebSocket (internal only)
|
|
```
|
|
|
|
#### 2.3 Initial Setup Script
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Run on each Oracle Cloud instance
|
|
|
|
# Update system
|
|
sudo dnf update -y # Oracle Linux
|
|
# sudo apt update && sudo apt upgrade -y # Ubuntu
|
|
|
|
# Install Docker
|
|
curl -fsSL https://get.docker.com | sh
|
|
sudo systemctl enable docker
|
|
sudo systemctl start docker
|
|
sudo usermod -aG docker $USER
|
|
|
|
# Install Docker Compose
|
|
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
|
sudo chmod +x /usr/local/bin/docker-compose
|
|
|
|
# Open firewall ports
|
|
sudo firewall-cmd --permanent --add-port=17511/tcp
|
|
sudo firewall-cmd --permanent --add-port=80/tcp
|
|
sudo firewall-cmd --permanent --add-port=443/tcp
|
|
sudo firewall-cmd --reload
|
|
|
|
# Create directories
|
|
mkdir -p ~/synor
|
|
mkdir -p /data/synor
|
|
sudo chown $USER:$USER /data/synor
|
|
```
|
|
|
|
#### 2.4 Docker Compose for Main Node (synor-1)
|
|
|
|
```yaml
|
|
# ~/synor/docker-compose.yml
|
|
version: '3.8'
|
|
|
|
services:
|
|
synord:
|
|
image: ghcr.io/synor/synord:latest
|
|
container_name: synord
|
|
restart: unless-stopped
|
|
command:
|
|
- run
|
|
- --p2p-host=0.0.0.0
|
|
- --p2p-port=17511
|
|
- --rpc-host=0.0.0.0
|
|
- --rpc-port=17110
|
|
- --ws-port=17111
|
|
- --mine
|
|
- --coinbase=${COINBASE_ADDRESS}
|
|
ports:
|
|
- "17511:17511"
|
|
- "17110:17110"
|
|
- "17111:17111"
|
|
volumes:
|
|
- /data/synor:/data/synor
|
|
environment:
|
|
- RUST_LOG=info
|
|
- SYNOR_NETWORK=mainnet
|
|
|
|
explorer-api:
|
|
image: ghcr.io/synor/explorer-api:latest
|
|
container_name: explorer-api
|
|
restart: unless-stopped
|
|
environment:
|
|
- DATABASE_URL=${SUPABASE_DATABASE_URL}
|
|
- RPC_URL=http://synord:17110
|
|
- WS_URL=ws://synord:17111
|
|
- RUST_LOG=info
|
|
depends_on:
|
|
- synord
|
|
|
|
caddy:
|
|
image: caddy:alpine
|
|
container_name: caddy
|
|
restart: unless-stopped
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
volumes:
|
|
- ./Caddyfile:/etc/caddy/Caddyfile
|
|
- caddy_data:/data
|
|
- caddy_config:/config
|
|
|
|
volumes:
|
|
caddy_data:
|
|
caddy_config:
|
|
```
|
|
|
|
#### 2.5 Caddyfile (Auto SSL)
|
|
|
|
```caddyfile
|
|
# ~/synor/Caddyfile
|
|
api.synor.cc {
|
|
reverse_proxy explorer-api:3000
|
|
|
|
header {
|
|
Access-Control-Allow-Origin *
|
|
Access-Control-Allow-Methods "GET, POST, OPTIONS"
|
|
Access-Control-Allow-Headers "Content-Type"
|
|
}
|
|
}
|
|
|
|
# WebSocket proxy
|
|
ws.synor.cc {
|
|
reverse_proxy synord:17111
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Phase 3: Supabase Setup (Day 2)
|
|
|
|
#### 3.1 Create Project
|
|
|
|
1. Go to supabase.com → New Project
|
|
2. Name: `synor-explorer`
|
|
3. Database Password: (save securely)
|
|
4. Region: Choose closest to Oracle (e.g., Frankfurt)
|
|
|
|
#### 3.2 Run Migrations
|
|
|
|
```sql
|
|
-- In Supabase SQL Editor
|
|
|
|
-- Blocks table
|
|
CREATE TABLE blocks (
|
|
hash TEXT PRIMARY KEY,
|
|
height BIGINT NOT NULL,
|
|
timestamp TIMESTAMPTZ NOT NULL,
|
|
parent_hashes TEXT[] NOT NULL,
|
|
transaction_count INT NOT NULL DEFAULT 0,
|
|
blue_score BIGINT NOT NULL,
|
|
is_chain_block BOOLEAN NOT NULL DEFAULT false,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_blocks_height ON blocks(height DESC);
|
|
CREATE INDEX idx_blocks_timestamp ON blocks(timestamp DESC);
|
|
CREATE INDEX idx_blocks_blue_score ON blocks(blue_score DESC);
|
|
|
|
-- Transactions table
|
|
CREATE TABLE transactions (
|
|
id TEXT PRIMARY KEY,
|
|
block_hash TEXT REFERENCES blocks(hash),
|
|
is_coinbase BOOLEAN NOT NULL DEFAULT false,
|
|
inputs JSONB NOT NULL DEFAULT '[]',
|
|
outputs JSONB NOT NULL DEFAULT '[]',
|
|
mass BIGINT NOT NULL DEFAULT 0,
|
|
fee BIGINT NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_tx_block ON transactions(block_hash);
|
|
|
|
-- Addresses table
|
|
CREATE TABLE addresses (
|
|
address TEXT PRIMARY KEY,
|
|
balance BIGINT NOT NULL DEFAULT 0,
|
|
tx_count INT NOT NULL DEFAULT 0,
|
|
first_seen TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
last_seen TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
-- Enable public read access
|
|
ALTER TABLE blocks ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE transactions ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE addresses ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY "Public read blocks" ON blocks FOR SELECT USING (true);
|
|
CREATE POLICY "Public read transactions" ON transactions FOR SELECT USING (true);
|
|
CREATE POLICY "Public read addresses" ON addresses FOR SELECT USING (true);
|
|
```
|
|
|
|
#### 3.3 Get Connection String
|
|
|
|
```bash
|
|
# From Supabase Dashboard → Settings → Database → Connection String
|
|
DATABASE_URL=postgres://postgres.[project]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres
|
|
```
|
|
|
|
---
|
|
|
|
### Phase 4: Vercel Deployment (Day 2-3)
|
|
|
|
#### 4.1 Connect Repositories
|
|
|
|
```bash
|
|
# Install Vercel CLI
|
|
npm i -g vercel
|
|
|
|
# Login
|
|
vercel login
|
|
|
|
# Deploy each app
|
|
cd apps/website
|
|
vercel --prod
|
|
|
|
cd ../web
|
|
vercel --prod
|
|
|
|
cd ../explorer-web
|
|
vercel --prod
|
|
```
|
|
|
|
#### 4.2 Add vercel.json to Each App
|
|
|
|
```json
|
|
{
|
|
"rewrites": [
|
|
{ "source": "/(.*)", "destination": "/index.html" }
|
|
],
|
|
"headers": [
|
|
{
|
|
"source": "/(.*)",
|
|
"headers": [
|
|
{ "key": "X-Content-Type-Options", "value": "nosniff" },
|
|
{ "key": "X-Frame-Options", "value": "DENY" }
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
#### 4.3 Configure Environment Variables
|
|
|
|
In Vercel Dashboard → Project → Settings → Environment Variables:
|
|
|
|
```bash
|
|
# Wallet app
|
|
VITE_RPC_URL=https://api.synor.cc
|
|
VITE_WS_URL=wss://ws.synor.cc
|
|
VITE_NETWORK=mainnet
|
|
|
|
# Explorer app
|
|
VITE_API_URL=https://api.synor.cc
|
|
VITE_WS_URL=wss://ws.synor.cc
|
|
```
|
|
|
|
#### 4.4 Add Custom Domains
|
|
|
|
1. Go to Project → Settings → Domains
|
|
2. Add domain and follow DNS instructions
|
|
3. Vercel auto-provisions SSL
|
|
|
|
---
|
|
|
|
### Phase 5: Upstash Redis (Day 3)
|
|
|
|
#### 5.1 Create Database
|
|
|
|
1. Go to upstash.com → Create Database
|
|
2. Name: `synor-ratelimit`
|
|
3. Region: Global (or closest to Oracle)
|
|
4. Type: Regional (free tier)
|
|
|
|
#### 5.2 Get Credentials
|
|
|
|
```bash
|
|
UPSTASH_REDIS_REST_URL=https://[id].upstash.io
|
|
UPSTASH_REDIS_REST_TOKEN=[token]
|
|
```
|
|
|
|
---
|
|
|
|
### Phase 6: DNS Configuration (Day 3)
|
|
|
|
#### Using Cloudflare (Recommended)
|
|
|
|
```zone
|
|
# A Records (Oracle Cloud IPs)
|
|
api.synor.cc A <ORACLE_INSTANCE_1_IP>
|
|
ws.synor.cc A <ORACLE_INSTANCE_1_IP>
|
|
seed1.synor.cc A <ORACLE_INSTANCE_1_IP>
|
|
seed2.synor.cc A <ORACLE_INSTANCE_2_IP>
|
|
seed3.synor.cc A <ORACLE_INSTANCE_3_IP>
|
|
|
|
# CNAME Records (Vercel)
|
|
synor.cc CNAME cname.vercel-dns.com
|
|
www.synor.cc CNAME cname.vercel-dns.com
|
|
wallet.synor.cc CNAME cname.vercel-dns.com
|
|
explorer.synor.cc CNAME cname.vercel-dns.com
|
|
docs.synor.cc CNAME cname.vercel-dns.com
|
|
```
|
|
|
|
---
|
|
|
|
## CI/CD Pipeline
|
|
|
|
### GitHub Actions (Free)
|
|
|
|
```yaml
|
|
# .github/workflows/deploy.yml
|
|
name: Deploy
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
deploy-vercel:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Deploy Website
|
|
uses: amondnet/vercel-action@v25
|
|
with:
|
|
vercel-token: ${{ secrets.VERCEL_TOKEN }}
|
|
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
|
|
vercel-project-id: ${{ secrets.VERCEL_PROJECT_WEBSITE }}
|
|
working-directory: ./apps/website
|
|
vercel-args: '--prod'
|
|
|
|
deploy-oracle:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Build and push to GHCR
|
|
run: |
|
|
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
|
docker build -t ghcr.io/${{ github.repository }}/synord:latest .
|
|
docker push ghcr.io/${{ github.repository }}/synord:latest
|
|
|
|
- name: Deploy to Oracle
|
|
uses: appleboy/ssh-action@v1.0.0
|
|
with:
|
|
host: ${{ secrets.ORACLE_HOST }}
|
|
username: opc
|
|
key: ${{ secrets.ORACLE_SSH_KEY }}
|
|
script: |
|
|
cd ~/synor
|
|
docker-compose pull
|
|
docker-compose up -d
|
|
```
|
|
|
|
---
|
|
|
|
## Deployment Checklist
|
|
|
|
### Day 1: Accounts
|
|
- [ ] Create Vercel account
|
|
- [ ] Create Supabase account
|
|
- [ ] Create Upstash account
|
|
- [ ] Create Oracle Cloud account (requires credit card for verification, not charged)
|
|
- [ ] Register domain (or use free subdomain)
|
|
|
|
### Day 2: Infrastructure
|
|
- [ ] Create 3 Oracle ARM instances
|
|
- [ ] Configure security lists
|
|
- [ ] Run setup scripts
|
|
- [ ] Deploy Docker containers
|
|
- [ ] Create Supabase database
|
|
- [ ] Run migrations
|
|
|
|
### Day 3: Frontend
|
|
- [ ] Deploy apps to Vercel
|
|
- [ ] Configure environment variables
|
|
- [ ] Set up custom domains
|
|
- [ ] Create Upstash Redis
|
|
- [ ] Configure DNS
|
|
|
|
### Day 4: Testing
|
|
- [ ] Verify all endpoints
|
|
- [ ] Test wallet connectivity
|
|
- [ ] Test explorer data
|
|
- [ ] Monitor logs
|
|
|
|
### Day 5: Go-Live
|
|
- [ ] Final DNS verification
|
|
- [ ] Announce launch
|
|
- [ ] Monitor metrics
|
|
|
|
---
|
|
|
|
## Cost Summary
|
|
|
|
| Service | Monthly Cost |
|
|
|---------|-------------|
|
|
| Vercel (4 apps) | $0 |
|
|
| Supabase (500MB) | $0 |
|
|
| Upstash Redis | $0 |
|
|
| Oracle Cloud (3 VMs) | $0 |
|
|
| Domain (.cc) | ~$0.83/mo ($10/yr) |
|
|
| **Total** | **~$1/month** |
|
|
|
|
### When to Upgrade
|
|
|
|
| Threshold | Action | New Cost |
|
|
|-----------|--------|----------|
|
|
| 100K visitors/mo | Vercel Pro | +$20/mo |
|
|
| 500MB database | Supabase Pro | +$25/mo |
|
|
| 10K Redis cmds/day | Upstash Pay-go | +$5/mo |
|
|
| Need more nodes | Hetzner VPS | +$5/mo each |
|
|
|
|
---
|
|
|
|
## Quick Reference
|
|
|
|
### URLs (After Deployment)
|
|
|
|
| Service | URL |
|
|
|---------|-----|
|
|
| Website | https://synor.cc |
|
|
| Wallet | https://wallet.synor.cc |
|
|
| Explorer | https://explorer.synor.cc |
|
|
| API | https://api.synor.cc |
|
|
| WebSocket | wss://ws.synor.cc |
|
|
|
|
### SSH Commands
|
|
|
|
```bash
|
|
# Connect to Oracle instances
|
|
ssh -i ~/.ssh/oracle_key opc@<ORACLE_IP>
|
|
|
|
# View logs
|
|
docker-compose logs -f synord
|
|
docker-compose logs -f explorer-api
|
|
|
|
# Restart services
|
|
docker-compose restart
|
|
|
|
# Update to latest
|
|
docker-compose pull && docker-compose up -d
|
|
```
|
|
|
|
---
|
|
|
|
*Document Version: 3.0 (Zero-Cost Edition)*
|
|
*Last Updated: January 10, 2026*
|