This commit is contained in:
Raven Scott
2026-05-29 04:43:08 -04:00
parent 9975ada3f5
commit 16f3b036e5
25 changed files with 1852 additions and 1547 deletions
+2
View File
@@ -2,6 +2,8 @@
# dependencies
/node_modules
/node_modules.root-owned
/.npm-local
/.pnp
.pnp.js
+25 -9
View File
@@ -4,7 +4,7 @@ Modern, professional public-facing website for P2NS (Peer-to-Peer Decentralized
## Features
- **Modern Design**: Built with Next.js 14+, TypeScript, and Tailwind CSS
- **Modern Design**: Built with Next.js 16, TypeScript, and Tailwind CSS 4
- **Responsive**: Mobile-first design that works on all devices
- **Dark Mode**: Automatic dark mode support
- **Interactive Diagrams**: Animated visualizations of P2P concepts
@@ -16,13 +16,13 @@ Modern, professional public-facing website for P2NS (Peer-to-Peer Decentralized
### Prerequisites
- Node.js 18 or higher
- npm or yarn
- Node.js 20 or higher
- npm
### Installation
```bash
# Install dependencies
# Install dependencies (run as your user, not root — Docker-created node_modules may need removal first)
npm install
# Run development server
@@ -72,12 +72,28 @@ No environment variables are required for basic operation. All configuration is
## Technologies Used
- **Next.js 14+**: React framework with App Router
- **TypeScript**: Type-safe development
- **Tailwind CSS**: Utility-first CSS framework
- **Framer Motion**: Animation library
- **Next.js 16**: React framework with App Router
- **TypeScript 5**: Type-safe development
- **Tailwind CSS 4**: Utility-first CSS framework
- **Framer Motion 12**: Animation library
- **Lucide React**: Icon library
- **Shiki**: Syntax highlighting
## Dependencies and security
Keep dependencies current:
```bash
npm audit # should report 0 vulnerabilities after overrides in package.json
npm outdated # review available upgrades
npm install # apply package-lock.json
```
If `npm install` fails with permission errors, `node_modules` may have been created by Docker as root. Remove it and reinstall as your user:
```bash
rm -rf node_modules package-lock.json # or: sudo rm -rf node_modules
npm install
```
## Contributing
+4 -2
View File
@@ -119,7 +119,7 @@ export default function AboutPage() {
Privacy & Security
</h3>
<p className="text-gray-600 dark:text-gray-300">
The stack uses ibsodium for end-to-end encryption and also employs noise encryption for further security. Web connections are safeguarded via TLS and custom CA.
Peer connections use Noise XX handshakes and libsodium secretstream (XChaCha20-Poly1305) over UDP via the Holepunch stack. Web connections are safeguarded via TLS and a custom CA.
</p>
</div>
<div className="bg-white dark:bg-gray-900 p-6 rounded-lg border border-gray-200 dark:border-gray-700">
@@ -145,7 +145,9 @@ export default function AboutPage() {
</h2>
<p className="text-center text-gray-600 dark:text-gray-300 mb-12 max-w-2xl mx-auto">
P2NS is built on a modular architecture using proven P2P
technologies
technologies. Built-in plugins include peer.directory, peer.paste,
file.drop, and the p2ns.admin interfaceextensible via the plugin SDK
and protomux RPC.
</p>
<ArchitectureDiagram />
</div>
+3 -3
View File
@@ -3,12 +3,12 @@ import { Github, ExternalLink, Code, Heart, MessageCircle } from 'lucide-react';
export const metadata: Metadata = {
title: 'Community - P2NS',
description: 'Contribute to P2NS, get support, join the Discord community, and learn about the roadmap.',
description: 'Contribute to P2NS, get support, and join the Discord community.',
keywords: ['P2NS community', 'contribute', 'open source', 'Discord', 'support', 'contributing'],
authors: [{ name: 'Raven Scott' }],
openGraph: {
title: 'Community - P2NS',
description: 'Contribute to P2NS, get support, and learn about the roadmap.',
description: 'Contribute to P2NS, get support, and join the Discord community.',
url: 'https://p2ns.space/community',
siteName: 'P2NS',
type: 'website',
@@ -16,7 +16,7 @@ export const metadata: Metadata = {
twitter: {
card: 'summary_large_image',
title: 'Community - P2NS',
description: 'Contribute to P2NS, get support, and learn about the roadmap.',
description: 'Contribute to P2NS, get support, and join the Discord community.',
},
alternates: {
canonical: 'https://p2ns.space/community',
+40 -14
View File
@@ -80,10 +80,12 @@ export default function AdminPanelPage() {
<li><strong className="text-gray-900 dark:text-white">Peers</strong> - Monitor connected peers</li>
<li><strong className="text-gray-900 dark:text-white">Certificates</strong> - Manage TLS certificates</li>
<li><strong className="text-gray-900 dark:text-white">Interfaces</strong> - View virtual network interfaces</li>
<li><strong className="text-gray-900 dark:text-white">Logs</strong> - Real-time system logs</li>
<li><strong className="text-gray-900 dark:text-white">Stats</strong> - System statistics and metrics</li>
<li><strong className="text-gray-900 dark:text-white">Backups</strong> - Create, restore, and manage system backups</li>
<li><strong className="text-gray-900 dark:text-white">Diagnostics</strong> - DNS lookup, ping, traceroute, connection tests, invite RPC diagnostics</li>
<li><strong className="text-gray-900 dark:text-white">Stats</strong> - Live metrics, Core RPC invite diagnostics, Plugin RPC protocol stats</li>
<li><strong className="text-gray-900 dark:text-white">Logs</strong> - Tail split log files by channel (core, proxy, DNS, plugins, Holesail)</li>
<li><strong className="text-gray-900 dark:text-white">Settings</strong> - Configure environment variables and subnets</li>
<li><strong className="text-gray-900 dark:text-white">Plugins</strong> - Start/stop plugins, view logs, run actions with validated parameters</li>
</ul>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">Detailed Feature Descriptions</h2>
@@ -290,21 +292,33 @@ export default function AdminPanelPage() {
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4 mt-8">Logs</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
The Logs tab provides a real-time terminal view of system logs:
The Logs tab tails split log files written under <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">LOG_DIR</code> (default <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">./logs</code>):
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-6">
<li><strong className="text-gray-900 dark:text-white">Real-time streaming:</strong> See logs as they're generated</li>
<li><strong className="text-gray-900 dark:text-white">Log levels:</strong> View DEBUG, INFO, WARN, and ERROR messages</li>
<li><strong className="text-gray-900 dark:text-white">Terminal interface:</strong> Full-featured terminal with scrollback and search</li>
<li><strong className="text-gray-900 dark:text-white">Auto-scroll:</strong> Automatically scroll to show latest logs</li>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-4">
<li><strong className="text-gray-900 dark:text-white">Channels:</strong> core, proxy, httpProxy, dns, plugins, holesail</li>
<li><strong className="text-gray-900 dark:text-white">Channel filter:</strong> Select which subsystem to tail</li>
<li><strong className="text-gray-900 dark:text-white">WebSocket tail:</strong> <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">subscribe-log</code> / <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">file-log</code> / <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">log-snapshot</code></li>
<li><strong className="text-gray-900 dark:text-white">REST:</strong> <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/logs</code> and <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/logs/:channel</code></li>
<li><strong className="text-gray-900 dark:text-white">Plugin logs:</strong> Per-plugin output also available via plugin-log WebSocket messages</li>
</ul>
<p className="text-gray-600 dark:text-gray-300 mb-6">
This is invaluable for debugging issues and monitoring system behavior in real-time.
Use the core channel when debugging invite RPC or consensus issues.
</p>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4 mt-8">Stats</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
The Stats tab provides comprehensive statistics and metrics about your P2NS node:
The Stats tab loads an initial snapshot via <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/stats</code>, then receives live updates over WebSocket with <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">subscribe-stats</code> and <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">stats-snapshot</code> payloads:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-4">
<li><strong className="text-gray-900 dark:text-white">Core RPC:</strong> Invite flow diagnostics (<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">invite.request</code>, <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">invite.deliver</code>, <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">core.status</code>)</li>
<li><strong className="text-gray-900 dark:text-white">Plugin RPC:</strong> Protocol stats for plugin peer communication</li>
<li><strong className="text-gray-900 dark:text-white">Hypercore stats:</strong> Optional holepunch stats when <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">ENABLE_HYPERCORE_STATS=true</code></li>
</ul>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Detailed invite diagnostics are also available via <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/diagnostics/invites</code> and the Diagnostics tab.
</p>
<p className="text-gray-600 dark:text-gray-300 mb-4">
The Stats tab also provides comprehensive statistics and metrics about your P2NS node:
</p>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">System Overview</h4>
@@ -360,13 +374,23 @@ export default function AdminPanelPage() {
<li>Net change over time</li>
</ul>
<p className="text-gray-600 dark:text-gray-300 mb-6">
All statistics include interactive charts and can be exported for analysis. You can adjust the refresh interval
and time range to focus on specific periods.
Charts update live via WebSocket. You can adjust the time range to focus on specific periods.
</p>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4 mt-8">Plugins</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
The Plugins tab manages built-in and custom plugin sites:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-6">
<li><strong className="text-gray-900 dark:text-white">Start/stop:</strong> Enable or disable individual plugins</li>
<li><strong className="text-gray-900 dark:text-white">Plugin logs:</strong> View per-plugin application logs</li>
<li><strong className="text-gray-900 dark:text-white">Actions with params:</strong> Run plugin admin actions; parameters are validated before execution via a modal</li>
<li><strong className="text-gray-900 dark:text-white">Master note:</strong> file.drop is disabled on master nodes (<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">disable_on_master</code>)</li>
</ul>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4 mt-8">Network Diagnostics</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
The Network Diagnostics section provides powerful network troubleshooting tools:
The Diagnostics tab provides network troubleshooting tools and Core invite RPC diagnostics (aligned with the Stats Core section):
</p>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">DNS Lookup</h4>
@@ -491,6 +515,7 @@ export default function AdminPanelPage() {
<li><strong className="text-gray-900 dark:text-white">Save changes:</strong> Persist modifications to <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">.env</code> file</li>
<li><strong className="text-gray-900 dark:text-white">Validation:</strong> Settings are validated before saving to prevent invalid configurations</li>
<li><strong className="text-gray-900 dark:text-white">Category filtering:</strong> Filter settings by category for easier navigation</li>
<li><strong className="text-gray-900 dark:text-white">Storage clean:</strong> Reset Corestore and plugin HyperDB storage (same scope as <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">--clean</code> on the CLI)</li>
</ul>
<p className="text-gray-600 dark:text-gray-300 mb-6">
All settings changes are saved to your <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">.env</code> file. Live-reloadable settings take effect immediately, while restart-required settings will be applied on the next node restart.
@@ -502,7 +527,8 @@ export default function AdminPanelPage() {
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-4">
<li><strong className="text-gray-900 dark:text-white">Automatic refresh:</strong> Data updates automatically when changes occur</li>
<li><strong className="text-gray-900 dark:text-white">Live logs:</strong> System logs stream in real-time</li>
<li><strong className="text-gray-900 dark:text-white">Live logs:</strong> Per-channel log tail via WebSocket</li>
<li><strong className="text-gray-900 dark:text-white">Live stats:</strong> Metrics and Core/Plugin RPC sections update via subscribe-stats</li>
<li><strong className="text-gray-900 dark:text-white">Peer updates:</strong> See peers connect and disconnect immediately</li>
<li><strong className="text-gray-900 dark:text-white">Domain changes:</strong> New domain registrations appear instantly</li>
<li><strong className="text-gray-900 dark:text-white">Status indicators:</strong> Connection status shown in the top-right corner</li>
+19 -1
View File
@@ -66,10 +66,24 @@ export default function APIPage() {
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">WebSocket Endpoint</h2>
<CodeBlock code="wss://p2ns.admin/ws" />
<p className="text-gray-600 dark:text-gray-300 mt-4">
The WebSocket connection provides real-time updates including logs, database changes, peer updates,
The WebSocket connection provides real-time updates including split logs, live stats, database changes, peer updates,
and system events.
</p>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">WebSocket Messages</h2>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Common client messages (send as JSON with a <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">type</code> field):
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-4">
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">subscribe-stats</code> / <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">unsubscribe-stats</code> / <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">request-stats-snapshot</code></li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">subscribe-log</code> e.g. <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">{`{ "type": "subscribe-log", "channel": "dns", "lines": 500 }`}</code></li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">unsubscribe-log</code></li>
</ul>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Server messages include <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">stats-snapshot</code>, <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">log-snapshot</code>, <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">file-log</code>, and <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">plugin-log</code>.
Log channels: core, proxy, httpProxy, dns, plugins, holesail.
</p>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">Key Endpoints</h2>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300">
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/resolved-domains</code> - List all resolved domains with ownership and services</li>
@@ -77,6 +91,10 @@ export default function APIPage() {
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">POST /api/remove-domain</code> - Remove a domain</li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/peers</code> - List connected peers</li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/status</code> - Get system status</li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/stats</code> - One-shot metrics snapshot (Stats tab initial load; live updates use WebSocket)</li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/logs</code> - List split log channels</li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/logs/:channel</code> - Tail a log channel</li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/diagnostics/invites</code> - Core invite RPC diagnostics</li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/local-dns</code> - Get local DNS records</li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/holesail-servers</code> - List Holesail servers</li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/holesail-clients</code> - List Holesail clients</li>
+26 -3
View File
@@ -52,13 +52,36 @@ export default function ArchitecturePage() {
<li>Stores claims as <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">claim:domain:claimant</code></li>
<li>Stores votes as <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">vote:domain:claimant:voter</code></li>
<li>Master nodes initialize the core and issue invites</li>
<li>Joiners request invites to become writers</li>
<li>Joiners request invites to become writers via Core RPC (not a legacy invite channel)</li>
</ul>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4 mt-8">Core RPC Control Plane</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Core invite and consensus traffic uses protomux RPC on the p2ns swarm connection:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300">
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">invite.request</code>, <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">invite.deliver</code>, <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">invite.ack</code></li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">core.status</code> for remote peer diagnostics</li>
<li>Consensus RPC methods for domain operations across peers</li>
</ul>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4 mt-8">Plugin System</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Internal domains are discovered from <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">plugin-sites/&#123;domain&#125;/config.json</code>.
Plugin peer communication uses protomux RPC only (<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">sdk.channels.register</code>).
Plugin HyperDB data can use shared Corestore namespaces (<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">USE_SHARED_CORESTORE_NAMESPACES</code>).
</p>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4 mt-8">Split Logging</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Subsystem logs are written to separate files under <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">LOG_DIR</code>:
core, proxy, http-proxy, dns, plugins, and holesail. The admin Logs tab tails these via WebSocket.
</p>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4 mt-8">Hyperswarm</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Hyperswarm handles peer discovery using a fixed topic derived from <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">sha256('p2ns-dns')</code>.
All peers interested in P2NS join this topic and can discover each other through the DHT.
Hyperswarm handles peer discovery on HyperDHT using a 32-byte topic derived from <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">sha256(TOPIC_SEED)</code> (default seed <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">p2ns-dns</code>).
All peers interested in P2NS join this topic and discover each other without a central directory.
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300">
<li>Automatic peer discovery</li>
+161 -28
View File
@@ -88,9 +88,14 @@ export default function ConfigurationPage() {
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">SUBSCRIPTIONS_FILE</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">./cache/subscriptions.json</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">cache/subscriptions.json</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Path to service subscriptions storage file</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">PEER_HISTORY_FILE</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">cache/peer_history.json</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Path to peer connection history file</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">CERTS_DIR</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">./certs</code></td>
@@ -154,7 +159,52 @@ export default function ConfigurationPage() {
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">PUBLIC_DNS_SERVER</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">1.1.1.1</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Public DNS server for fallback resolution</td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Public DNS server(s) for fallback resolution (comma-separated for failover)</td>
</tr>
</tbody>
</table>
</div>
<div>
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-2">Hyperswarm & P2P</h3>
<table className="w-full border-collapse bg-white dark:bg-gray-900">
<thead>
<tr className="bg-gray-100 dark:bg-gray-800">
<th className="px-4 py-2 text-left border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-white font-semibold">Variable</th>
<th className="px-4 py-2 text-left border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-white font-semibold">Default</th>
<th className="px-4 py-2 text-left border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-white font-semibold">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">MAX_PEERS</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">24</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Maximum number of Hyperswarm peer connections</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">SWARM_CONNECTION_TIMEOUT</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">30000</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Swarm connection timeout in milliseconds</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">SWARM_KEEPALIVE_INTERVAL</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">60000</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Interval in ms to rejoin the swarm topic (0 to disable)</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">CHANNEL_KEEPALIVE_INTERVAL</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">30000</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Plugin channel heartbeat interval in ms (0 to disable)</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">CHANNEL_KEEPALIVE_TIMEOUT</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">10000</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Time in ms before a stale plugin channel is recreated</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">TOPIC_SEED</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">p2ns-dns</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Seed for generating Hyperswarm discovery topic</td>
</tr>
</tbody>
</table>
@@ -221,7 +271,7 @@ export default function ConfigurationPage() {
</div>
<div>
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-2">Security & Access</h3>
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-2">Feature Flags</h3>
<table className="w-full border-collapse bg-white dark:bg-gray-900">
<thead>
<tr className="bg-gray-100 dark:bg-gray-800">
@@ -233,9 +283,19 @@ export default function ConfigurationPage() {
<tbody>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">ALLOW_ANY_WRITER_INVITES</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">false</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">true</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Allow non-master nodes to issue invites</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">USE_SHARED_CORESTORE_NAMESPACES</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">true</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Use shared main Corestore namespaces for plugin DBs and drives (set false only for debugging)</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">ENABLE_HYPERCORE_STATS</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">true</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Collect hypercore/corestore stats for /api/stats (set false to disable)</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">DISABLE_DNS_SERVER</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">false</code></td>
@@ -273,11 +333,75 @@ export default function ConfigurationPage() {
<tbody>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">LOG_LEVEL</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">0</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">1</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Logging verbosity level (0=DEBUG, 1=INFO, 2=WARN, 3=ERROR)</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">LOG_DIR</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">./logs</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Directory for split log files (core, proxy, http-proxy, dns, plugins, holesail)</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">LOG_BUFFER_LINES</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">2000</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">In-memory tail lines per channel for admin /api/logs and WebSocket tail</td>
</tr>
</tbody>
</table>
<p className="text-gray-600 dark:text-gray-300 mt-2 text-sm">
Protomux RPC is always enabled for core invite/consensus and plugin peer communication.
</p>
</div>
<div>
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-2">Master Node</h3>
<p className="text-gray-600 dark:text-gray-300 mb-2 text-sm">
Master mode: use <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">--master</code> on the command line or set <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">P2NS_MASTER=true</code> (or <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">MASTER=true</code>) for Docker/PM2.
</p>
<table className="w-full border-collapse bg-white dark:bg-gray-900">
<thead>
<tr className="bg-gray-100 dark:bg-gray-800">
<th className="px-4 py-2 text-left border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-white font-semibold">Variable</th>
<th className="px-4 py-2 text-left border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-white font-semibold">Default</th>
<th className="px-4 py-2 text-left border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-white font-semibold">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">P2NS_MASTER</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200">unset</td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Set to true to run as master without --master flag</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">MASTER_RECONNECT_INTERVAL</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">5</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Base reconnection interval in seconds for disconnected peers</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">MASTER_MAX_RECONNECT_ATTEMPTS</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">10</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Maximum reconnection attempts per peer</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">MASTER_PROACTIVE_INVITE_DELAY</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">2000</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Delay in ms before sending proactive invite to new peers</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">CREATE_INVITE_TIMEOUT_MS</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">20000</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Timeout for Autopass createInvite including member.flushed()</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">DNS_PASS_OP_GAP_MS</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">50</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Gap between serialized Autopass operations in ms</td>
</tr>
</tbody>
</table>
<p className="text-gray-600 dark:text-gray-300 mt-2 text-sm">
Autopass replication uses its own swarm; <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">MASTER_REPLICATE_DELAY</code> is unused for dnsPass replication.
</p>
</div>
<div>
@@ -341,15 +465,10 @@ export default function ConfigurationPage() {
</tr>
</thead>
<tbody>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">TOPIC_SEED</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">p2ns-dns</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Seed for generating Hyperswarm discovery topic</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">INTERNAL_DOMAINS</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">peer.directory,peer.dir,p2ns.admin</code></td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Comma-separated list of internal domains</td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200">auto</td>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-600 dark:text-gray-300">Internal plugin domains are discovered from plugin-sites/&#123;domain&#125;/config.json. p2ns.admin is always internal. Optional comma-separated override.</td>
</tr>
<tr>
<td className="px-4 py-2 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-gray-200"><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">CONSENSUS_QUORUM_THRESHOLD</code></td>
@@ -382,14 +501,22 @@ export default function ConfigurationPage() {
</div>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">Example Configuration</h2>
<p className="text-gray-600 dark:text-gray-300 mb-4">
See <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">default.env</code> in the repository for the full list. Abbreviated example:
</p>
<CodeBlock code={`# Logging
LOG_LEVEL=1
# LOG_DIR=./logs
# LOG_BUFFER_LINES=2000
# Storage & Files
STORAGE_DIR=./my-storage
DOMAINS_FILE=cache/domains.json
LOCAL_DNS_FILE=cache/local_dns.json
SUBSCRIPTIONS_FILE=./cache/subscriptions.json
SUBSCRIPTIONS_FILE=cache/subscriptions.json
PEER_HISTORY_FILE=cache/peer_history.json
CERTS_DIR=./certs
BACKUP_DIR=./backups
BACKUP_RETENTION=25
BACKUP_INTERVAL=720
@@ -400,7 +527,14 @@ HTTP_PORT=80
INTERNAL_PORT=8080
PUBLIC_DNS_SERVER=1.1.1.1
# Network Configuration (Virtual Interfaces)
# Hyperswarm
MAX_PEERS=24
SWARM_CONNECTION_TIMEOUT=30000
SWARM_KEEPALIVE_INTERVAL=60000
CHANNEL_KEEPALIVE_INTERVAL=30000
CHANNEL_KEEPALIVE_TIMEOUT=10000
# Virtual interfaces
SUBNET_BASE=192.168.3.
INITIAL_IP_INDEX=2
SUBNETS=[{"base":"192.168.3.0","cidr":24,"startIndex":2,"name":"Primary Subnet"}]
@@ -409,28 +543,27 @@ SUBNETS=[{"base":"192.168.3.0","cidr":24,"startIndex":2,"name":"Primary Subnet"}
HOLESAIL_TIMEOUT=5
FULL_PERSISTENCE=false
# Security & Access
ALLOW_ANY_WRITER_INVITES=false
# Feature flags
DISABLE_DNS_SERVER=false
DISABLE_PROXY_SERVER=false
DISABLE_VIRTUAL_INTERFACES=false
DISABLE_AUTO_SUBSCRIPTION=false
ALLOW_ANY_WRITER_INVITES=true
USE_SHARED_CORESTORE_NAMESPACES=true
ENABLE_HYPERCORE_STATS=true
# Performance
RATE_LIMIT_MAX_REQUESTS=100
RATE_LIMIT_WINDOW_MS=60
DNS_POOL_SIZE=5
METRICS_RETENTION_MS=60
METRICS_SAMPLING_RATE=1.0
# Advanced
TOPIC_SEED=p2ns-dns
INTERNAL_DOMAINS=peer.directory,peer.dir,p2ns.admin
# Consensus
CONSENSUS_QUORUM_THRESHOLD=0.5
CONSENSUS_MIN_VOTES=2
CONSENSUS_TIE_BREAKER=timestamp
CONSENSUS_VOTE_VALIDATION=true
CONSENSUS_IMMEDIATE_UPDATE=true`} />
CONSENSUS_IMMEDIATE_UPDATE=true
# P2P
TOPIC_SEED=p2ns-dns
# Master (uncomment for Docker/PM2)
# P2NS_MASTER=true
MASTER_PROACTIVE_INVITE_DELAY=2000`} />
</div>
</section>
</div>
+14 -2
View File
@@ -70,6 +70,10 @@ cd p2ns`} />
The master node initializes the network and loads domains from <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">domains.json</code>:
</p>
<CodeBlock code="sudo node p2ns.js --master" />
<p className="text-gray-600 dark:text-gray-300 mb-4 mt-4">
For Docker or PM2, you can set <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">P2NS_MASTER=true</code> in{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">.env</code> instead of passing <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">--master</code>.
</p>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4 mt-8">Joiner Node</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
@@ -79,14 +83,17 @@ cd p2ns`} />
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4 mt-8">Clean Storage</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
To start fresh, remove the storage directory:
Wipe Corestore and plugin database storage for a fresh start. Required after some upgrades and when invite or plugin DB issues persist:
</p>
<CodeBlock code="sudo node p2ns.js --clean [--master]" />
<p className="text-gray-600 dark:text-gray-300 mb-4 mt-4">
See the <a href="/docs/upgrading" className="text-blue-600 dark:text-blue-400 hover:underline">Upgrading</a> guide for coordinated network upgrades and when to use <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">--clean</code>.
</p>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">Adding Your First Domain</h2>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4">1. Generate a Holesail Hash</h3>
<CodeBlock code="holesail --live 80" />
<CodeBlock code="holesail --live 80 --public" />
<p className="text-gray-600 dark:text-gray-300 mb-4 mt-4">
This will output a connection hash like: <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">hs://s00084bf87dfa89a3048fb081c0e6207eb5a</code>
</p>
@@ -113,7 +120,11 @@ cd p2ns`} />
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300">
<li><strong>Admin Interface:</strong> <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">https://p2ns.admin</code></li>
<li><strong>Peer Directory:</strong> <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">https://peer.directory</code></li>
<li><strong>Peer Paste:</strong> <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">https://peer.paste</code> — temporary E2EE text snippets</li>
</ul>
<p className="text-gray-600 dark:text-gray-300 mt-4">
More built-in plugins: <a href="/docs/plugins" className="text-blue-600 dark:text-blue-400 hover:underline">Built-in Plugins</a>.
</p>
<p className="text-gray-600 dark:text-gray-300 mt-4">
<strong>Note:</strong> You'll need to trust the root CA certificate in your browser. The system will attempt to install it automatically.
</p>
@@ -121,6 +132,7 @@ cd p2ns`} />
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">Next Steps</h2>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300">
<li>Read the <a href="/docs/configuration" className="text-blue-600 dark:text-blue-400 hover:underline">Configuration</a> guide</li>
<li>Review <a href="/docs/upgrading" className="text-blue-600 dark:text-blue-400 hover:underline">Upgrading</a> before updating an existing network</li>
<li>Check out the <a href="/docs/api" className="text-blue-600 dark:text-blue-400 hover:underline">API Reference</a></li>
<li>Learn about <a href="/learn/p2p" className="text-blue-600 dark:text-blue-400 hover:underline">How P2P Works</a></li>
</ul>
+99 -56
View File
@@ -52,9 +52,10 @@ export default function HowItWorksPage() {
This makes it resistant to censorship and single points of failure.
</p>
<p className="text-gray-600 dark:text-gray-300 mb-6">
The system uses advanced networking techniques like UDP hole-punching to connect through firewalls and NAT,
making it possible to access services even when they're behind restrictive network configurations. Let's walk through
exactly how this all works, step by step.
The system uses UDP hole-punching (via HyperDHT) to connect through firewalls and NAT, making it possible to
reach services behind restrictive networks. Two P2P layers work together: <strong className="text-gray-900 dark:text-white">Hyperswarm</strong> on the
P2NS topic syncs DNS claims and votes; <strong className="text-gray-900 dark:text-white">Holesail</strong> uses separate hashes on the DHT for each
published service tunnel. Let's walk through exactly how this works, step by step.
</p>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">The Big Picture</h2>
@@ -65,10 +66,13 @@ export default function HowItWorksPage() {
<P2NSFlowDiagram />
</div>
<p className="text-gray-600 dark:text-gray-300 mb-6">
The key components involved are: <strong className="text-gray-900 dark:text-white">peers</strong> (other P2NS nodes),
<strong className="text-gray-900 dark:text-white"> Holesail</strong> (for firewall traversal),
<strong className="text-gray-900 dark:text-white"> virtual interfaces</strong> (for local routing), and
<strong className="text-gray-900 dark:text-white"> proxy servers</strong> (for HTTPS handling). Let's dive into each step in detail.
The key components are: <strong className="text-gray-900 dark:text-white">peers</strong> (Hyperswarm + Corestore/Autopass for DNS state),
<strong className="text-gray-900 dark:text-white"> protomux-rpc</strong> (core invites and plugin traffic),
<strong className="text-gray-900 dark:text-white">Holesail</strong> (per-service tunnels),
<strong className="text-gray-900 dark:text-white">virtual interfaces</strong> (local IPs per domain), and
<strong className="text-gray-900 dark:text-white">HTTPS/HTTP proxies</strong> (TLS and routing). Built-in sites such as{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">p2ns.admin</code> and plugin domains under{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">plugin-sites/</code> are resolved without a P2P claim.
</p>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">Step-by-Step Process</h2>
@@ -82,17 +86,24 @@ export default function HowItWorksPage() {
<p className="text-gray-600 dark:text-gray-300 mb-4">
The service provider first needs to create a Holesail connection hash. They run:
</p>
<CodeBlock code="holesail --live 80" />
<CodeBlock code="holesail --live 80 --public" />
<p className="text-gray-600 dark:text-gray-300 mb-4">
This generates a random cryptographic string (hash) like <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">hs://s00084bf87dfa89a3048fb081c0e6207eb5a</code>.
This hash is stored on the DHT (Distributed Hash Table). Other peers can use this hash to look up the service information on the DHT and establish a connection.
Use <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">--public</code> so the service is announced on HyperDHT for remote peers.
This generates a hash like <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">hs://s00084bf87dfa89a3048fb081c0e6207eb5a</code>.
That hash is independent of the P2NS swarm topic—peers use it only when connecting to your service tunnel.
</p>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">2. Create a Domain Claim</h4>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Next, they register their domain (e.g., <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">example.tld</code>)
by creating a claim in the P2P network. This claim says "I own example.tld and it should point to this Holesail hash."
The claim is stored in a distributed ledger called Autopass, which all peers can read and verify.
by creating a claim in the P2P network. This claim says &quot;I own example.tld and it should point to this Holesail hash.&quot;
Claims live in the shared <strong className="text-gray-900 dark:text-white">Autopass</strong> store (<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">dnsPass</code>) on top of Corestore—an append-only, signed log replicated across writers.
</p>
<p className="text-gray-600 dark:text-gray-300 mb-4">
You can add domains via the admin UI (<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">https://p2ns.admin</code>),{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">cache/domains.json</code>, or the API. On a new network, one node must run as{' '}
<strong className="text-gray-900 dark:text-white">master</strong> (<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">--master</code> or{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">P2NS_MASTER=true</code>) to initialize the core and issue writer invites.
</p>
<p className="text-gray-600 dark:text-gray-300 mb-4">
The claim is stored with a key like <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">claim:example.tld:claimant-id</code>
@@ -118,29 +129,44 @@ export default function HowItWorksPage() {
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">1. Topic-Based Discovery</h4>
<p className="text-gray-600 dark:text-gray-300 mb-4">
All P2NS nodes use the same "topic" to find each other. Think of it like a meeting room number - everyone who wants
to participate in P2NS goes to the same room. The topic is generated from a seed string (<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">p2ns-dns</code>),
to participate in P2NS goes to the same room. The topic is a 32-byte buffer from <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">sha256(TOPIC_SEED)</code> (default <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">p2ns-dns</code>),
which ensures all P2NS nodes discover each other regardless of where they are in the world.
</p>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">2. Connection Establishment</h4>
<p className="text-gray-600 dark:text-gray-300 mb-4">
When a node starts up, it joins the Hyperswarm network using this topic. Hyperswarm uses a distributed hash table (DHT)
to help nodes find each other. Once two nodes discover each other, they establish a direct connection.
When a node starts up, it joins Hyperswarm on this topic. Under the hood, <strong>HyperDHT</strong> announces and
looks up peers, coordinates UDP hole punching, and falls back to encrypted relays when needed. Each connection
completes a Noise XX handshake before any application data flows.
</p>
<p className="text-gray-600 dark:text-gray-300 mb-4">
The connection process involves:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-4">
<li>Exchanging public keys for secure communication</li>
<li>Setting up encrypted channels for data exchange</li>
<li>Establishing replication streams for the distributed ledger</li>
<li>Noise XX handshake proves each peer holds the advertised key</li>
<li>Protomux multiplexes core RPC and plugin protocols on the same connection</li>
<li>Autopass replicates <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">dnsPass</code> to authorized writers (separate from the P2NS topic used only for discovery)</li>
</ul>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">3. Data Synchronization</h4>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">3. Joining as a Writer (Master vs Joiner)</h4>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Once connected, nodes use Corestore to synchronize the distributed ledger. Corestore is a distributed storage system
that ensures all peers have the same data. When a new claim or vote is added, it's automatically replicated to all
connected peers.
Only peers with Autopass writer access can append claims and votes. A <strong className="text-gray-900 dark:text-white">master</strong> node
creates the core and can deliver invites; <strong className="text-gray-900 dark:text-white">joiners</strong> request access over protomux-rpc:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-4">
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">invite.request</code> — joiner asks a master (or peer that can relay) for an invite</li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">invite.deliver</code> / <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">invite.ack</code> — invite wire is delivered and acknowledged (no legacy invite channel)</li>
<li><code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">core.status</code> — diagnostics for whether a peer can provide invites</li>
</ul>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Masters may proactively send invites when new peers connect (<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">MASTER_PROACTIVE_INVITE_DELAY</code>).
See <a href="/docs/architecture" className="text-blue-600 dark:text-blue-400 hover:underline">Architecture</a> and <a href="/docs/configuration" className="text-blue-600 dark:text-blue-400 hover:underline">Configuration</a> for master/joiner settings.
</p>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">4. Data Synchronization</h4>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Once a peer is a writer, new claims and votes append to <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">dnsPass</code> and replicate
to other writers through Autopass. Corestore manages the underlying Hypercore logs. When a new claim or vote is added, authorized peers receive it automatically.
</p>
<p className="text-gray-600 dark:text-gray-300 mb-4">
This means:
@@ -160,17 +186,26 @@ export default function HowItWorksPage() {
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">1. The DNS Query</h4>
<p className="text-gray-600 dark:text-gray-300 mb-4">
When you type a domain like <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">https://example.tld</code>
in your browser, or when an application makes a DNS query, it goes to the P2NS DNS server (running on port 53).
This server is configured as your system's DNS resolver, so all DNS queries go through it.
in your browser, or when an application makes a DNS query, it goes to the P2NS DNS server (port 53 by default, configurable).
This server is configured as your system&apos;s DNS resolver, so queries flow through P2NS first.
</p>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">2. Checking the Cache</h4>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">2. Internal and Local Records</h4>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Before P2P lookup, the resolver checks:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-4">
<li><strong className="text-gray-900 dark:text-white">Internal domains:</strong> <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">p2ns.admin</code> and any domain with a plugin under <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">plugin-sites/&#123;domain&#125;/config.json</code> (e.g. <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">peer.directory</code>, <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">peer.paste</code>)</li>
<li><strong className="text-gray-900 dark:text-white">Local DNS:</strong> entries in <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">cache/local_dns.json</code> override both P2P and public DNS</li>
</ul>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">3. Checking the Cache</h4>
<p className="text-gray-600 dark:text-gray-300 mb-4">
First, P2NS checks its local cache to see if it recently resolved this domain. If the cache entry is still valid
(typically cached for 30 seconds), it returns the cached result immediately. This makes repeated queries very fast.
</p>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">3. Querying the P2P Network</h4>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">4. Querying the P2P Network</h4>
<p className="text-gray-600 dark:text-gray-300 mb-4">
If not in cache, P2NS looks up the domain in the distributed ledger. It searches through all the claims stored locally
(remember, each node has a complete copy) to find any claims for the requested domain.
@@ -180,13 +215,13 @@ export default function HowItWorksPage() {
to find all people who have claimed this domain.
</p>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">4. Consensus Mechanism</h4>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">5. Consensus Mechanism</h4>
<p className="text-gray-600 dark:text-gray-300 mb-4">
If multiple people have claimed the same domain, P2NS uses a consensus algorithm to determine which one is correct:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-4">
<li><strong className="text-gray-900 dark:text-white">Count votes:</strong> Each claim can have votes from other peers. The system counts how many votes each claimant has received.</li>
<li><strong className="text-gray-900 dark:text-white">Check quorum:</strong> A quorum (minimum number of votes) must be met. By default, at least 50% of active peers must vote, with a minimum of 2 votes.</li>
<li><strong className="text-gray-900 dark:text-white">Check quorum:</strong> Configurable via <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">CONSENSUS_QUORUM_THRESHOLD</code> (default 50% of active peers) and <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">CONSENSUS_MIN_VOTES</code> (default 2).</li>
<li><strong className="text-gray-900 dark:text-white">Resolve ties:</strong> If two claimants have the same number of votes, the system uses a tie-breaker (like preferring the oldest claim).</li>
<li><strong className="text-gray-900 dark:text-white">Return the hash:</strong> Once consensus is reached, the system returns the Holesail hash associated with the winning claim.</li>
</ul>
@@ -194,11 +229,10 @@ export default function HowItWorksPage() {
This ensures that everyone in the network agrees on which service should be associated with each domain name.
</p>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">5. Fallback to Public DNS</h4>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">6. Fallback to Public DNS</h4>
<p className="text-gray-600 dark:text-gray-300 mb-6">
If the domain isn't found in the P2P network, P2NS can fall back to traditional public DNS (like 1.1.1.1 or 8.8.8.8).
This allows you to access both P2P domains and regular internet domains through the same DNS server. You can also configure
local DNS records that take precedence over both P2P and public DNS.
If the domain is not internal, not in local DNS, and not resolved via P2P consensus, P2NS forwards to <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">PUBLIC_DNS_SERVER</code> (comma-separated list supported for failover).
You can use P2P domains and the public internet through one resolver.
</p>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4 mt-8">Part 4: Virtual Interface Setup</h3>
@@ -253,11 +287,15 @@ export default function HowItWorksPage() {
Traditional networking often fails when both the client and server are behind firewalls, but Holesail solves this by:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-4">
<li>Using a bootstrap server to coordinate the connection</li>
<li>Using HyperDHT (the distributed hash table) to exchange endpoints and coordinate simultaneous UDP probes</li>
<li>Sending UDP packets from both sides simultaneously to "punch holes" in the firewall</li>
<li>Establishing a direct peer-to-peer connection once the hole is punched</li>
<li>Maintaining the connection even through network changes</li>
<li>Establishing a direct peer-to-peer connection once the hole is punched, with encrypted relay fallback when needed</li>
<li>Maintaining tunnels; optional <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">FULL_PERSISTENCE</code> keeps clients/servers alive across restarts (see Configuration)</li>
</ul>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Holesail server and client configs persist under <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">cache/holesail_servers.json</code> and{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">cache/holesail_clients.json</code>.
</p>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">2. Using the Hash to Connect</h4>
<p className="text-gray-600 dark:text-gray-300 mb-4">
@@ -384,8 +422,8 @@ export default function HowItWorksPage() {
For a domain to be considered "resolved" (consensus reached), it must meet quorum requirements:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-4">
<li><strong className="text-gray-900 dark:text-white">Percentage threshold:</strong> By default, at least 50% of active peers must vote</li>
<li><strong className="text-gray-900 dark:text-white">Minimum votes:</strong> At least 2 votes are required, regardless of peer count</li>
<li><strong className="text-gray-900 dark:text-white">Percentage threshold:</strong> <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">CONSENSUS_QUORUM_THRESHOLD</code> (default 0.5 = 50% of active peers)</li>
<li><strong className="text-gray-900 dark:text-white">Minimum votes:</strong> <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">CONSENSUS_MIN_VOTES</code> (default 2), regardless of peer count</li>
<li><strong className="text-gray-900 dark:text-white">Vote validation:</strong> Votes must reference existing claims (invalid votes are ignored)</li>
</ul>
<p className="text-gray-600 dark:text-gray-300 mb-4">
@@ -397,7 +435,7 @@ export default function HowItWorksPage() {
When multiple claimants have the same number of votes, P2NS uses tie-breaking strategies:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-4">
<li><strong className="text-gray-900 dark:text-white">Timestamp:</strong> Prefer the oldest claim (default)</li>
<li><strong className="text-gray-900 dark:text-white">Timestamp:</strong> Prefer the oldest claim (<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">CONSENSUS_TIE_BREAKER=timestamp</code>, default)</li>
<li><strong className="text-gray-900 dark:text-white">Claimant age:</strong> Prefer the claimant with the longest history in the network</li>
<li><strong className="text-gray-900 dark:text-white">Lexicographic:</strong> Alphabetical ordering of claimant IDs</li>
</ul>
@@ -410,41 +448,36 @@ export default function HowItWorksPage() {
P2NS uses several advanced technologies. Here's what each one does in simple terms:
</p>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4">Hyperswarm</h3>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4">Hyperswarm & HyperDHT</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
<strong className="text-gray-900 dark:text-white">What it does:</strong> Helps nodes find each other on the internet without a central directory.
<strong className="text-gray-900 dark:text-white">What it does:</strong> Discovers other P2NS nodes on topic <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">sha256(TOPIC_SEED)</code>, coordinates hole punching, and opens encrypted swarm connections.
</p>
<p className="text-gray-600 dark:text-gray-300 mb-6">
<strong className="text-gray-900 dark:text-white">Simple analogy:</strong> Like a distributed phone book where everyone helps maintain the listings.
When you want to find someone, you ask the network, and it routes you to the right place.
<strong className="text-gray-900 dark:text-white">Simple analogy:</strong> A distributed meeting roomeveryone who joins the same topic can find each other without a central directory server.
</p>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4">Corestore</h3>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4">Protomux / protomux-rpc</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
<strong className="text-gray-900 dark:text-white">What it does:</strong> Provides distributed, synchronized storage for the domain claims and votes.
<strong className="text-gray-900 dark:text-white">What it does:</strong> Multiplexes JSON-RPC methods on each peer connectioncore invites, consensus helpers, and per-plugin protocols.
</p>
<p className="text-gray-600 dark:text-gray-300 mb-6">
<strong className="text-gray-900 dark:text-white">Simple analogy:</strong> Like a shared Google Doc that everyone has a copy of and automatically syncs.
When someone makes a change, everyone's copy updates automatically.
<strong className="text-gray-900 dark:text-white">Simple analogy:</strong> Several phone lines over one secure callcore control traffic and plugin apps share the same tunnel without mixing messages.
</p>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4">Autopass</h3>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4">Corestore & Autopass</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
<strong className="text-gray-900 dark:text-white">What it does:</strong> Manages the secure, append-only ledger of claims and votes.
<strong className="text-gray-900 dark:text-white">What it does:</strong> Corestore holds Hypercore append-only logs; Autopass (<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">dnsPass</code>) stores claims and votes with invite-gated writers.
</p>
<p className="text-gray-600 dark:text-gray-300 mb-6">
<strong className="text-gray-900 dark:text-white">Simple analogy:</strong> Like a blockchain ledger, but simpler. It keeps a permanent record
of who claimed what and who voted for whom, and this record can't be tampered with.
<strong className="text-gray-900 dark:text-white">Simple analogy:</strong> A signed ledger everyone authorized can append toentries are immutable and verifiable by Merkle proofs, not by trusting who sent them.
</p>
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4">Holesail</h3>
<p className="text-gray-600 dark:text-gray-300 mb-4">
<strong className="text-gray-900 dark:text-white">What it does:</strong> Establishes direct connections through firewalls and NAT using UDP hole-punching.
<strong className="text-gray-900 dark:text-white">What it does:</strong> Publishes and connects to service tunnels by hash on HyperDHTseparate from the P2NS DNS swarm topic.
</p>
<p className="text-gray-600 dark:text-gray-300 mb-6">
<strong className="text-gray-900 dark:text-white">Simple analogy:</strong> Like two people behind locked doors coordinating to open their doors
at the same time so they can pass messages through. The "hole" is the temporary opening in the firewall that allows
direct communication.
<strong className="text-gray-900 dark:text-white">Simple analogy:</strong> A dedicated address for your web server or game port; DNS consensus points a domain name at that address.
</p>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">Real-World Example</h2>
@@ -542,7 +575,7 @@ export default function HowItWorksPage() {
</ul>
</li>
<li>Each client establishes a Holesail connection to the corresponding service</li>
<li>Bob's subscription preferences are saved locally in <code className="bg-gray-200 dark:bg-gray-700 px-1.5 py-0.5 rounded">subscriptions.json</code></li>
<li>Bob&apos;s subscription preferences are saved locally in <code className="bg-gray-200 dark:bg-gray-700 px-1.5 py-0.5 rounded">cache/subscriptions.json</code></li>
</ul>
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3 mt-6">Step 3: Dynamic Synchronization - Alice Adds a New Service</h4>
@@ -581,7 +614,7 @@ export default function HowItWorksPage() {
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-3 mt-6">Step 5: Auto-Subscription on Bootup</h4>
<ul className="list-disc pl-6 space-y-2 text-gray-700 dark:text-gray-300 mb-4">
<li>When Bob restarts his P2NS node, the system loads his saved subscriptions from <code className="bg-gray-200 dark:bg-gray-700 px-1.5 py-0.5 rounded">subscriptions.json</code></li>
<li>When Bob restarts his P2NS node, the system loads his saved subscriptions from <code className="bg-gray-200 dark:bg-gray-700 px-1.5 py-0.5 rounded">cache/subscriptions.json</code></li>
<li>For each subscribed service, P2NS automatically:
<ul className="list-disc pl-6 mt-2 space-y-1">
<li>Creates the Holesail client</li>
@@ -609,6 +642,16 @@ export default function HowItWorksPage() {
Domain owners can offer multiple services, and subscribers can automatically access them all with minimal configuration.
The system handles all the complexity of connection management, protocol differences, and dynamic updates.
</p>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">Related Documentation</h2>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300">
<li><a href="/docs/getting-started" className="text-blue-600 dark:text-blue-400 hover:underline">Getting Started</a> install, master node, first domain</li>
<li><a href="/docs/architecture" className="text-blue-600 dark:text-blue-400 hover:underline">Architecture</a> modules, Core RPC, plugins, logging</li>
<li><a href="/docs/configuration" className="text-blue-600 dark:text-blue-400 hover:underline">Configuration</a> environment variables and defaults</li>
<li><a href="/docs/upgrading" className="text-blue-600 dark:text-blue-400 hover:underline">Upgrading</a> coordinated upgrades and <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">--clean</code></li>
<li><a href="/docs/plugins" className="text-blue-600 dark:text-blue-400 hover:underline">Built-in Plugins</a> peer.directory, peer.paste, and more</li>
<li><a href="/learn/p2p" className="text-blue-600 dark:text-blue-400 hover:underline">How P2P Works</a> NAT, HyperDHT, and the Holepunch stack</li>
</ul>
</div>
</section>
</div>
+19 -1
View File
@@ -1,6 +1,6 @@
import type { Metadata } from 'next';
import Link from 'next/link';
import { Book, Settings, Code, Wrench, Network, FileText, LayoutDashboard } from 'lucide-react';
import { Book, Settings, Code, Wrench, Network, FileText, LayoutDashboard, ArrowUpCircle, Puzzle, GitBranch } from 'lucide-react';
export const metadata: Metadata = {
title: 'Documentation - P2NS',
@@ -31,6 +31,12 @@ const docSections = [
icon: Book,
href: '/docs/getting-started',
},
{
title: 'Upgrading',
description: 'Coordinated upgrades, storage reset, and breaking-change guidance',
icon: ArrowUpCircle,
href: '/docs/upgrading',
},
{
title: 'Configuration',
description: 'Environment variables, setup files, and configuration options',
@@ -61,6 +67,18 @@ const docSections = [
icon: Network,
href: '/docs/architecture',
},
{
title: 'How It Works',
description: 'End-to-end flows for DNS resolution, tunneling, and consensus',
icon: GitBranch,
href: '/docs/how-it-works',
},
{
title: 'Built-in Plugins',
description: 'Overview of peer.directory, peer.paste, file.drop, and more',
icon: Puzzle,
href: '/docs/plugins',
},
];
export default function DocsPage() {
+167
View File
@@ -0,0 +1,167 @@
import type { Metadata } from 'next';
import Link from 'next/link';
import { ExternalLink } from 'lucide-react';
export const metadata: Metadata = {
title: 'Built-in Plugins - P2NS Documentation',
description: 'Overview of P2NS built-in plugins: peer.directory, peer.paste, file.drop, and more.',
keywords: ['P2NS plugins', 'peer.paste', 'peer.directory', 'file.drop', 'plugin system'],
authors: [{ name: 'Raven Scott' }],
openGraph: {
title: 'Built-in Plugins - P2NS Documentation',
description: 'Overview of P2NS built-in plugins.',
url: 'https://p2ns.space/docs/plugins',
siteName: 'P2NS',
type: 'website',
},
alternates: {
canonical: 'https://p2ns.space/docs/plugins',
},
};
const repoBase = 'https://git.ssh.surf/snxraven/p2ns/src/branch/main';
const plugins = [
{
name: 'p2ns.admin',
url: 'https://p2ns.admin',
description: 'Web-based admin interface for domains, Holesail, DNS, certificates, stats, logs, backups, and plugin management.',
docHref: `${repoBase}/docs/RESTAPI.md`,
docLabel: 'REST API',
},
{
name: 'peer.directory',
url: 'https://peer.directory',
description: 'Browse and search domains registered across the P2NS network.',
docHref: `${repoBase}/docs/plugins/README.md`,
docLabel: 'Plugin index',
},
{
name: 'peer.paste',
url: 'https://peer.paste',
description:
'Temporary replicated text snippets with E2EE by default, optional public or passphrase mode, markdown and syntax highlighting, expiry, burn-after-read, and max-read limits.',
docHref: `${repoBase}/docs/plugins/peer.paste.md`,
docLabel: 'peer.paste docs',
},
{
name: 'file.drop',
url: 'https://file.drop',
description:
'Temporary file sharing with shareable links (24-hour storage). Disabled on master nodes via disable_on_master — use a joiner node to access file.drop.',
docHref: `${repoBase}/plugin-sites/file.drop/README.md`,
docLabel: 'file.drop README',
note: 'Not available on master nodes',
},
{
name: 'global.profile',
url: 'https://global.profile',
description: 'Distributed user profiles across the network.',
docHref: `${repoBase}/docs/plugins/README.md`,
docLabel: 'Plugin index',
},
{
name: 'peer.visualize',
url: 'https://peer.visualize',
description: 'Visualize peer connections and network topology.',
docHref: `${repoBase}/docs/plugins/README.md`,
docLabel: 'Plugin index',
},
{
name: 'domain.consensus',
url: null,
description: 'Consensus and domain voting utilities for the network.',
docHref: `${repoBase}/docs/CONSENSUS.md`,
docLabel: 'Consensus docs',
},
{
name: 'example.plugin',
url: null,
description: 'Reference plugin for authors building custom internal domains.',
docHref: `${repoBase}/docs/plugins/PLUGIN_SDK.md`,
docLabel: 'Plugin SDK',
},
];
export default function PluginsPage() {
return (
<div className="min-h-screen">
<section className="py-20 bg-gradient-to-b from-blue-50 to-white dark:from-gray-900 dark:to-gray-800">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<h1 className="text-4xl sm:text-5xl font-bold text-gray-900 dark:text-white mb-6">
Built-in Plugins
</h1>
<p className="text-xl text-gray-600 dark:text-gray-300">
Internal domains served by the P2NS plugin system
</p>
</div>
</section>
<section className="py-20 bg-white dark:bg-gray-900">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<p className="text-gray-600 dark:text-gray-300 mb-8">
Plugin domains are auto-discovered from <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">plugin-sites/&#123;domain&#125;/config.json</code>.
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">p2ns.admin</code> is always treated as internal.
Detailed API and development docs live in the{' '}
<a href={`${repoBase}/docs/plugins/README.md`} target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline">
main repository
</a>.
</p>
<div className="space-y-6">
{plugins.map((plugin) => (
<div
key={plugin.name}
className="border border-gray-200 dark:border-gray-700 rounded-lg p-6 bg-white dark:bg-gray-800"
>
<div className="flex flex-wrap items-center gap-2 mb-2">
<h2 className="text-xl font-semibold text-gray-900 dark:text-white">{plugin.name}</h2>
{plugin.note && (
<span className="text-xs font-medium px-2 py-0.5 rounded bg-amber-100 dark:bg-amber-900/40 text-amber-900 dark:text-amber-100">
{plugin.note}
</span>
)}
</div>
{plugin.url && (
<p className="text-sm text-gray-500 dark:text-gray-400 mb-2">
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">{plugin.url}</code>
</p>
)}
<p className="text-gray-600 dark:text-gray-300 mb-4">{plugin.description}</p>
<a
href={plugin.docHref}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 text-blue-600 dark:text-blue-400 hover:underline text-sm"
>
{plugin.docLabel}
<ExternalLink className="h-3.5 w-3.5" />
</a>
</div>
))}
</div>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mt-12 mb-4">Plugin authors</h2>
<p className="text-gray-600 dark:text-gray-300 mb-4">
Custom plugins use protomux RPC via <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">sdk.channels.register</code>.
Legacy message channels are no longer supported.
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300">
<li>
<a href={`${repoBase}/docs/plugins/PLUGIN_SDK.md`} target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline">Plugin SDK</a>
</li>
<li>
<a href={`${repoBase}/docs/plugins/PLUGIN_CHANNELS.md`} target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline">Plugin RPC</a>
</li>
<li>
<a href={`${repoBase}/docs/plugins/HYPERDB.md`} target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline">HyperDB</a>
</li>
<li>
<Link href="/docs/upgrading" className="text-blue-600 dark:text-blue-400 hover:underline">Upgrading</Link> (breaking changes)
</li>
</ul>
</div>
</section>
</div>
);
}
+22 -8
View File
@@ -133,17 +133,30 @@ curl -k https://p2ns.admin/api/interfaces`} />
<li>Check browser console for errors</li>
</ul>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">Sync Issues</h2>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">Upgrade and Version Mismatch</h2>
<p className="text-gray-600 dark:text-gray-300 mb-4">
If domains aren't syncing across peers:
After upgrading P2NS, all peers must run the same version. Mixed RPC/legacy nodes break invites and plugin communication.
See the <a href="/docs/upgrading" className="text-blue-600 dark:text-blue-400 hover:underline">Upgrading</a> guide.
</p>
<CodeBlock code={`# Reset storage (WARNING: deletes all data)
sudo node p2ns.js --clean --master
<p className="text-gray-600 dark:text-gray-300 mb-4">
After a Holepunch stack upgrade, wipe storage on every node:
</p>
<CodeBlock code="sudo node p2ns.js --clean [--master]" />
# Check swarm logs for peer events
# Look for [Swarm] connection/disconnection messages`} />
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">Sync and Invite Issues</h2>
<p className="text-gray-600 dark:text-gray-300 mb-4">
If domains aren&apos;t syncing or joiners never receive invites:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-4">
<li>Confirm all peers upgraded together (no mixed versions)</li>
<li>Check admin Stats Core RPC invite diagnostics</li>
<li>Use <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/diagnostics/invites</code></li>
<li>Tail the <strong>core</strong> log channel in the admin Logs tab</li>
</ul>
<CodeBlock code={`# Reset storage (WARNING: deletes all data)
sudo node p2ns.js --clean --master`} />
<p className="text-gray-600 dark:text-gray-300 mt-4">
<strong>Solution:</strong> Monitor swarm logs, ensure peers are connected, and verify Autopass invites are working.
<strong>Solution:</strong> Ensure peers are connected on Hyperswarm, verify Core RPC invite flow, and use --clean if storage is from an older stack.
</p>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6 mt-12">Getting Help</h2>
@@ -151,7 +164,8 @@ sudo node p2ns.js --clean --master
If you're still experiencing issues:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300">
<li>Check the logs in the admin interface</li>
<li>Check split logs in the admin Logs tab (select core, dns, or plugins channel)</li>
<li>Read <a href="/docs/upgrading" className="text-blue-600 dark:text-blue-400 hover:underline">Upgrading</a> for breaking changes</li>
<li>Review the <a href="/docs/architecture" className="text-blue-600 dark:text-blue-400 hover:underline">Architecture</a> documentation</li>
<li>Check the <a href="https://git.ssh.surf/snxraven/p2ns" target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline">repository</a> for known issues</li>
<li>Enable debug logging by setting <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">LOG_LEVEL=0</code> in your .env</li>
+146
View File
@@ -0,0 +1,146 @@
import type { Metadata } from 'next';
import Link from 'next/link';
import CodeBlock from '@/components/ui/CodeBlock';
export const metadata: Metadata = {
title: 'Upgrading - P2NS Documentation',
description: 'How to upgrade P2NS safely: coordinated peer upgrades, storage reset with --clean, and invite RPC troubleshooting.',
keywords: ['P2NS upgrade', 'migration', 'clean storage', 'breaking changes', 'P2NS update'],
authors: [{ name: 'Raven Scott' }],
openGraph: {
title: 'Upgrading - P2NS Documentation',
description: 'Coordinated upgrades, storage reset, and invite troubleshooting for P2NS.',
url: 'https://p2ns.space/docs/upgrading',
siteName: 'P2NS',
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: 'Upgrading - P2NS Documentation',
description: 'Coordinated upgrades, storage reset, and invite troubleshooting for P2NS.',
},
alternates: {
canonical: 'https://p2ns.space/docs/upgrading',
},
};
const repoBase = 'https://git.ssh.surf/snxraven/p2ns/src/branch/main';
export default function UpgradingPage() {
return (
<div className="min-h-screen">
<section className="py-20 bg-gradient-to-b from-blue-50 to-white dark:from-gray-900 dark:to-gray-800">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<h1 className="text-4xl sm:text-5xl font-bold text-gray-900 dark:text-white mb-6">
Upgrading P2NS
</h1>
<p className="text-xl text-gray-600 dark:text-gray-300">
Safe upgrade steps for breaking releases and storage migrations
</p>
</div>
</section>
<section className="py-20 bg-white dark:bg-gray-900">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="bg-amber-50 dark:bg-amber-900/30 border-l-4 border-amber-500 p-4 rounded mb-8">
<p className="text-amber-900 dark:text-amber-100">
<strong>Important:</strong> Recent releases include breaking changes to the core invite protocol
and plugin peer communication. All peers on a network must upgrade together. Mixed old and new
versions will not interoperate for invites or plugin RPC.
</p>
</div>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6">Coordinated upgrade</h2>
<p className="text-gray-600 dark:text-gray-300 mb-4">
When upgrading across a release that changes the Holepunch stack, core RPC, or plugin protocols:
</p>
<ol className="list-decimal pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-6">
<li>Pull the latest <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">p2ns</code> code on every node (master and joiners).</li>
<li>Run <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">npm install</code> on each node.</li>
<li>Stop all running nodes.</li>
<li>Reset storage on each node (see below) if required for your upgrade path.</li>
<li>Start all nodes on the new versionmasters first, then joiners.</li>
</ol>
<p className="text-gray-600 dark:text-gray-300 mb-6">
Core control-plane traffic now uses protomux RPC only (<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">invite.request</code>,{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">invite.deliver</code>,{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">invite.ack</code>,{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">core.status</code>, consensus RPC). The legacy{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">p2ns.core-invite</code> protomux channel is removed.
Plugin peer communication is also RPC-only.
</p>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6">When to reset storage</h2>
<p className="text-gray-600 dark:text-gray-300 mb-4">
After a Holepunch stack upgrade (Corestore, Autopass, HyperDB, etc.), existing on-disk P2P storage
may be incompatible. Wipe storage with <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">--clean</code>:
</p>
<CodeBlock code="sudo node p2ns.js --clean [--master]" />
<p className="text-gray-600 dark:text-gray-300 mb-4 mt-4">
Also use <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">--clean</code> when:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-6">
<li>Joiners repeatedly fail to accept invites after an upgrade</li>
<li>Plugin HyperDB schema mismatches persist after a version bump</li>
<li>You need a known-good fresh state for debugging</li>
</ul>
<p className="text-gray-600 dark:text-gray-300 mb-6">
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">--clean</code> clears Corestore data and resets plugin HyperDB storage.
The admin panel storage-clean action includes the same plugin storage scope. Back up{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">STORAGE_DIR</code> and{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">cache/</code> first if you need to preserve domains or local DNS.
</p>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6">Shared Corestore namespaces</h2>
<p className="text-gray-600 dark:text-gray-300 mb-6">
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">USE_SHARED_CORESTORE_NAMESPACES</code> defaults to{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">true</code>.
Plugin databases and drives use shared namespaces on the main Corestore. Changing this after data
exists may require a clean reset. See the{' '}
<Link href="/docs/configuration" className="text-blue-600 dark:text-blue-400 hover:underline">Configuration</Link> guide.
</p>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6">Invite troubleshooting</h2>
<p className="text-gray-600 dark:text-gray-300 mb-4">
If peers connect but joiners never sync the dnsPass ledger:
</p>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300 mb-4">
<li>Confirm every peer runs the same P2NS generation (no mixed RPC/legacy nodes).</li>
<li>Open the admin <strong>Stats</strong> tab and review the <strong>Core RPC</strong> invite diagnostics section.</li>
<li>Use <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">GET /api/diagnostics/invites</code> for detailed invite RPC state.</li>
<li>Tail the <strong>core</strong> log channel in the admin <strong>Logs</strong> tab (or <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">core.log</code> under <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">LOG_DIR</code>).</li>
<li>Try <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">--clean</code> on affected nodes if storage may be from an older stack.</li>
</ul>
<p className="text-gray-600 dark:text-gray-300 mb-6">
Master nodes send proactive invites after <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">MASTER_PROACTIVE_INVITE_DELAY</code> (default 2000 ms).
Autopass replication uses its own swarm; the p2ns Hyperswarm topic does not replicate dnsPass directly.
</p>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6">Plugin authors</h2>
<p className="text-gray-600 dark:text-gray-300 mb-6">
If you maintain custom plugins, migrate from legacy message channels to{' '}
<code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">sdk.channels.register</code> with RPC methods.
See the repository{' '}
<a href={`${repoBase}/docs/plugins/PLUGIN_CHANNELS.md`} target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline">Plugin RPC</a>{' '}
and{' '}
<a href={`${repoBase}/docs/plugins/PLUGIN_SDK.md`} target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline">Plugin SDK</a>{' '}
docs for migration tables and Core RPC methods.
</p>
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-6">Further reading</h2>
<ul className="list-disc pl-6 space-y-2 text-gray-600 dark:text-gray-300">
<li><Link href="/docs/troubleshooting" className="text-blue-600 dark:text-blue-400 hover:underline">Troubleshooting</Link></li>
<li><Link href="/docs/admin-panel" className="text-blue-600 dark:text-blue-400 hover:underline">Admin Panel</Link> (Logs, Stats, diagnostics)</li>
<li><Link href="/docs/api" className="text-blue-600 dark:text-blue-400 hover:underline">API Reference</Link></li>
<li>
<a href={`${repoBase}/docs/RESTAPI.md`} target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline">REST API (full reference)</a>
</li>
<li>
<a href={`${repoBase}/docs/plugins/HYPERDB.md`} target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline">HyperDB recovery and version migration</a>
</li>
</ul>
</div>
</section>
</div>
);
}
+24 -10
View File
@@ -20,6 +20,7 @@ import {
BarChart,
Layers,
RefreshCw,
Clipboard,
type LucideIcon,
} from 'lucide-react';
@@ -141,10 +142,10 @@ const features: Feature[] = [
name: 'Web-Based Admin Interface',
icon: Code,
description:
'Comprehensive management panel for domains, Holesail servers/clients, local DNS, certificates, and more.',
benefits: ['Full system control', 'Real-time updates', 'WebSocket integration', 'Intuitive UI'],
'Management panel at p2ns.admin for domains, Holesail, DNS, certificates, split logs, live stats, Core RPC diagnostics, and plugins.',
benefits: ['Split log channels', 'Live stats via WebSocket', 'Core/Plugin RPC diagnostics', 'Plugin management'],
example:
'Manage all aspects of your P2NS node through a modern web interface with real-time updates and comprehensive controls.',
'Tail core or dns logs per channel, monitor invite RPC health in Stats, and manage plugins with validated action parameters.',
},
{
name: 'Stateful Persistence',
@@ -170,7 +171,7 @@ const features: Feature[] = [
{
name: 'Invitation-Based Peer Joining',
icon: UserPlus,
description: 'Master nodes issue invites to joiners, with optional support for joiners to issue invites.',
description: 'Master nodes issue invites over protomux RPC (invite.request, invite.deliver, invite.ack); joiners sync via Autopass.',
benefits: [
'Controlled network growth',
'Secure peer addition',
@@ -209,15 +210,15 @@ const features: Feature[] = [
{
name: 'System Metrics and Monitoring',
icon: BarChart,
description: 'Real-time and historical metrics collection with configurable sampling, aggregation, and retention periods.',
description: 'Live stats over WebSocket (subscribe-stats) with historical metrics, Core RPC invite diagnostics, and Plugin RPC protocol stats.',
benefits: [
'Real-time metrics',
'Historical data',
'Configurable sampling',
'Performance insights',
'Live WebSocket updates',
'Core invite diagnostics',
'Plugin RPC stats',
'Hypercore stats (optional)',
],
example:
'Track DNS queries, peer connections, API requests, resource usage, and more with detailed metrics and interactive charts.',
'The Stats tab loads once via HTTP then streams stats-snapshot payloads with DNS, peer, Holesail, and invite RPC sections.',
},
{
name: 'Subnet Configuration',
@@ -232,6 +233,19 @@ const features: Feature[] = [
example:
'Configure multiple subnets (e.g., 192.168.3.0/24 and 10.0.0.0/24) to organize IP allocation across different network ranges for better management.',
},
{
name: 'Peer Paste',
icon: Clipboard,
description: 'Temporary E2EE text snippets at peer.paste with expiry, burn-after-read, markdown, and syntax highlighting.',
benefits: [
'E2EE by default',
'Replicated across peers',
'Public or passphrase mode',
'REST and web UI',
],
example:
'Share code or notes at https://peer.paste with automatic expiration and optional burn-after-read limits.',
},
{
name: 'Service Subscriptions',
icon: RefreshCw,
+57 -38
View File
@@ -55,9 +55,10 @@ export default function P2PPage() {
UDP Hole Punching
</h2>
<p className="text-lg text-gray-600 dark:text-gray-300 mb-8">
UDP hole-punching is a technique that allows two peers behind NAT (Network Address Translation)
devices to establish a direct connection. This is essential for P2P networking in environments
with firewalls or NAT.
UDP hole-punching lets two peers behind NAT establish a direct path. In the Holepunch stack
(used by Hyperswarm and Holesail), peers coordinate through <strong>HyperDHT</strong>a distributed
hash tablenot a single central rendezvous server. When both sides use predictable NAT ports,
most sessions connect directly; symmetric NAT or blocked UDP may fall back to encrypted relays.
</p>
<UDPHolePunchingDiagram />
@@ -66,24 +67,25 @@ export default function P2PPage() {
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4">How It Works</h3>
<ol className="list-decimal pl-6 space-y-3 text-gray-600 dark:text-gray-300">
<li>
<strong>Both peers contact a bootstrap server:</strong> Each peer sends a UDP packet to a
publicly accessible server, which records their public IP addresses and ports.
<strong>Announce on the DHT:</strong> Each peer registers its public endpoints with HyperDHT
under its cryptographic identity.
</li>
<li>
<strong>Exchange connection information:</strong> The server shares each peer's public endpoint
with the other peer.
<strong>Signaling through the DHT:</strong> When Alice wants Bob, the DHT routes a connect
intent so both sides learn each other&apos;s public IP and port.
</li>
<li>
<strong>Simultaneous connection attempts:</strong> Both peers attempt to send UDP packets to
each other's public endpoints at the same time.
<strong>Simultaneous UDP probes:</strong> Both peers send UDP packets to each other&apos;s
public endpoints at the same time (often via libudx).
</li>
<li>
<strong>NAT creates a "hole":</strong> The NAT device sees an outgoing packet and creates a
temporary mapping, allowing incoming packets from that destination.
<strong>NAT creates a &quot;hole&quot;:</strong> Outgoing packets open a temporary mapping so
return traffic from that peer can pass through the firewall.
</li>
<li>
<strong>Direct connection established:</strong> Once the hole is punched, peers can communicate
directly without the bootstrap server.
<strong>Direct path or relay:</strong> On success, peers talk directly; if hole-punching fails
(e.g. symmetric NAT on both sides), Hyperswarm can route through blind relays that only see
encrypted Noise traffic (~85% of connections succeed with hole punching in typical conditions).
</li>
</ol>
</div>
@@ -107,10 +109,9 @@ export default function P2PPage() {
<div className="bg-white dark:bg-gray-900 p-6 rounded-lg border border-gray-200 dark:border-gray-700">
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">Types of NAT</h3>
<ul className="space-y-2 text-gray-600 dark:text-gray-300">
<li><strong>Full Cone NAT:</strong> Allows any external host to send packets to the mapped port.</li>
<li><strong>Restricted Cone NAT:</strong> Only allows packets from previously contacted hosts.</li>
<li><strong>Port Restricted Cone NAT:</strong> Similar to restricted, but also checks the port.</li>
<li><strong>Symmetric NAT:</strong> Different external ports for different destinations (most challenging).</li>
<li><strong>Open NAT:</strong> Predictable mappinghole punching usually works.</li>
<li><strong>Consistent NAT:</strong> Same external port per internal portstill punchable with coordination.</li>
<li><strong>Random / symmetric NAT:</strong> Unpredictable external portsoften needs a relay fallback.</li>
</ul>
</div>
<div className="bg-white dark:bg-gray-900 p-6 rounded-lg border border-gray-200 dark:border-gray-700">
@@ -138,8 +139,8 @@ export default function P2PPage() {
Peer Discovery with Hyperswarm
</h2>
<p className="text-lg text-gray-600 dark:text-gray-300 mb-8">
Hyperswarm is a distributed hash table (DHT) based peer discovery system. It allows peers to find
each other without a central directory server.
Hyperswarm is a high-level API on top of <strong>HyperDHT</strong> (Kademlia-style DHT). Peers
announce and discover each other by 32-byte topics without a central directory server.
</p>
<PeerDiscoveryDiagram />
@@ -148,16 +149,13 @@ export default function P2PPage() {
<h3 className="text-2xl font-semibold text-gray-900 dark:text-white mb-4">How Hyperswarm Works</h3>
<div className="space-y-4 text-gray-600 dark:text-gray-300">
<p>
<strong>Topic-based Discovery:</strong> P2NS uses a fixed topic (derived from 'p2ns-dns') to create
a discovery channel. All peers interested in P2NS join this topic.
<strong>Topic-based discovery:</strong> P2NS hashes <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">TOPIC_SEED</code> (default <code className="bg-gray-800 dark:bg-gray-700 text-gray-100 dark:text-gray-200 px-1.5 py-0.5 rounded">p2ns-dns</code>) with SHA-256 to form a 32-byte topic. Every node interested in the network joins that topic.
</p>
<p>
<strong>DHT Lookup:</strong> When a peer wants to find others, it queries the DHT for the topic.
The DHT returns a list of peers that have announced themselves for that topic.
<strong>DHT lookup:</strong> HyperDHT returns peers that have announced on the topic. DHT results are candidates onlyeach connection still completes a Noise XX handshake so the remote party must prove key ownership.
</p>
<p>
<strong>Connection Establishment:</strong> Once peers discover each other, they use the UDP
hole-punching techniques described above to establish direct connections.
<strong>Connection establishment:</strong> Hyperswarm coordinates UDP hole punching (and relay fallback when needed), then runs encrypted streams for Core RPC and plugin traffic.
</p>
<p>
<strong>Automatic Reconnection:</strong> Hyperswarm automatically handles peer churn, reconnecting
@@ -168,27 +166,48 @@ export default function P2PPage() {
</div>
</section>
{/* Corestore & Autopass */}
{/* Holepunch stack */}
<section className="py-20 bg-gray-50 dark:bg-gray-800">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
The Holepunch Connection Stack
</h2>
<p className="text-lg text-gray-600 dark:text-gray-300 mb-6">
P2NS uses the Holepunch stack in layers: raw UDP paths, then encryption, then application protocols.
</p>
<ol className="list-decimal pl-6 space-y-2 text-gray-600 dark:text-gray-300">
<li><strong>libudx</strong> reliable UDP between peers after hole punching</li>
<li><strong>Secret Stream (Noise XX)</strong> mutual authentication and XChaCha20-Poly1305 encryption</li>
<li><strong>Protomux / protomux-rpc</strong> multiplexed channels for core invites, consensus, and plugins</li>
<li><strong>Hypercore / Corestore</strong> append-only, Merkle-verified logs for DNS state</li>
</ol>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-4">
Domain tunnels use <strong>Holesail</strong> (separate hashes on the DHT) for reaching services behind NAT; the P2NS swarm topic is only for DNS peer sync.
</p>
</div>
</section>
{/* Corestore & Autopass */}
<section className="py-20 bg-white dark:bg-gray-900">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">
Decentralized Storage & Security
</h2>
<div className="grid md:grid-cols-2 gap-8">
<div className="bg-white dark:bg-gray-900 p-6 rounded-lg border border-gray-200 dark:border-gray-700">
<div className="bg-gray-50 dark:bg-gray-800 p-6 rounded-lg border border-gray-200 dark:border-gray-700">
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">Corestore</h3>
<p className="text-gray-600 dark:text-gray-300 mb-3">
Corestore provides decentralized, append-only storage for P2NS. Domain claims and votes are stored
in a distributed ledger that all peers can access and verify.
Corestore manages Hypercore append-only logs. Each block is Merkle-linked and signed with Ed25519,
so peers verify data by proofnot by trusting who delivered it. P2NS stores domain claims and votes in this replicated log.
</p>
<ul className="list-disc pl-6 space-y-1 text-gray-600 dark:text-gray-300">
<li>Append-only structure prevents tampering</li>
<li>Cryptographically signed entries</li>
<li>Automatic replication across peers</li>
<li>Conflict resolution through voting</li>
<li>Immutable append-only history</li>
<li>Sparse replication of blocks</li>
<li>Automatic sync across peers</li>
<li>Domain conflicts resolved by peer voting (not Autobase)</li>
</ul>
</div>
<div className="bg-white dark:bg-gray-900 p-6 rounded-lg border border-gray-200 dark:border-gray-700">
<div className="bg-gray-50 dark:bg-gray-800 p-6 rounded-lg border border-gray-200 dark:border-gray-700">
<h3 className="text-xl font-semibold text-gray-900 dark:text-white mb-3">Autopass</h3>
<p className="text-gray-600 dark:text-gray-300 mb-3">
Autopass manages secure writer additions to the Corestore. It ensures only authorized peers can
@@ -215,11 +234,11 @@ export default function P2PPage() {
{[
{ term: 'NAT (Network Address Translation)', definition: 'A method of remapping IP addresses, allowing multiple devices to share a single public IP address.' },
{ term: 'CGNAT (Carrier-Grade NAT)', definition: 'A large-scale NAT implementation used by ISPs to conserve IPv4 addresses, adding complexity to P2P connections.' },
{ term: 'UDP Hole Punching', definition: 'A technique that allows two peers behind NAT devices to establish a direct connection by coordinating packet timing.' },
{ term: 'UDP Hole Punching', definition: 'A technique that allows two peers behind NAT devices to establish a direct connection by coordinating simultaneous UDP probes, usually signaled over HyperDHT.' },
{ term: 'HyperDHT', definition: 'The Kademlia DHT used by Hyperswarm for peer announcements, lookups, and hole-punch coordination—distributed, not a single rendezvous server.' },
{ term: 'DHT (Distributed Hash Table)', definition: 'A decentralized key-value store that allows peers to find each other without a central directory.' },
{ term: 'Bootstrap Server', definition: 'A publicly accessible server that helps peers behind NAT discover each other\'s public endpoints.' },
{ term: 'Peer Discovery', definition: 'The process of finding other peers in a P2P network, typically using DHT or similar mechanisms.' },
{ term: 'Consensus', definition: 'A mechanism for agreeing on the state of distributed data, in P2NS achieved through voting on domain claims.' },
{ term: 'Peer Discovery', definition: 'The process of finding other peers in a P2P network, typically using DHT topics (as P2NS does on Hyperswarm).' },
{ term: 'Consensus', definition: 'In P2NS, agreement on domain ownership through voting on claims in the shared Corestore log (distinct from Autobase-style multi-writer ordering).' },
].map((item) => (
<div key={item.term} className="border-l-4 border-blue-500 pl-4">
<dt className="text-lg font-semibold text-gray-900 dark:text-white mb-1">
+12
View File
@@ -46,6 +46,18 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
changeFrequency: 'weekly',
priority: 0.9,
},
{
url: `${baseUrl}/docs/upgrading`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.9,
},
{
url: `${baseUrl}/docs/plugins`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.8,
},
{
url: `${baseUrl}/docs/configuration`,
lastModified: new Date(),
+1 -1
View File
@@ -10,7 +10,7 @@ export default function PeerDiscoveryDiagram() {
<div className="inline-flex items-center space-x-2 px-4 py-2 bg-purple-100 dark:bg-purple-900 rounded-lg mb-4">
<Search className="h-5 w-5 text-purple-600 dark:text-purple-400" />
<span className="text-sm font-medium text-gray-900 dark:text-white">
Topic: sha256('p2ns-dns')
Topic: sha256(TOPIC_SEED)
</span>
</div>
</div>
@@ -35,8 +35,8 @@ export default function UDPHolePunchingDiagram() {
>
<div className="bg-purple-100 dark:bg-purple-900 p-6 rounded-lg mb-4">
<Server className="h-12 w-12 text-purple-600 dark:text-purple-400 mx-auto mb-2" />
<h3 className="font-semibold text-gray-900 dark:text-white">Bootstrap</h3>
<p className="text-sm text-gray-600 dark:text-gray-400">Server</p>
<h3 className="font-semibold text-gray-900 dark:text-white">HyperDHT</h3>
<p className="text-sm text-gray-600 dark:text-gray-400">DHT signaling</p>
</div>
<div className="space-y-2">
<div className="flex items-center justify-center space-x-2 text-sm">
+11 -1
View File
@@ -29,10 +29,20 @@ export default function Footer() {
<h4 className="text-sm font-semibold mb-4 text-gray-900 dark:text-gray-100">Documentation</h4>
<ul className="space-y-2">
<li>
<Link href="/docs" className="text-sm text-gray-600 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
<Link href="/docs/getting-started" className="text-sm text-gray-600 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
Getting Started
</Link>
</li>
<li>
<Link href="/docs/upgrading" className="text-sm text-gray-600 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
Upgrading
</Link>
</li>
<li>
<Link href="/docs/plugins" className="text-sm text-gray-600 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
Plugins
</Link>
</li>
<li>
<Link href="/learn/p2p" className="text-sm text-gray-600 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors">
Learn P2P
+1
View File
@@ -7,6 +7,7 @@ import { useState } from 'react';
const commands = [
{ label: 'Clone repository', command: 'git clone https://git.ssh.surf/snxraven/p2ns.git' },
{ label: 'Install dependencies', command: 'npm install' },
{ label: 'Run as master node', command: 'sudo node p2ns.js --master' },
{ label: 'Run as joiner node', command: 'sudo node p2ns.js' },
];
+2 -2
View File
@@ -4,11 +4,11 @@ import { motion } from 'framer-motion';
const technologies = [
{ name: 'Corestore', description: 'Decentralized storage', url: 'https://www.npmjs.com/package/corestore' },
{ name: 'Hyperswarm', description: 'Peer discovery', url: 'https://www.npmjs.com/package/hyperswarm' },
{ name: 'Hyperswarm', description: 'HyperDHT discovery', url: 'https://www.npmjs.com/package/hyperswarm' },
{ name: 'Holesail', description: 'Secure Tunnels', url: 'https://holesail.io' },
{ name: 'Autopass', description: 'Secure invitations', url: 'https://www.npmjs.com/package/autopass' },
{ name: 'Node.js', description: 'Runtime', url: 'https://nodejs.org' },
{ name: 'Protomux', description: 'Channel multiplexing', url: 'https://www.npmjs.com/package/protomux' },
{ name: 'Protomux', description: 'RPC multiplexing (core + plugins)', url: 'https://www.npmjs.com/package/protomux-rpc' },
];
export default function TechnologyStack() {
+3 -1
View File
@@ -1,7 +1,9 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
turbopack: {
root: process.cwd(),
},
};
export default nextConfig;
+971 -1349
View File
File diff suppressed because it is too large Load Diff
+21 -16
View File
@@ -6,27 +6,32 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "eslint ."
},
"dependencies": {
"framer-motion": "^11.0.0",
"lucide-react": "^0.554.0",
"shiki": "^1.0.0"
"framer-motion": "^12.40.0",
"lucide-react": "^0.575.0"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.17",
"@types/node": "^20.19.25",
"@types/react": "^19.2.6",
"@tailwindcss/postcss": "^4.3.0",
"@types/node": "^20.19.41",
"@types/react": "^19.2.15",
"@types/react-dom": "^19.2.3",
"autoprefixer": "^10.4.22",
"baseline-browser-mapping": "^2.8.32",
"eslint": "^9.39.1",
"eslint-config-next": "^16.0.3",
"next": "^16.0.3",
"postcss": "^8.5.6",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"tailwindcss": "^4.1.17",
"autoprefixer": "^10.5.0",
"baseline-browser-mapping": "^2.10.32",
"eslint": "^9.39.4",
"eslint-config-next": "^16.2.6",
"next": "^16.2.6",
"postcss": "^8.5.15",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"tailwindcss": "^4.3.0",
"typescript": "^5.9.3"
},
"engines": {
"node": ">=20"
},
"overrides": {
"postcss": "^8.5.15"
}
}