Updates
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/node_modules.root-owned
|
||||
/.npm-local
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
|
||||
@@ -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
@@ -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 interface—extensible via the plugin SDK
|
||||
and protomux RPC.
|
||||
</p>
|
||||
<ArchitectureDiagram />
|
||||
</div>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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/{domain}/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
@@ -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/{domain}/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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 "I own example.tld and it should point to this Holesail hash."
|
||||
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'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/{domain}/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 room—everyone 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 connection—core 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 call—core 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 to—entries 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 HyperDHT—separate 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'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
@@ -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() {
|
||||
|
||||
@@ -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/{domain}/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>
|
||||
);
|
||||
}
|
||||
@@ -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'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>
|
||||
|
||||
@@ -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 version—masters 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
@@ -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
@@ -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 table—not 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'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'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 "hole":</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 mapping—hole punching usually works.</li>
|
||||
<li><strong>Consistent NAT:</strong> Same external port per internal port—still punchable with coordination.</li>
|
||||
<li><strong>Random / symmetric NAT:</strong> Unpredictable external ports—often 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 only—each 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 proof—not 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">
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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' },
|
||||
];
|
||||
|
||||
|
||||
@@ -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
@@ -1,7 +1,9 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
turbopack: {
|
||||
root: process.cwd(),
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
Generated
+971
-1349
File diff suppressed because it is too large
Load Diff
+21
-16
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user