Add synor-hosting crate for decentralized web hosting with: - Name Registry: On-chain name→CID mapping with ownership, expiry, and custom domain linking - Domain Verification: CNAME/TXT DNS verification for custom domains - Hosting Router: Host-based routing with SPA support, redirects, and custom headers - synor.json: Project configuration for build, routes, and error pages Users can deploy to myapp.synor.cc and optionally link custom domains. 23 tests passing.
23 KiB
23 KiB
Synor Hosting Architecture
Decentralized web hosting powered by Synor Storage Layer.
Overview
Synor Hosting enables users to deploy web applications to permanent, censorship-resistant infrastructure with human-readable URLs. It combines:
- Synor Storage L2: Decentralized content storage
- Name Registry: On-chain name → CID mapping
- Enhanced Gateway: Subdomain-based routing with SSL
- Custom Domains: User-owned domain integration
User Journey
┌─────────────────────────────────────────────────────────────────────────┐
│ Deployment Flow │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. BUILD 2. UPLOAD 3. REGISTER │
│ ───────────── ───────────── ───────────── │
│ $ next build $ synor upload $ synor register │
│ $ synor pack ./out myfirstapp │
│ │ │ │
│ ▼ ▼ │
│ CID: synor1abc... TX confirmed │
│ │
│ 4. ACCESS │
│ ───────────── │
│ https://myfirstapp.synor.network ──────────────────────▶ Your App! │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Architecture Components
1. Name Registry (On-Chain)
The name registry is a smart contract on Synor L1 that maps human-readable names to CIDs.
┌─────────────────────────────────────────────────────────────────┐
│ Name Registry Contract │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Names Table │
│ ┌────────────────┬──────────────────┬───────────────────────┐ │
│ │ Name │ Owner │ CID │ │
│ ├────────────────┼──────────────────┼───────────────────────┤ │
│ │ myfirstapp │ synor1user123... │ synor1abc789xyz... │ │
│ │ coolsite │ synor1user456... │ synor1def012uvw... │ │
│ │ myportfolio │ synor1user789... │ synor1ghi345rst... │ │
│ └────────────────┴──────────────────┴───────────────────────┘ │
│ │
│ Custom Domains Table │
│ ┌────────────────────┬────────────────┬─────────────────────┐ │
│ │ Domain │ Name │ Verified │ │
│ ├────────────────────┼────────────────┼─────────────────────┤ │
│ │ myfirstapp.com │ myfirstapp │ true │ │
│ │ www.coolsite.io │ coolsite │ true │ │
│ └────────────────────┴────────────────┴─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Contract Interface
/// Name Registry Contract
pub trait NameRegistry {
/// Register a new name (must be unique, pay registration fee)
fn register(name: String, cid: ContentId) -> Result<()>;
/// Update CID for a name you own (deploy new version)
fn update(name: String, new_cid: ContentId) -> Result<()>;
/// Transfer ownership to another address
fn transfer(name: String, new_owner: Address) -> Result<()>;
/// Resolve name to CID
fn resolve(name: String) -> Option<ContentId>;
/// Add custom domain (requires verification)
fn add_custom_domain(name: String, domain: String) -> Result<()>;
/// Verify custom domain ownership
fn verify_domain(name: String, domain: String, proof: DomainProof) -> Result<()>;
/// Remove custom domain
fn remove_custom_domain(name: String, domain: String) -> Result<()>;
/// Resolve custom domain to name
fn resolve_domain(domain: String) -> Option<String>;
}
Name Rules
| Rule | Description |
|---|---|
| Length | 3-63 characters |
| Characters | a-z, 0-9, hyphen (not at start/end) |
| Reserved | synor, admin, api, gateway, www, etc. |
| Fee | Registration fee + annual renewal |
| Grace Period | 30 days after expiry before release |
2. Enhanced Gateway
The gateway routes requests based on the Host header instead of just the path.
┌─────────────────────────────────────────────────────────────────────────┐
│ Gateway Routing Flow │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Incoming Request │
│ ───────────────── │
│ GET / HTTP/1.1 │
│ Host: myfirstapp.synor.network │
│ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Extract Host │ │
│ │ header │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Is it │ YES │ Parse subdomain │ │
│ │ *.synor.network?├────▶│ "myfirstapp" │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ NO │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Check custom │ │ Query Name │ │
│ │ domain table │ │ Registry │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Resolve to CID │ │
│ │ synor1abc789xyz... │ │
│ └────────────────────┬────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Fetch from Storage │ │
│ │ Serve with correct MIME type │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
SPA Routing
For Single Page Applications (React, Next.js, Vue, etc.), the gateway handles client-side routing:
Request: GET /dashboard/settings
Host: myfirstapp.synor.network
1. Try exact path: /dashboard/settings → Not found
2. Try with .html: /dashboard/settings.html → Not found
3. Check if SPA (has index.html): Yes
4. Serve index.html with 200 (not redirect)
5. Client-side router handles /dashboard/settings
3. SSL/TLS Management
┌─────────────────────────────────────────────────────────────────────────┐
│ SSL Certificate Strategy │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Synor Domains (*.synor.network) │
│ ───────────────────────────────── │
│ • Wildcard certificate: *.synor.network │
│ • Single cert covers all subdomains │
│ • Auto-renewed via Let's Encrypt │
│ │
│ Custom Domains │
│ ───────────────────────────────── │
│ • On-demand certificate generation │
│ • Let's Encrypt HTTP-01 or DNS-01 challenge │
│ • Stored in distributed cache │
│ • Auto-renewal 30 days before expiry │
│ │
│ Certificate Storage │
│ ───────────────────────────────── │
│ • Encrypted at rest │
│ • Replicated across gateway nodes │
│ • Hot-reload without restart │
│ │
└─────────────────────────────────────────────────────────────────────────┘
4. Custom Domain Verification
Users must prove domain ownership before linking to their Synor name.
┌─────────────────────────────────────────────────────────────────────────┐
│ Domain Verification Flow │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Step 1: Request Verification │
│ ───────────────────────────── │
│ $ synor domain add myfirstapp.com │
│ │
│ Response: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ To verify ownership of myfirstapp.com, add ONE of: │ │
│ │ │ │
│ │ Option A: CNAME Record │ │
│ │ myfirstapp.com CNAME myfirstapp.synor.network │ │
│ │ │ │
│ │ Option B: TXT Record │ │
│ │ _synor.myfirstapp.com TXT "synor-verify=abc123xyz789" │ │
│ │ │ │
│ │ Then run: synor domain verify myfirstapp.com │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Step 2: Add DNS Record (User action) │
│ ───────────────────────────────────── │
│ User adds record at their DNS provider (Cloudflare, Route53, etc.) │
│ │
│ Step 3: Verify │
│ ───────────── │
│ $ synor domain verify myfirstapp.com │
│ │
│ Gateway checks: │
│ 1. DNS lookup for CNAME or TXT record │
│ 2. Verify record matches expected value │
│ 3. Submit verification proof to L1 │
│ 4. Issue SSL certificate │
│ │
│ ✓ Domain verified! myfirstapp.com → myfirstapp.synor.network │
│ │
└─────────────────────────────────────────────────────────────────────────┘
URL Patterns
| Pattern | Example | Description |
|---|---|---|
| CID Path | gateway.synor.cc/synor1abc... |
Direct CID access |
| Subdomain | myfirstapp.synor.network |
Registered name |
| Custom Domain | myfirstapp.com |
User's own domain |
| Path in App | myfirstapp.synor.network/dashboard |
SPA routing |
CLI Commands
# Upload and get CID
synor upload ./dist
# Output: Uploaded! CID: synor1abc789xyz...
# Register a name
synor register myapp
# Output: Name "myapp" registered to synor1abc789xyz...
# Update deployment (new version)
synor upload ./dist --name myapp
# Output: Updated! myapp now points to synor1def012...
# Add custom domain
synor domain add myapp.com
# Output: Add CNAME record, then run: synor domain verify myapp.com
# Verify domain
synor domain verify myapp.com
# Output: ✓ Domain verified!
# Check status
synor status myapp
# Output:
# Name: myapp
# CID: synor1abc789xyz...
# URL: https://myapp.synor.network
# Custom Domains:
# - myapp.com (verified)
# - www.myapp.com (pending)
Pricing Model
| Service | Cost | Notes |
|---|---|---|
| Name Registration | 10 SYNOR | One-time |
| Annual Renewal | 5 SYNOR | Per year |
| Storage | Per deal pricing | See storage tiers |
| Custom Domain | Free | Unlimited per name |
| SSL Certificate | Free | Auto-managed |
| Bandwidth | Free* | Fair use policy |
*Heavy usage may require staking or premium tier.
DNS Configuration
Synor Infrastructure
# Wildcard for all subdomains
*.synor.network A <gateway-cluster-ip>
*.synor.network AAAA <gateway-cluster-ipv6>
# Gateway load balancer
gateway.synor.cc A <gateway-cluster-ip>
g.synor.cc CNAME gateway.synor.cc
User Custom Domain
# Option 1: CNAME (recommended)
myapp.com CNAME myapp.synor.network
# Option 2: A record (if CNAME not supported at apex)
myapp.com A <gateway-cluster-ip>
# For www subdomain
www.myapp.com CNAME myapp.synor.network
Deployment Types
Static Sites
# Supported frameworks
- Next.js (static export)
- React (CRA, Vite)
- Vue.js
- Svelte/SvelteKit
- Astro
- Hugo, Jekyll, 11ty
- Plain HTML/CSS/JS
Configuration File (synor.json)
{
"name": "myapp",
"build": {
"command": "npm run build",
"output": "dist"
},
"routes": {
"/*": "/index.html"
},
"headers": {
"/*": {
"Cache-Control": "public, max-age=31536000, immutable"
},
"/index.html": {
"Cache-Control": "no-cache"
}
},
"redirects": [
{ "from": "/old-page", "to": "/new-page", "status": 301 }
]
}
Security Considerations
Name Squatting Prevention
- Minimum registration fee discourages mass registration
- Trademark dispute resolution process
- Reserved names for common terms
Phishing Protection
- Confusable name detection (l vs 1, O vs 0)
- Warning for names similar to popular sites
- Report mechanism for malicious content
Content Moderation
- Gateway operators can block CIDs (not remove from storage)
- Multiple gateways ensure censorship resistance
- Hash-based blocking list shared among operators
Implementation Phases
Phase 1: Name Registry (2 weeks)
- Name registry smart contract
- CLI commands: register, update, transfer
- Basic gateway subdomain routing
Phase 2: Enhanced Gateway (2 weeks)
- Host-based routing
- SPA support (fallback to index.html)
- Custom headers and redirects
- Caching layer
Phase 3: Custom Domains (2 weeks)
- Domain verification (CNAME/TXT)
- On-demand SSL certificates
- Certificate storage and renewal
Phase 4: Developer Experience (1 week)
- synor.json configuration
- CI/CD integration examples
- GitHub Actions deployment
Comparison
| Feature | Synor Hosting | Vercel | Netlify | IPFS+ENS |
|---|---|---|---|---|
| Decentralized | ✓ | ✗ | ✗ | ✓ |
| Custom Domains | ✓ | ✓ | ✓ | ✓ |
| Auto SSL | ✓ | ✓ | ✓ | ✗ |
| Serverless Functions | Future | ✓ | ✓ | ✗ |
| Censorship Resistant | ✓ | ✗ | ✗ | ✓ |
| Pay Once, Host Forever | ✓ | ✗ | ✗ | ✓ |
| Native Blockchain | ✓ | ✗ | ✗ | ✗ |
Future Enhancements
- Synor Functions: Serverless compute layer
- Database Integration: Decentralized database for dynamic apps
- Analytics: Privacy-preserving usage analytics
- Team Collaboration: Multi-user access to names
- Staging Environments: Preview deployments before going live