1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>HermesP2P â Safe. Secure. Sovereign.</title> 7 <meta name="description" content="Decentralized, ephemeral peer-to-peer messaging. No servers, no persistence, no compromise."> 8 <link rel="icon" type="image/pdf" href="/static/images/favicon.pdf"> 9 <link href="/static/css/style.css" rel="stylesheet"> 10 <style> 11 * { margin: 0; padding: 0; box-sizing: border-box; } 12 body { 13 font-family: Arial, Helvetica, sans-serif; 14 background: #ffffff; 15 color: #333; 16 line-height: 1.6; 17 } 18 a { color: #000033; text-decoration: none; } 19 a:hover { text-decoration: underline; } 20 21 /* Hero */ 22 .hero { 23 text-align: center; 24 padding: 48px 24px 40px; 25 max-width: 800px; 26 margin: 0 auto; 27 } 28 .hero img { height: 120px; margin-bottom: 16px; } 29 .hero h1 { 30 font-size: 2.4rem; 31 color: #000033; 32 letter-spacing: -0.02em; 33 margin-bottom: 4px; 34 } 35 .hero .tagline { 36 font-size: 1.2rem; 37 font-style: italic; 38 color: #6c757d; 39 margin-bottom: 16px; 40 } 41 .hero .subtitle { 42 font-size: 1.05rem; 43 color: #555; 44 max-width: 600px; 45 margin: 0 auto 28px; 46 } 47 .cta-group { 48 display: flex; 49 gap: 12px; 50 justify-content: center; 51 flex-wrap: wrap; 52 margin-bottom: 8px; 53 } 54 .btn { 55 display: inline-block; 56 padding: 10px 24px; 57 border-radius: 6px; 58 font-weight: 600; 59 font-size: 0.95rem; 60 cursor: pointer; 61 border: none; 62 transition: background 0.15s ease; 63 } 64 .btn-primary { 65 background: #000033; 66 color: #fff !important; 67 } 68 .btn-primary:hover { 69 background: #000055; 70 text-decoration: none; 71 } 72 .btn-secondary { 73 background: #e9ecef; 74 color: #000033 !important; 75 border: 1px solid #ced4da; 76 } 77 .btn-secondary:hover { 78 background: #dee2e6; 79 text-decoration: none; 80 } 81 82 /* Sections */ 83 .section { 84 max-width: 800px; 85 margin: 0 auto; 86 padding: 0 24px 48px; 87 } 88 .section h2 { 89 font-size: 1.5rem; 90 color: #000033; 91 margin-bottom: 16px; 92 padding-top: 32px; 93 border-top: 1px solid #e9ecef; 94 } 95 .section h2:first-child { border-top: none; padding-top: 0; } 96 .section p { 97 color: #555; 98 font-size: 1rem; 99 margin-bottom: 12px; 100 } 101 102 /* Feature grid */ 103 .feature-grid { 104 display: grid; 105 grid-template-columns: repeat(3, 1fr); 106 gap: 20px; 107 margin: 20px 0; 108 } 109 .feature-card { 110 background: #f8f9fa; 111 border: 1px solid #e9ecef; 112 border-radius: 8px; 113 padding: 20px; 114 } 115 .feature-card h3 { 116 font-size: 1rem; 117 color: #000033; 118 margin-bottom: 6px; 119 } 120 .feature-card p { 121 font-size: 0.9rem; 122 color: #6c757d; 123 margin: 0; 124 } 125 126 /* Protocol features */ 127 .protocol-grid { 128 display: grid; 129 grid-template-columns: repeat(2, 1fr); 130 gap: 16px; 131 margin: 20px 0; 132 } 133 .protocol-item { 134 display: flex; 135 gap: 12px; 136 align-items: flex-start; 137 } 138 .protocol-num { 139 flex-shrink: 0; 140 width: 32px; 141 height: 32px; 142 border-radius: 50%; 143 background: #000033; 144 color: #fff; 145 display: flex; 146 align-items: center; 147 justify-content: center; 148 font-weight: 700; 149 font-size: 0.85rem; 150 } 151 .protocol-item h4 { 152 font-size: 0.95rem; 153 color: #000033; 154 margin-bottom: 2px; 155 } 156 .protocol-item p { 157 font-size: 0.9rem; 158 color: #6c757d; 159 margin: 0; 160 } 161 162 /* Auth box */ 163 .auth-box { 164 background: #f8f9fa; 165 border: 1px solid #e9ecef; 166 border-radius: 8px; 167 padding: 24px; 168 margin: 20px 0; 169 font-family: 'SF Mono', 'Fira Code', Menlo, Consolas, monospace; 170 font-size: 0.85rem; 171 line-height: 1.8; 172 color: #6c757d; 173 } 174 .auth-box .label { color: #000033; font-weight: 600; } 175 .auth-box .arrow { color: #000033; } 176 177 /* Upload status */ 178 .alert { padding: 12px 16px; border-radius: 6px; margin-top: 12px; font-size: 0.95rem; } 179 .alert-info { background: #e7f3ff;
179 color: #0c5460; } 180 .alert-success { background: #d4edda; color: #155724; } 181 .alert-danger { background: #f8d7da; color: #721c24; } 182 .d-none { display: none; } 183 184 /* Book */ 185 .book-box { 186 background: #f8f9fa; 187 border: 1px solid #e9ecef; 188 border-radius: 8px; 189 padding: 28px; 190 text-align: center; 191 margin: 20px 0; 192 } 193 .book-box h3 { 194 font-size: 1.1rem; 195 color: #000033; 196 margin-bottom: 8px; 197 } 198 .book-box p { 199 color: #6c757d; 200 font-size: 0.95rem; 201 max-width: 500px; 202 margin: 0 auto 16px; 203 } 204 205 /* Footer */ 206 footer { 207 text-align: center; 208 padding: 24px; 209 color: #6c757d; 210 font-size: 0.85rem; 211 border-top: 1px solid #e9ecef; 212 max-width: 800px; 213 margin: 0 auto; 214 } 215 216 @media (max-width: 768px) { 217 .hero h1 { font-size: 1.8rem; } 218 .feature-grid { grid-template-columns: 1fr; } 219 .protocol-grid { grid-template-columns: 1fr; } 220 .support-grid { grid-template-columns: 1fr; } 221 } 222 </style> 223</head> 224<body> 225 226<section class="hero"> 227 <img src="/static/images/hermesp2p.png" alt="HermesP2P Logo"> 228 <h1>HermesP2P</h1> 229 <p class="tagline">Safe. Secure. Sovereign.</p> 230 <p class="subtitle"> 231 Decentralized, ephemeral peer-to-peer messaging. 232 No servers store your messages. No accounts to breach. 233 Your config file is your identity — upload it and you're you. 234 </p> 235 <div class="cta-group"> 236 <input type="file" id="configFile" accept=".json" style="display:none"> 237 <button onclick="document.getElementById('configFile').click()" class="btn btn-secondary">Upload Config</button> 238 <a href="/client" class="btn btn-primary">New Identity</a> 239 </div> 240 <div id="uploadStatus" class="alert d-none"></div> 241</section> 242 243<div class="section"> 244 <h2>Three Principles</h2> 245 <div class="feature-grid"> 246 <div class="feature-card"> 247 <h3>Ephemeral</h3> 248 <p>Messages are transient. They exist in memory during transit and are never persisted. No server stores your history. No database to subpoena.</p> 249 </div> 250 <div class="feature-card"> 251 <h3>Decentralized</h3> 252 <p>No central servers, no authority, no single point of failure. Every node contributes to resilience. The network self-organizes.</p> 253 </div> 254 <div class="feature-card"> 255 <h3>Sovereign</h3> 256 <p>Your Ed25519 keypair is your identity. Private keys never leave your device. No passwords, no accounts, no third-party authentication.</p> 257 </div> 258 </div> 259</div> 260 261<div class="section"> 262 <h2>Stateless Authentication</h2> 263 <p> 264 There are no accounts. No passwords. No sessions stored on a server. 265 <strong>Your configuration file IS your identity.</strong> 266 It contains your keypair, your channels, your contacts' public keys, and your peer list. 267 Upload it to any node and you're authenticated. Download it when you leave. The node forgets you existed. 268 </p> 269 <div class="auth-box"> 270 <div><span class="label">Config File</span></div> 271 <div> <span class="arrow">+--</span> <span class="label">keypair</span>: Ed25519 public/private keys</div> 272 <div> <span class="arrow">+--</span> <span class="label">channels</span>: subscribed public & private topics</div> 273 <div> <span class="arrow">+--</span> <span class="label">friends</span>: contacts' public keys</div> 274 <div> <span class="arrow">+--</span> <span class="label">peers</span>: known network nodes</div> 275 <div> </div> 276 <div><span class="arrow">></span> Upload to any node = authenticated</div> 277 <div><span class="arrow">></span> Download when done = node forgets you</div> 278 <div><span class="arrow">></span> No server state. No breach surface.</div> 279 </div> 280</div> 281 282<div class="section"> 283 <h2>Protocol Features</h2> 284 <div class="protocol-grid"> 285 <div class="protocol-item"> 286 <div class="protocol-num">1</div> 287 <div> 288 <h4>Ed25519 Signatures</h4> 289 <p>Every message is signed. Every signature is verified at every hop. No impersonation possible.</p> 290 </div> 291 </div> 292 <div class="protocol-item"> 293 <div class="protocol-num">2</div> 294 <div> 295 <h4>Fixed-Size Padding</h4> 296 <p>Messages are padded to fixed sizes (1KB/4KB/16KB/64KB) to prevent traffic analysis. Ob
296servers can't distinguish message types by size.</p> 297 </div> 298 </div> 299 <div class="protocol-item"> 300 <div class="protocol-num">3</div> 301 <div> 302 <h4>TTL Hop Control</h4> 303 <p>Time-to-live counters control message propagation depth. Messages spread through the network efficiently without flooding.</p> 304 </div> 305 </div> 306 <div class="protocol-item"> 307 <div class="protocol-num">4</div> 308 <div> 309 <h4>Peer Discovery</h4> 310 <p>Nodes discover peers and build diverse connection graphs automatically. The network adapts and self-heals.</p> 311 </div> 312 </div> 313 <div class="protocol-item"> 314 <div class="protocol-num">5</div> 315 <div> 316 <h4>Pseudonyms</h4> 317 <p>Channel-specific pseudonyms let you participate in public channels without revealing your global identity.</p> 318 </div> 319 </div> 320 <div class="protocol-item"> 321 <div class="protocol-num">6</div> 322 <div> 323 <h4>Encrypted Channels</h4> 324 <p>Private channels use AES-256 symmetric keys. Public channels are open. Direct messages are encrypted to the recipient's public key.</p> 325 </div> 326 </div> 327 </div> 328</div> 329 330<div class="section"> 331 <h2>Getting Started</h2> 332 <p>Two options to join the network:</p> 333 <p> 334 <strong>Upload your config</strong> — already have a keypair? Upload your configuration file to rejoin your channels and contacts instantly. 335 </p> 336 <p> 337 <strong>Create a new identity</strong> — generate a fresh Ed25519 keypair and subscribe to default channels. Your keys are created locally and never touch a server. 338 </p> 339 <div class="cta-group" style="margin-top: 20px;"> 340 <button onclick="document.getElementById('configFile').click()" class="btn btn-secondary">Upload Config</button> 341 <a href="/client" class="btn btn-primary">New Identity</a> 342 </div> 343 <p style="margin-top: 20px;"> 344 Want to run your own node? Clone the repo and start contributing to network resilience: 345 </p> 346 <div class="auth-box" style="margin-bottom: 0;"> 347 <div>git clone https://github.com/jmcentire/HermesP2P.git</div> 348 <div>cd HermesP2P && npm install && npm run start</div> 349 </div> 350</div> 351 352<div class="section"> 353 <h2>Learn More</h2> 354 <div class="book-box"> 355 <h3>Built on the ideas in <em>Privacy</em></h3> 356 <p> 357 The theoretical foundation behind HermesP2P's design decisions around ephemeral communication, 358 key sovereignty, and decentralized trust. 359 </p> 360 <a href="https://a.co/d/05PSJFBK" class="btn btn-secondary" style="margin-right: 8px;">Read the Book</a> 361 <a href="https://github.com/jmcentire/HermesP2P" class="btn btn-primary">View on GitHub</a> 362 </div> 363 <div style="text-align: center; margin-top: 16px;"> 364 <a href="https://docs.google.com/presentation/d/1xYhkXgVJ6M-QtQr5NqtQpqmQN9M7kQOD8_eBW3R3yZY/edit?usp=sharing" 365 target="_blank" style="color: #6c757d; font-size: 0.9rem;"> 366 View Presentation Deck 367 </a> 368 </div> 369</div> 370 371<div class="section" style="margin-top:24px;"> 372 <h2>Privacy Stack</h2> 373 <p style="margin-bottom:12px;">HermesP2P is one layer of a larger privacy architecture.</p> 374 <div style="display:flex;gap:16px;flex-wrap:wrap;"> 375 <a href="https://signet.tools" class="btn btn-secondary">Signet</a> 376 <a href="https://jmcentire.github.io/agent-safe/" class="btn btn-secondary">Agent-Safe</a> 377 <a href="https://jmcentire.github.io/tessera/" class="btn btn-secondary">Tessera</a> 378 <a href="https://jmcentire.github.io/BlindDB/" class="btn btn-secondary">BlindDB</a> 379 </div> 380</div> 381 382<footer> 383 <p>HermesP2P · <a href="https://github.com/jmcentire/HermesP2P">GitHub</a> · <a href="https://a.co/d/05PSJFBK">Privacy (Book)</a> · <a href="mailto:[email protected]">[email protected]</a></p> 384</footer> 385
386<script> 387 document.getElementById('configFile').addEventListener('change', async function(e) { 388 const file = e.target.files[0]; 389 if (!file) return; 390 391 const statusDiv = document.getElementById('uploadStatus'); 392 statusDiv.className = 'alert alert-info'; 393 statusDiv.textContent = 'Validating configuration...'; 394 statusDiv.classList.remove('d-none'); 395 396 const formData = new FormData(); 397 formData.append('config', file); 398 399 try { 400 const response = await fetch('/validate-config', { 401 method: 'POST', 402 body: formData 403 }); 404 405 const result = await response.json(); 406 407 if (response.ok) { 408 statusDiv.className = 'alert alert-success'; 409 statusDiv.textContent = 'Configuration valid! Redirecting to client...'; 410 sessionStorage.setItem('hp2pConfig', JSON.stringify(result.config)); 411 setTimeout(() => { 412 window.location.href = "/client"; 413 }, 1500); 414 } else { 415 statusDiv.className = 'alert alert-danger'; 416 statusDiv.textContent = 'Error: ' + result.error; 417 } 418 } catch (error) { 419 statusDiv.className = 'alert alert-danger'; 420 statusDiv.textContent = 'Error uploading configuration: ' + error.message; 421 } 422 }); 423</script>
423 424 425<!-- WebMCP (A6): advertise a minimal tool to in-page AI agents via navigator.modelContext. -->
426<script> 427 (function() { 428 if (typeof navigator === 'undefined' || !navigator.modelContext || typeof navigator.modelContext.provideContext !== 'function') { 429 return; 430 } 431 try { 432 navigator.modelContext.provideContext({ 433 tools: [ 434 { 435 name: "get_site_info", 436 description: "Return Hermes P2P metadata", 437 inputSchema: { type: "object", properties: {} }, 438 execute: async () => ({ 439 site: "hp2p.net", 440 description: "Hermes P2P â content-addressable pull-based P2P relay" 441 }) 442 } 443 ] 444 }); 445 } catch (e) { /* no-op: WebMCP is best-effort */ } 446 })(); 447</script>
447 448 449</body> 450</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.