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.
430 lines
23 KiB
Markdown
430 lines
23 KiB
Markdown
# 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
|
|
|
|
```rust
|
|
/// 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```yaml
|
|
# 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)
|
|
|
|
```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
|
|
|
|
1. **Synor Functions**: Serverless compute layer
|
|
2. **Database Integration**: Decentralized database for dynamic apps
|
|
3. **Analytics**: Privacy-preserving usage analytics
|
|
4. **Team Collaboration**: Multi-user access to names
|
|
5. **Staging Environments**: Preview deployments before going live
|