| … | … | @@ -0,0 +1,586 @@ |
| 1 | # Hyperident Gateway Architecture | |
| 2 | ||
| 3 | **Status**: 🚧 Under Development | |
| 4 | **Version**: 0.1.0 | |
| 5 | **Date**: 2026-01-17 | |
| 6 | ||
| 7 | --- | |
| 8 | ||
| 9 | ## Overview | |
| 10 | ||
| 11 | Hyperident Gateway is a UTUN-based P2P gateway that exposes local services globally via Hyperswarm. It uses hyperident for deterministic identity management and virtual IP assignment. | |
| 12 | ||
| 13 | ## Core Concept | |
| 14 | ||
| 15 | ``` | |
| 16 | Internet/Remote Client | |
| 17 | ↓ | |
| 18 | nginx (optional) | |
| 19 | ↓ | |
| 20 | Virtual IP (10.22.0.x) | |
| 21 | ↓ | |
| 22 | UTUN Interface (tun0) | |
| 23 | ↓ | |
| 24 | Gateway Router | |
| 25 | ↓ | |
| 26 | Hyperswarm P2P | |
| 27 | ↓ | |
| 28 | Local Service (git daemon, postgres, fedicore, etc.) | |
| 29 | ``` | |
| 30 | ||
| 31 | --- | |
| 32 | ||
| 33 | ## Component Architecture | |
| 34 | ||
| 35 | ### Layer 1: Identity Management | |
| 36 | ||
| 37 | **GatewayIdentity** | |
| 38 | - Integrates with hyperident (`~/.hyperident/`) | |
| 39 | - Derives deterministic topics from profile names | |
| 40 | - Assigns virtual IPs via hashing | |
| 41 | - Manages service registry | |
| 42 | ||
| 43 | ```javascript | |
| 44 | class GatewayIdentity { | |
| 45 | async deriveServiceTopic(profileName) // → 32-byte Buffer | |
| 46 | async assignIP(profileName) // → '10.22.0.x' | |
| 47 | async registerService(config) // → { name, ip, topic, port } | |
| 48 | } | |
| 49 | ``` | |
| 50 | ||
| 51 | ### Layer 2: Virtual Network Interface | |
| 52 | ||
| 53 | **UTUNManager** | |
| 54 | - Creates and manages TUN device | |
| 55 | - Configures virtual subnet (10.22.0.0/16) | |
| 56 | - Handles packet read/write | |
| 57 | - IP packet parsing and routing | |
| 58 | ||
| 59 | ```javascript | |
| 60 | class UTUNManager { | |
| 61 | async create(config) // Create tun0 | |
| 62 | on('packet', (ipPacket) => {}) // Emit parsed IP packets | |
| 63 | async write(buffer) // Write IP packet to TUN | |
| 64 | async close() // Cleanup | |
| 65 | } | |
| 66 | ``` | |
| 67 | ||
| 68 | ### Layer 3: P2P Routing | |
| 69 | ||
| 70 | **HyperswarmRouter** | |
| 71 | - Joins Hyperswarm topics for services | |
| 72 | - Discovers P2P peers | |
| 73 | - Manages connection pool | |
| 74 | - Routes packets to/from peers | |
| 75 | ||
| 76 | ```javascript | |
| 77 | class HyperswarmRouter { | |
| 78 | async joinService(topic, ip) // Join swarm for service | |
| 79 | on('connection', (conn, info) => {}) // New peer | |
| 80 | async routePacket(dstIP, packet) // Send to P2P peer | |
| 81 | async close() // Cleanup | |
| 82 | } | |
| 83 | ``` | |
| 84 | ||
| 85 | ### Layer 4: Service Registry | |
| 86 | ||
| 87 | **ServiceRegistry** | |
| 88 | - Tracks IP → service mappings | |
| 89 | - Manages connection states | |
| 90 | - Health checking (future) | |
| 91 | - Load balancing (future) | |
| 92 | ||
| 93 | ```javascript | |
| 94 | class ServiceRegistry { | |
| 95 | register(service) // Add service | |
| 96 | lookup(virtualIP) // Find service by IP | |
| 97 | listServices() // Get all services | |
| 98 | updateConnection(ip, conn) // Track connections | |
| 99 | } | |
| 100 | ``` | |
| 101 | ||
| 102 | --- | |
| 103 | ||
| 104 | ## Data Flow | |
| 105 | ||
| 106 | ### Outbound (Client → Service) | |
| 107 | ||
| 108 | ``` | |
| 109 | 1. Client makes request | |
| 110 | ↓ | |
| 111 | 2. nginx proxies to virtual IP (e.g., 10.22.0.10:9418) | |
| 112 | ↓ | |
| 113 | 3. UTUN receives IP packet (dst: 10.22.0.10) | |
| 114 | ↓ | |
| 115 | 4. Gateway parses packet, looks up service | |
| 116 | ↓ | |
| 117 | 5. Finds Hyperswarm connection for that IP | |
| 118 | ↓ | |
| 119 | 6. Writes packet to P2P stream | |
| 120 | ↓ | |
| 121 | 7. Remote peer receives packet | |
| 122 | ↓ | |
| 123 | 8. Service processes request | |
| 124 | ``` | |
| 125 | ||
| 126 | ### Inbound (Service → Client) | |
| 127 | ||
| 128 | ``` | |
| 129 | 1. Service sends response | |
| 130 | ↓ | |
| 131 | 2. P2P stream receives packet | |
| 132 | ↓ | |
| 133 | 3. Gateway writes to UTUN | |
| 134 | ↓ | |
| 135 | 4. Packet routed back through network stack | |
| 136 | ↓ | |
| 137 | 5. nginx receives response | |
| 138 | ↓ | |
| 139 | 6. Client receives data | |
| 140 | ``` | |
| 141 | ||
| 142 | --- | |
| 143 | ||
| 144 | ## Configuration Schema | |
| 145 | ||
| 146 | ### gateway-config.json | |
| 147 | ||
| 148 | ```json | |
| 149 | { | |
| 150 | "version": "1.0.0", | |
| 151 | "gateway": { | |
| 152 | "virtualIP": "10.22.0.1", | |
| 153 | "subnet": "10.22.0.0/16", | |
| 154 | "tunDevice": "tun0" | |
| 155 | }, | |
| 156 | "hyperident": { | |
| 157 | "identityPath": "~/.hyperident/", | |
| 158 | "autoCreateProfiles": false | |
| 159 | }, | |
| 160 | "services": [ | |
| 161 | { | |
| 162 | "name": "git-daemon", | |
| 163 | "profile": "git-daemon", | |
| 164 | "port": 9418, | |
| 165 | "transport": "tcp", | |
| 166 | "enabled": true | |
| 167 | }, | |
| 168 | { | |
| 169 | "name": "fedicore", | |
| 170 | "profile": "fedicore", | |
| 171 | "port": 3000, | |
| 172 | "transport": "tcp", | |
| 173 | "enabled": true | |
| 174 | } | |
| 175 | ], | |
| 176 | "networking": { | |
| 177 | "ipAssignmentStrategy": "hash", | |
| 178 | "collisionHandling": "error", | |
| 179 | "connectionPoolSize": 64 | |
| 180 | }, | |
| 181 | "logging": { | |
| 182 | "level": "info", | |
| 183 | "file": "/var/log/hyperident-gateway.log" | |
| 184 | } | |
| 185 | } | |
| 186 | ``` | |
| 187 | ||
| 188 | --- | |
| 189 | ||
| 190 | ## IP Assignment Strategy | |
| 191 | ||
| 192 | ### Hash-Based (Default) | |
| 193 | ||
| 194 | ```javascript | |
| 195 | function assignIP(topic) { | |
| 196 | const hash = crypto.hash(topic) // SHA256 | |
| 197 | const octet3 = hash[0] // 0-255 | |
| 198 | const octet4 = hash[1] // 0-255 | |
| 199 | return `10.22.${octet3}.${octet4}` | |
| 200 | } | |
| 201 | ``` | |
| 202 | ||
| 203 | **Benefits:** | |
| 204 | - Deterministic (same topic → same IP) | |
| 205 | - No central coordination needed | |
| 206 | - Works across multiple gateways | |
| 207 | ||
| 208 | **Drawbacks:** | |
| 209 | - Collision risk (~1% with 100 services) | |
| 210 | - Need collision detection | |
| 211 | ||
| 212 | ### Static Override | |
| 213 | ||
| 214 | ```json | |
| 215 | { | |
| 216 | "staticMappings": { | |
| 217 | "git-daemon": "10.22.0.10", | |
| 218 | "fedicore": "10.22.0.5" | |
| 219 | } | |
| 220 | } | |
| 221 | ``` | |
| 222 | ||
| 223 | --- | |
| 224 | ||
| 225 | ## Packet Flow Detail | |
| 226 | ||
| 227 | ### IP Packet Structure | |
| 228 | ||
| 229 | ``` | |
| 230 | ┌─────────────────────────────────────────┐ | |
| 231 | │ IP Header │ | |
| 232 | │ - Source IP: client IP │ | |
| 233 | │ - Dest IP: 10.22.0.10 │ | |
| 234 | │ - Protocol: TCP (6) │ | |
| 235 | ├─────────────────────────────────────────┤ | |
| 236 | │ TCP Header │ | |
| 237 | │ - Source Port: random │ | |
| 238 | │ - Dest Port: 9418 │ | |
| 239 | ├─────────────────────────────────────────┤ | |
| 240 | │ Payload (git protocol data) │ | |
| 241 | └─────────────────────────────────────────┘ | |
| 242 | ``` | |
| 243 | ||
| 244 | ### Gateway Processing | |
| 245 | ||
| 246 | ```javascript | |
| 247 | tun.on('data', async (buffer) => { | |
| 248 | // 1. Parse IP packet | |
| 249 | const packet = parseIPPacket(buffer) | |
| 250 | // { src: '192.168.1.100', dst: '10.22.0.10', protocol: 'tcp', ... } | |
| 251 | ||
| 252 | // 2. Lookup service | |
| 253 | const service = registry.lookup(packet.dst) | |
| 254 | if (!service) return // Drop packet | |
| 255 | ||
| 256 | // 3. Get P2P connection | |
| 257 | const conn = service.connections[0] | |
| 258 | if (!conn) { | |
| 259 | console.warn(`No connection for ${service.name}`) | |
| 260 | return | |
| 261 | } | |
| 262 | ||
| 263 | // 4. Forward packet | |
| 264 | await conn.write(buffer) | |
| 265 | }) | |
| 266 | ``` | |
| 267 | ||
| 268 | --- | |
| 269 | ||
| 270 | ## Security Considerations | |
| 271 | ||
| 272 | ### Current Design | |
| 273 | ||
| 274 | 1. **Hyperswarm E2E Encryption** ✅ | |
| 275 | - All P2P traffic encrypted via NoiseSecretStream | |
| 276 | - No plaintext over the wire | |
| 277 | ||
| 278 | 2. **Hyperident Key Management** ✅ | |
| 279 | - BIP39 mnemonic for root identity | |
| 280 | - Deterministic key derivation | |
| 281 | - Optional passphrase protection | |
| 282 | ||
| 283 | 3. **IP Spoofing Prevention** ⚠️ | |
| 284 | - UTUN is local-only (can't spoof virtual IPs from outside) | |
| 285 | - Trust boundary at gateway machine | |
| 286 | ||
| 287 | 4. **Service Authentication** 🚧 | |
| 288 | - Future: Verify peers match expected public keys | |
| 289 | - Future: Service-level ACLs | |
| 290 | ||
| 291 | ### Threat Model | |
| 292 | ||
| 293 | **In Scope:** | |
| 294 | - Unauthorized access to P2P network | |
| 295 | - Man-in-the-middle attacks | |
| 296 | - Service impersonation | |
| 297 | ||
| 298 | **Out of Scope:** | |
| 299 | - Local machine compromise | |
| 300 | - Kernel-level attacks | |
| 301 | - Physical access | |
| 302 | ||
| 303 | --- | |
| 304 | ||
| 305 | ## Performance Characteristics | |
| 306 | ||
| 307 | ### Expected Throughput | |
| 308 | ||
| 309 | - **UTUN overhead**: ~5-10% (packet parsing) | |
| 310 | - **Hyperswarm overhead**: ~10-15% (encryption) | |
| 311 | - **Total overhead**: ~20-25% | |
| 312 | ||
| 313 | **Baseline:** 1 Gbps network → ~750 Mbps through gateway | |
| 314 | ||
| 315 | ### Latency | |
| 316 | ||
| 317 | - **Local**: +1-2ms (UTUN + routing) | |
| 318 | - **P2P (good conditions)**: +10-50ms (DHT discovery + holepunching) | |
| 319 | - **P2P (poor NAT)**: +100-500ms (relay required) | |
| 320 | ||
| 321 | ### Connection Limits | |
| 322 | ||
| 323 | - **Hyperswarm default**: 64 connections per swarm | |
| 324 | - **UTUN**: No inherent limit | |
| 325 | - **Practical limit**: ~100-200 concurrent services | |
| 326 | ||
| 327 | --- | |
| 328 | ||
| 329 | ## Scalability | |
| 330 | ||
| 331 | ### Horizontal Scaling | |
| 332 | ||
| 333 | **Multiple Gateways:** | |
| 334 | ``` | |
| 335 | Gateway 1 (10.22.0.0/17) → Services 0-127 | |
| 336 | Gateway 2 (10.22.128.0/17) → Services 128-255 | |
| 337 | ``` | |
| 338 | ||
| 339 | Each gateway handles subset of IP space. | |
| 340 | ||
| 341 | ### Vertical Scaling | |
| 342 | ||
| 343 | **Single Gateway:** | |
| 344 | - Use /16 subnet (65,534 IPs) | |
| 345 | - Collision probability with 1000 services: ~1.5% | |
| 346 | - Implement collision detection and rehashing | |
| 347 | ||
| 348 | --- | |
| 349 | ||
| 350 | ## Implementation Phases | |
| 351 | ||
| 352 | ### Phase 1: Core Gateway ✅ (Current) | |
| 353 | ||
| 354 | - [x] Architecture design | |
| 355 | - [ ] Project structure | |
| 356 | - [ ] GatewayIdentity class | |
| 357 | - [ ] UTUNManager class | |
| 358 | - [ ] HyperswarmRouter class | |
| 359 | - [ ] ServiceRegistry class | |
| 360 | - [ ] Basic packet routing | |
| 361 | ||
| 362 | ### Phase 2: Service Integration | |
| 363 | ||
| 364 | - [ ] Git daemon integration | |
| 365 | - [ ] Fedicore integration | |
| 366 | - [ ] Health checking | |
| 367 | - [ ] Connection pooling | |
| 368 | - [ ] Error recovery | |
| 369 | ||
| 370 | ### Phase 3: nginx Integration | |
| 371 | ||
| 372 | - [ ] Auto-generate nginx configs | |
| 373 | - [ ] Dynamic upstream updates | |
| 374 | - [ ] WebSocket support | |
| 375 | - [ ] SSL termination | |
| 376 | ||
| 377 | ### Phase 4: Production Hardening | |
| 378 | ||
| 379 | - [ ] Collision detection | |
| 380 | - [ ] Service ACLs | |
| 381 | - [ ] Rate limiting | |
| 382 | - [ ] Monitoring/metrics | |
| 383 | - [ ] Systemd service | |
| 384 | ||
| 385 | ### Phase 5: Advanced Features | |
| 386 | ||
| 387 | - [ ] Load balancing across replicas | |
| 388 | - [ ] Failover/redundancy | |
| 389 | - [ ] Geographic routing | |
| 390 | - [ ] Custom protocol handlers | |
| 391 | ||
| 392 | --- | |
| 393 | ||
| 394 | ## Testing Strategy | |
| 395 | ||
| 396 | ### Unit Tests | |
| 397 | ||
| 398 | - IP assignment determinism | |
| 399 | - Packet parsing correctness | |
| 400 | - Service registry operations | |
| 401 | - Connection lifecycle | |
| 402 | ||
| 403 | ### Integration Tests | |
| 404 | ||
| 405 | - Full packet flow (client → gateway → service) | |
| 406 | - Service discovery via Hyperswarm | |
| 407 | - Connection recovery after failures | |
| 408 | - Multi-service routing | |
| 409 | ||
| 410 | ### System Tests | |
| 411 | ||
| 412 | - git clone via gateway | |
| 413 | - Fedicore web access via gateway | |
| 414 | - Performance benchmarks | |
| 415 | - Long-running stability | |
| 416 | ||
| 417 | --- | |
| 418 | ||
| 419 | ## Deployment Architecture | |
| 420 | ||
| 421 | ### Development Setup | |
| 422 | ||
| 423 | ``` | |
| 424 | ┌─────────────────────────────────────────┐ | |
| 425 | │ Local Machine │ | |
| 426 | │ ├─ Gateway (sudo required for UTUN) │ | |
| 427 | │ ├─ git daemon (port 9418) │ | |
| 428 | │ └─ fedicore (port 3000) │ | |
| 429 | └─────────────────────────────────────────┘ | |
| 430 | ``` | |
| 431 | ||
| 432 | ### Production Setup | |
| 433 | ||
| 434 | ``` | |
| 435 | ┌─────────────────────────────────────────┐ | |
| 436 | │ Cloud VM (Gateway) │ | |
| 437 | │ ├─ nginx (80/443) │ | |
| 438 | │ ├─ hyperident-gateway (UTUN) │ | |
| 439 | │ └─ Hyperswarm (P2P connections) │ | |
| 440 | └───────────────┬─────────────────────────┘ | |
| 441 | │ Internet | |
| 442 | ↓ | |
| 443 | ┌───────────────┴─────────────────────────┐ | |
| 444 | │ Edge Devices (Services) │ | |
| 445 | │ ├─ Laptop 1: git daemon │ | |
| 446 | │ ├─ Laptop 2: fedicore │ | |
| 447 | │ └─ Server 1: database │ | |
| 448 | └─────────────────────────────────────────┘ | |
| 449 | ``` | |
| 450 | ||
| 451 | --- | |
| 452 | ||
| 453 | ## Dependencies | |
| 454 | ||
| 455 | ```json | |
| 456 | { | |
| 457 | "dependencies": { | |
| 458 | "utun": "latest", | |
| 459 | "hyperswarm": "^4.12.1", | |
| 460 | "hypercore-crypto": "^3.4.0", | |
| 461 | "ip-packet": "^2.2.1", | |
| 462 | "minimist": "^1.2.8" | |
| 463 | }, | |
| 464 | "devDependencies": { | |
| 465 | "brittle": "^3.0.0" | |
| 466 | } | |
| 467 | } | |
| 468 | ``` | |
| 469 | ||
| 470 | **External Dependencies:** | |
| 471 | - hyperident (separate project) | |
| 472 | - Node.js 20+ | |
| 473 | - Linux kernel with TUN support (or macOS) | |
| 474 | ||
| 475 | --- | |
| 476 | ||
| 477 | ## File Structure | |
| 478 | ||
| 479 | ``` | |
| 480 | hyperident-gateway/ | |
| 481 | ├── gateway.js # Main entry point | |
| 482 | ├── lib/ | |
| 483 | │ ├── gateway-identity.js # Hyperident integration | |
| 484 | │ ├── utun-manager.js # TUN device management | |
| 485 | │ ├── hyperswarm-router.js # P2P routing | |
| 486 | │ ├── service-registry.js # Service tracking | |
| 487 | │ └── packet-parser.js # IP packet parsing | |
| 488 | ├── config/ | |
| 489 | │ ├── gateway-config.json # Main configuration | |
| 490 | │ └── services.json # Service definitions | |
| 491 | ├── test/ | |
| 492 | │ ├── unit/ | |
| 493 | │ └── integration/ | |
| 494 | ├── docs/ | |
| 495 | │ ├── ARCHITECTURE.md # This file | |
| 496 | │ └── API.md # API reference | |
| 497 | ├── package.json | |
| 498 | └── README.md | |
| 499 | ``` | |
| 500 | ||
| 501 | --- | |
| 502 | ||
| 503 | ## API Reference (Future) | |
| 504 | ||
| 505 | ### CLI | |
| 506 | ||
| 507 | ```bash | |
| 508 | # Start gateway | |
| 509 | hyperident-gateway start [--config gateway-config.json] | |
| 510 | ||
| 511 | # Register service | |
| 512 | hyperident-gateway register <name> <profile> <port> | |
| 513 | ||
| 514 | # List services | |
| 515 | hyperident-gateway list | |
| 516 | ||
| 517 | # Show service details | |
| 518 | hyperident-gateway info <name> | |
| 519 | ||
| 520 | # Test connectivity | |
| 521 | hyperident-gateway test <name> | |
| 522 | ||
| 523 | # Generate nginx config | |
| 524 | hyperident-gateway nginx-config > /etc/nginx/sites-available/gateway | |
| 525 | ``` | |
| 526 | ||
| 527 | ### Programmatic | |
| 528 | ||
| 529 | ```javascript | |
| 530 | const Gateway = require('hyperident-gateway') | |
| 531 | ||
| 532 | const gateway = new Gateway({ | |
| 533 | virtualIP: '10.22.0.1', | |
| 534 | subnet: '10.22.0.0/16' | |
| 535 | }) | |
| 536 | ||
| 537 | await gateway.start() | |
| 538 | ||
| 539 | await gateway.registerService({ | |
| 540 | name: 'git-daemon', | |
| 541 | profile: 'git-daemon', | |
| 542 | port: 9418 | |
| 543 | }) | |
| 544 | ||
| 545 | gateway.on('service-connected', (service) => { | |
| 546 | console.log(`${service.name} is online`) | |
| 547 | }) | |
| 548 | ``` | |
| 549 | ||
| 550 | --- | |
| 551 | ||
| 552 | ## Monitoring & Observability | |
| 553 | ||
| 554 | ### Metrics | |
| 555 | ||
| 556 | - Packets routed per second | |
| 557 | - Active connections per service | |
| 558 | - Bytes transferred per service | |
| 559 | - Connection failures | |
| 560 | - Packet drop rate | |
| 561 | ||
| 562 | ### Logging | |
| 563 | ||
| 564 | ``` | |
| 565 | [INFO] Gateway started on 10.22.0.1 | |
| 566 | [INFO] Registered service: git-daemon → 10.22.0.10 | |
| 567 | [INFO] Hyperswarm joined topic: e1e807375329... | |
| 568 | [INFO] Peer connected for git-daemon: 2 connections active | |
| 569 | [WARN] No connection for service: fedicore (IP: 10.22.0.5) | |
| 570 | [ERROR] Failed to route packet: destination unreachable | |
| 571 | ``` | |
| 572 | ||
| 573 | --- | |
| 574 | ||
| 575 | ## Next Steps | |
| 576 | ||
| 577 | 1. Implement core classes | |
| 578 | 2. Test with git daemon | |
| 579 | 3. Add Fedicore support | |
| 580 | 4. nginx integration | |
| 581 | 5. Production deployment | |
| 582 | ||
| 583 | --- | |
| 584 | ||
| 585 | **Status**: Architecture complete, ready for implementation | |
| 586 | **Last Updated**: 2026-01-17 |
| … | … | @@ -2,18 +2,133 @@ |
| 2 | 2 | |
| 3 | 3 | UTUN-based P2P gateway with nginx integration and hyperident identity management. |
| 4 | 4 | |
| 5 | ## Architecture | |
| 5 | ## Overview | |
| 6 | ||
| 7 | Hyperident Gateway exposes local services globally via Hyperswarm P2P networking. It uses deterministic virtual IP assignment based on hyperident profiles, enabling services to be accessed from anywhere as if they were on a local network. | |
| 6 | 8 | |
| 9 | ## Architecture | |
| 7 | 10 | |
| 11 | ``` | |
| 12 | Internet → nginx → Virtual IP (10.22.0.x) → UTUN → Hyperswarm → Edge Device | |
| 13 | ``` | |
| 8 | 14 | |
| 9 | 15 | ## Status |
| 10 | 16 | |
| 11 | 🚧 Under development | |
| 17 | 🚧 **Phase 1: Core Gateway** - In Progress | |
| 18 | ||
| 19 | - [x] Architecture design | |
| 20 | - [x] Project structure | |
| 21 | - [x] GatewayIdentity class | |
| 22 | - [x] ServiceRegistry class | |
| 23 | - [ ] UTUNManager class | |
| 24 | - [ ] HyperswarmRouter class | |
| 25 | - [ ] Integration layer | |
| 26 | - [ ] Testing | |
| 27 | ||
| 28 | ## Quick Start | |
| 29 | ||
| 30 | ### Prerequisites | |
| 31 | ||
| 32 | ```bash | |
| 33 | # Install Node.js 20+ | |
| 34 | # Install hyperident | |
| 35 | cd /home/fleeky/hyper-tests/hyperident | |
| 36 | npm install | |
| 37 | ||
| 38 | # Create hyperident profiles for services | |
| 39 | node index.js init | |
| 40 | node index.js create-profile git-daemon "Git daemon service" | |
| 41 | node index.js create-profile fedicore "Fedicore social network" | |
| 42 | ``` | |
| 43 | ||
| 44 | ### Installation | |
| 45 | ||
| 46 | ```bash | |
| 47 | cd /home/fleeky/projects/hyperident-gateway | |
| 48 | npm install | |
| 49 | ``` | |
| 50 | ||
| 51 | ### Configuration | |
| 52 | ||
| 53 | ```bash | |
| 54 | cp config/gateway-config.example.json config/gateway-config.json | |
| 55 | # Edit config/gateway-config.json as needed | |
| 56 | ``` | |
| 57 | ||
| 58 | ### Running | |
| 59 | ||
| 60 | ```bash | |
| 61 | # Requires sudo for UTUN device creation | |
| 62 | sudo npm start | |
| 63 | ``` | |
| 12 | 64 | |
| 13 | 65 | ## Components |
| 14 | 66 | |
| 15 | - Gateway Identity Manager (uses hyperident profiles) | |
| 16 | - UTUN Virtual Network Interface | |
| 17 | - Hyperswarm P2P Discovery | |
| 18 | - nginx Configuration Generator | |
| 67 | ### Gateway Identity Manager | |
| 68 | Uses hyperident profiles for deterministic service identity: | |
| 69 | - Derives Hyperswarm topics from profile names | |
| 70 | - Assigns virtual IPs via hashing | |
| 71 | - Manages service registry | |
| 72 | ||
| 73 | ### UTUN Virtual Network Interface (Coming Soon) | |
| 74 | - Creates virtual network device (tun0) | |
| 75 | - Routes packets to/from P2P connections | |
| 76 | - Supports TCP and UDP protocols | |
| 77 | ||
| 78 | ### Hyperswarm P2P Discovery (Coming Soon) | |
| 79 | - Joins swarm topics for services | |
| 80 | - Discovers and connects to peers | |
| 81 | - Handles connection lifecycle | |
| 82 | ||
| 83 | ### nginx Integration (Future) | |
| 84 | - Auto-generates nginx configurations | |
| 85 | - Dynamic upstream updates | |
| 86 | - SSL termination support | |
| 87 | ||
| 88 | ## Example Use Cases | |
| 89 | ||
| 90 | ### Git Daemon over P2P | |
| 91 | ||
| 92 | ```bash | |
| 93 | # On server (this machine) | |
| 94 | git daemon --base-path=/home/fleeky/git-repos | |
| 95 | ||
| 96 | # Register with gateway | |
| 97 | hyperident-gateway register git-daemon git-daemon 9418 | |
| 98 | ||
| 99 | # From anywhere in the world | |
| 100 | git clone hyperswarm://10.22.0.10:9418/fedicore.git | |
| 101 | ``` | |
| 102 | ||
| 103 | ### Fedicore Social Network | |
| 104 | ||
| 105 | ```bash | |
| 106 | # Start Fedicore | |
| 107 | cd /home/fleeky/test1/fedicore | |
| 108 | npm run web | |
| 109 | ||
| 110 | # Register with gateway | |
| 111 | hyperident-gateway register fedicore fedicore 3000 | |
| 112 | ||
| 113 | # Access from anywhere | |
| 114 | curl http://10.22.0.5:3000/api/timeline | |
| 115 | ``` | |
| 116 | ||
| 117 | ## Development | |
| 118 | ||
| 119 | ```bash | |
| 120 | # Run tests | |
| 121 | npm test | |
| 122 | ||
| 123 | # Development mode with debugging | |
| 124 | sudo npm run dev | |
| 125 | ``` | |
| 126 | ||
| 127 | ## Documentation | |
| 128 | ||
| 129 | - [Architecture](./ARCHITECTURE.md) - Detailed system design | |
| 130 | - [API Reference](./docs/API.md) - API documentation (coming soon) | |
| 131 | ||
| 132 | ## License | |
| 19 | 133 | |
| 134 | Apache-2.0 |
| … | … | @@ -0,0 +1,41 @@ |
| 1 | { | |
| 2 | "version": "1.0.0", | |
| 3 | "gateway": { | |
| 4 | "virtualIP": "10.22.0.1", | |
| 5 | "subnet": "10.22.0.0/16", | |
| 6 | "tunDevice": "tun0" | |
| 7 | }, | |
| 8 | "hyperident": { | |
| 9 | "identityPath": "~/.hyperident/", | |
| 10 | "autoCreateProfiles": false | |
| 11 | }, | |
| 12 | "staticMappings": { | |
| 13 | "git-daemon": "10.22.0.10", | |
| 14 | "fedicore": "10.22.0.5" | |
| 15 | }, | |
| 16 | "services": [ | |
| 17 | { | |
| 18 | "name": "git-daemon", | |
| 19 | "profile": "git-daemon", | |
| 20 | "port": 9418, | |
| 21 | "transport": "tcp", | |
| 22 | "enabled": true | |
| 23 | }, | |
| 24 | { | |
| 25 | "name": "fedicore", | |
| 26 | "profile": "fedicore", | |
| 27 | "port": 3000, | |
| 28 | "transport": "tcp", | |
| 29 | "enabled": true | |
| 30 | } | |
| 31 | ], | |
| 32 | "networking": { | |
| 33 | "ipAssignmentStrategy": "hash", | |
| 34 | "collisionHandling": "error", | |
| 35 | "connectionPoolSize": 64 | |
| 36 | }, | |
| 37 | "logging": { | |
| 38 | "level": "info", | |
| 39 | "file": "/var/log/hyperident-gateway.log" | |
| 40 | } | |
| 41 | } |
| … | … | @@ -0,0 +1,230 @@ |
| 1 | /** | |
| 2 | * GatewayIdentity - Integrates with hyperident for service identity management | |
| 3 | * | |
| 4 | * Responsibilities: | |
| 5 | * - Derive deterministic Hyperswarm topics from hyperident profiles | |
| 6 | * - Assign virtual IPs via hashing | |
| 7 | * - Manage service registry | |
| 8 | */ | |
| 9 | ||
| 10 | import crypto from 'hypercore-crypto' | |
| 11 | import { readFile } from 'fs/promises' | |
| 12 | import { homedir } from 'os' | |
| 13 | import path from 'path' | |
| 14 | ||
| 15 | export class GatewayIdentity { | |
| 16 | constructor(config = {}) { | |
| 17 | this.hyperidentPath = config.hyperidentPath || path.join(homedir(), '.hyperident') | |
| 18 | this.subnet = config.subnet || '10.22.0.0/16' | |
| 19 | this.ipAssignmentStrategy = config.ipAssignmentStrategy || 'hash' | |
| 20 | this.staticMappings = config.staticMappings || {} | |
| 21 | ||
| 22 | // Parse subnet | |
| 23 | const [network, cidr] = this.subnet.split('/') | |
| 24 | this.networkPrefix = network.split('.').slice(0, 2).join('.') // e.g., '10.22' | |
| 25 | this.cidr = parseInt(cidr) | |
| 26 | ||
| 27 | // Service registry: virtualIP → service info | |
| 28 | this.services = new Map() | |
| 29 | ||
| 30 | // Topic cache: profileName → topic | |
| 31 | this.topicCache = new Map() | |
| 32 | } | |
| 33 | ||
| 34 | /** | |
| 35 | * Load hyperident identity (similar to IdentityManager.loadIdentity) | |
| 36 | * For now, we'll use a simplified version that just reads profiles | |
| 37 | */ | |
| 38 | async loadProfiles() { | |
| 39 | try { | |
| 40 | const profilesPath = path.join(this.hyperidentPath, 'profiles.json') | |
| 41 | const data = await readFile(profilesPath, 'utf8') | |
| 42 | const profilesData = JSON.parse(data) | |
| 43 | return profilesData.profiles || {} | |
| 44 | } catch (error) { | |
| 45 | if (error.code === 'ENOENT') { | |
| 46 | throw new Error(`Hyperident not initialized. Run: cd ${this.hyperidentPath} && hyperident init`) | |
| 47 | } | |
| 48 | throw new Error(`Failed to load hyperident profiles: ${error.message}`) | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | /** | |
| 53 | * Derive Hyperswarm topic from hyperident profile | |
| 54 | * This mimics what hyperident's IdentityManager.deriveTopicFromProfile() does | |
| 55 | * | |
| 56 | * For now, we'll use a simplified approach: | |
| 57 | * - Hash the profile name with a consistent algorithm | |
| 58 | * - Return 32-byte topic | |
| 59 | * | |
| 60 | * TODO: Integrate directly with hyperident's IdentityManager for true derivation | |
| 61 | */ | |
| 62 | async deriveServiceTopic(profileName) { | |
| 63 | // Check cache first | |
| 64 | if (this.topicCache.has(profileName)) { | |
| 65 | return this.topicCache.get(profileName) | |
| 66 | } | |
| 67 | ||
| 68 | // For MVP: Simple hash-based topic derivation | |
| 69 | // This won't match hyperident's keet-identity-key derivation exactly | |
| 70 | // but will be deterministic and unique | |
| 71 | const profileHash = crypto.hash(Buffer.from(profileName, 'utf8')) | |
| 72 | const topic = profileHash.subarray(0, 32) // 32-byte topic | |
| 73 | ||
| 74 | this.topicCache.set(profileName, topic) | |
| 75 | return topic | |
| 76 | } | |
| 77 | ||
| 78 | /** | |
| 79 | * Assign virtual IP from topic | |
| 80 | * Uses hash-based assignment for determinism | |
| 81 | */ | |
| 82 | assignIP(topic) { | |
| 83 | // Check static mappings first | |
| 84 | // (Will implement after we have service name) | |
| 85 | ||
| 86 | const hash = crypto.hash(topic) | |
| 87 | ||
| 88 | if (this.cidr === 16) { | |
| 89 | // /16 subnet: 10.22.X.X | |
| 90 | const octet3 = hash[0] | |
| 91 | const octet4 = hash[1] | |
| 92 | return `${this.networkPrefix}.${octet3}.${octet4}` | |
| 93 | } else if (this.cidr === 24) { | |
| 94 | // /24 subnet: 10.22.0.X | |
| 95 | const octet4 = hash[0] % 254 + 1 // Avoid .0 and .255 | |
| 96 | return `${this.networkPrefix}.0.${octet4}` | |
| 97 | } else { | |
| 98 | throw new Error(`Unsupported subnet CIDR: /${this.cidr}. Use /16 or /24`) | |
| 99 | } | |
| 100 | } | |
| 101 | ||
| 102 | /** | |
| 103 | * Check for IP collision | |
| 104 | */ | |
| 105 | hasCollision(ip) { | |
| 106 | return this.services.has(ip) | |
| 107 | } | |
| 108 | ||
| 109 | /** | |
| 110 | * Register a service | |
| 111 | * @param {Object} serviceConfig - { name, profile, port, transport } | |
| 112 | * @returns {Object} - { name, ip, topic, port, transport } | |
| 113 | */ | |
| 114 | async registerService(serviceConfig) { | |
| 115 | const { name, profile, port, transport = 'tcp' } = serviceConfig | |
| 116 | ||
| 117 | if (!name || !profile || !port) { | |
| 118 | throw new Error('Service registration requires: name, profile, port') | |
| 119 | } | |
| 120 | ||
| 121 | // Check if hyperident profile exists | |
| 122 | const profiles = await this.loadProfiles() | |
| 123 | if (!profiles[profile]) { | |
| 124 | throw new Error(`Hyperident profile '${profile}' not found. Create it with: hyperident create-profile ${profile}`) | |
| 125 | } | |
| 126 | ||
| 127 | // Derive topic from profile | |
| 128 | const topic = await this.deriveServiceTopic(profile) | |
| 129 | ||
| 130 | // Assign IP (with static override support) | |
| 131 | let ip | |
| 132 | if (this.staticMappings[name]) { | |
| 133 | ip = this.staticMappings[name] | |
| 134 | console.log(`📌 Using static IP mapping for ${name}: ${ip}`) | |
| 135 | } else { | |
| 136 | ip = this.assignIP(topic) | |
| 137 | } | |
| 138 | ||
| 139 | // Check for collision | |
| 140 | if (this.hasCollision(ip)) { | |
| 141 | const existing = this.services.get(ip) | |
| 142 | throw new Error(`IP collision: ${ip} already assigned to service '${existing.name}'. Use static mapping or different profile.`) | |
| 143 | } | |
| 144 | ||
| 145 | // Store service | |
| 146 | const service = { | |
| 147 | name, | |
| 148 | profile, | |
| 149 | ip, | |
| 150 | topic: topic.toString('hex'), | |
| 151 | topicBuffer: topic, | |
| 152 | port, | |
| 153 | transport, | |
| 154 | connections: [], // Will be populated by HyperswarmRouter | |
| 155 | registered: new Date().toISOString() | |
| 156 | } | |
| 157 | ||
| 158 | this.services.set(ip, service) | |
| 159 | ||
| 160 | console.log(`✅ Registered service: ${name}`) | |
| 161 | console.log(` Profile: ${profile}`) | |
| 162 | console.log(` Virtual IP: ${ip}:${port}`) | |
| 163 | console.log(` Topic: ${topic.toString('hex').substring(0, 16)}...`) | |
| 164 | ||
| 165 | return service | |
| 166 | } | |
| 167 | ||
| 168 | /** | |
| 169 | * Lookup service by virtual IP | |
| 170 | */ | |
| 171 | lookup(virtualIP) { | |
| 172 | return this.services.get(virtualIP) | |
| 173 | } | |
| 174 | ||
| 175 | /** | |
| 176 | * List all registered services | |
| 177 | */ | |
| 178 | listServices() { | |
| 179 | return Array.from(this.services.values()) | |
| 180 | } | |
| 181 | ||
| 182 | /** | |
| 183 | * Get service by name | |
| 184 | */ | |
| 185 | getServiceByName(name) { | |
| 186 | for (const service of this.services.values()) { | |
| 187 | if (service.name === name) { | |
| 188 | return service | |
| 189 | } | |
| 190 | } | |
| 191 | return null | |
| 192 | } | |
| 193 | ||
| 194 | /** | |
| 195 | * Update service connection | |
| 196 | */ | |
| 197 | updateConnection(virtualIP, connection) { | |
| 198 | const service = this.services.get(virtualIP) | |
| 199 | if (service) { | |
| 200 | service.connections.push(connection) | |
| 201 | } | |
| 202 | } | |
| 203 | ||
| 204 | /** | |
| 205 | * Remove service connection | |
| 206 | */ | |
| 207 | removeConnection(virtualIP, connection) { | |
| 208 | const service = this.services.get(virtualIP) | |
| 209 | if (service) { | |
| 210 | service.connections = service.connections.filter(c => c !== connection) | |
| 211 | } | |
| 212 | } | |
| 213 | ||
| 214 | /** | |
| 215 | * Export service mappings for debugging/monitoring | |
| 216 | */ | |
| 217 | exportMappings() { | |
| 218 | const mappings = {} | |
| 219 | for (const [ip, service] of this.services.entries()) { | |
| 220 | mappings[ip] = { | |
| 221 | name: service.name, | |
| 222 | profile: service.profile, | |
| 223 | topic: service.topic, | |
| 224 | port: service.port, | |
| 225 | activeConnections: service.connections.length | |
| 226 | } | |
| 227 | } | |
| 228 | return mappings | |
| 229 | } | |
| 230 | } |
| … | … | @@ -0,0 +1,82 @@ |
| 1 | /** | |
| 2 | * ServiceRegistry - Tracks services and their connection states | |
| 3 | * | |
| 4 | * This is a thin wrapper around GatewayIdentity for now, | |
| 5 | * but will be extended with health checking, connection pooling, etc. | |
| 6 | */ | |
| 7 | ||
| 8 | export class ServiceRegistry { | |
| 9 | constructor(gatewayIdentity) { | |
| 10 | this.identity = gatewayIdentity | |
| 11 | } | |
| 12 | ||
| 13 | /** | |
| 14 | * Register a service | |
| 15 | */ | |
| 16 | async register(serviceConfig) { | |
| 17 | return await this.identity.registerService(serviceConfig) | |
| 18 | } | |
| 19 | ||
| 20 | /** | |
| 21 | * Lookup service by virtual IP | |
| 22 | */ | |
| 23 | lookup(virtualIP) { | |
| 24 | return this.identity.lookup(virtualIP) | |
| 25 | } | |
| 26 | ||
| 27 | /** | |
| 28 | * List all services | |
| 29 | */ | |
| 30 | listServices() { | |
| 31 | return this.identity.listServices() | |
| 32 | } | |
| 33 | ||
| 34 | /** | |
| 35 | * Get service by name | |
| 36 | */ | |
| 37 | getByName(name) { | |
| 38 | return this.identity.getServiceByName(name) | |
| 39 | } | |
| 40 | ||
| 41 | /** | |
| 42 | * Update connection for a service | |
| 43 | */ | |
| 44 | updateConnection(virtualIP, connection) { | |
| 45 | this.identity.updateConnection(virtualIP, connection) | |
| 46 | } | |
| 47 | ||
| 48 | /** | |
| 49 | * Remove connection from service | |
| 50 | */ | |
| 51 | removeConnection(virtualIP, connection) { | |
| 52 | this.identity.removeConnection(virtualIP, connection) | |
| 53 | } | |
| 54 | ||
| 55 | /** | |
| 56 | * Check if service has active connections | |
| 57 | */ | |
| 58 | isOnline(virtualIP) { | |
| 59 | const service = this.lookup(virtualIP) | |
| 60 | return service && service.connections.length > 0 | |
| 61 | } | |
| 62 | ||
| 63 | /** | |
| 64 | * Get service stats | |
| 65 | */ | |
| 66 | getStats() { | |
| 67 | const services = this.listServices() | |
| 68 | return { | |
| 69 | total: services.length, | |
| 70 | online: services.filter(s => s.connections.length > 0).length, | |
| 71 | offline: services.filter(s => s.connections.length === 0).length, | |
| 72 | totalConnections: services.reduce((sum, s) => sum + s.connections.length, 0) | |
| 73 | } | |
| 74 | } | |
| 75 | ||
| 76 | /** | |
| 77 | * Export service mappings | |
| 78 | */ | |
| 79 | exportMappings() { | |
| 80 | return this.identity.exportMappings() | |
| 81 | } | |
| 82 | } |
| … | … | @@ -0,0 +1,39 @@ |
| 1 | { | |
| 2 | "name": "hyperident-gateway", | |
| 3 | "version": "0.1.0", | |
| 4 | "description": "UTUN-based P2P gateway with nginx integration and hyperident identity management", | |
| 5 | "main": "gateway.js", | |
| 6 | "type": "module", | |
| 7 | "bin": { | |
| 8 | "hyperident-gateway": "./gateway.js" | |
| 9 | }, | |
| 10 | "scripts": { | |
| 11 | "start": "sudo node gateway.js", | |
| 12 | "dev": "sudo node --inspect gateway.js", | |
| 13 | "test": "brittle test/**/*.test.js" | |
| 14 | }, | |
| 15 | "keywords": [ | |
| 16 | "hyperswarm", | |
| 17 | "p2p", | |
| 18 | "gateway", | |
| 19 | "utun", | |
| 20 | "hyperident", | |
| 21 | "vpn", | |
| 22 | "networking" | |
| 23 | ], | |
| 24 | "author": "", | |
| 25 | "license": "Apache-2.0", | |
| 26 | "dependencies": { | |
| 27 | "utun": "^1.0.0", | |
| 28 | "hyperswarm": "^4.12.1", | |
| 29 | "hypercore-crypto": "^3.4.0", | |
| 30 | "ip-packet": "^2.2.1", | |
| 31 | "minimist": "^1.2.8" | |
| 32 | }, | |
| 33 | "devDependencies": { | |
| 34 | "brittle": "^3.0.0" | |
| 35 | }, | |
| 36 | "engines": { | |
| 37 | "node": ">=20.0.0" | |
| 38 | } | |
| 39 | } |