⏳ This skill is pending AI review.

Scores will appear once the review pipeline completes.

version unknown

nostr-vpn

@mmalmi⭐ 1.2k stars

Connect an authorized machine to a Nostr VPN private mesh, review device join requests, and verify access with the nvpn CLI. Use for account-free private networking between agent hosts, people, and servers.

Choose how to use this skill

You do not need every option. Choose the path your AI client supports. The stable page stays the same; versioned files are immutable.

1. Native installer

This listing has no registered native installer command. Use the complete package or source fallback below, depending on what your client supports.

Do not guess an installer command or replace an existing version without reviewing the diff.

2. Complete package recommended

Download the ZIP when available. It includes SKILL.md plus the references, security notes and version metadata.

No complete ProSkills package is published for this listing yet.

3. Prompt-only

Copy the prompt above when the agent can read the stable page or when you want to adopt the workflow without installing a skill.

Need only the instruction file?

Download SKILL.md only if your client requires a single file. The complete ZIP is safer for a full installation because it preserves the references and release context.

No path installs or executes anything by itself. Your agent still needs access to the project files. Before updating, compare the installed version and review the diff.

—/10

// RATINGS

⭐GitHub Stars
⭐⭐⭐⭐⭐ 1.2k on GitHubGitHub ↗

Very popular

🟢ProSkills Score
—
📍

Not yet listed on ClawHub or SkillsMP

// README

nostr-vpn

nostr-vpn is a Tailscale-style private mesh VPN with a data plane powered by our independently evolved FIPS implementation, based on the original FIPS project. It also includes an experimental marketplace for byte-metered public exit nodes paid in Bitcoin through Cashu.

Nostr identities and signed rosters control enrollment; peers connect directly when possible and route through FIPS neighbors when direct UDP is unavailable. MagicDNS, subnet routes, exit nodes, and WireGuard upstream egress are built in. The project includes the nvpn CLI and daemon plus native apps for macOS, Linux, Windows, Android, and iOS.

Connect people, servers, and agent hosts without an email, phone number, or VPN provider account. See agent onboarding and headless approval and the agent plugin.

The fork is optimized for high-rate VPN traffic. In comparable direct-path benchmarks it delivers roughly three times the original implementation's throughput by keeping packet ownership, buffers, and session state together, batching I/O and cryptography, reusing packet storage, and avoiding packet-by-packet queue hops, allocations, copies, and repeated lookups. It preserves the FIPS protocol surface.

Install

Desktop apps target Apple Silicon macOS and x64 Linux/Windows; mobile builds target arm64, and CLI archives also cover Linux arm64. StartOS and Umbrel support x86_64/amd64 and arm64. Intel macOS is source-only.

On Debian or Ubuntu, building the CLI with Cargo requires pkg-config and libdbus-1-dev (sudo apt install pkg-config libdbus-1-dev). The prebuilt CLI archives do not require these development packages.

CLI Quick Start

Create a network on the first device:

nvpn init
DEVICE_ID='<paste nostr_pubkey from nvpn init>'
nvpn set --device "$DEVICE_ID"
nvpn start --daemon --connect

On another device, start its daemon, generate a signed join request, and scan or paste the request into an admin's Nostr VPN app:

nvpn init
nvpn start --daemon --connect
nvpn join-request

On the admin device, open Devices → Add Join Request, scan or paste the request, check the device details, and confirm Add device. Approval is not automatic. The admin app signs and distributes the updated roster; this flow does not need a separate add-device --publish command. Keep the joining daemon running until it receives approval. For CLI inspection and approval, see the headless enrollment guide.

The daemon lifecycle is:

nvpn start --daemon --connect
nvpn status
nvpn stop

For startup at boot on Linux or macOS, pass the absolute path of the configuration you initialized to sudo nvpn service install --config <path>. Stop a separately started daemon with nvpn stop before installing the service. Use the same --config <path> with nvpn status, nvpn set, and nvpn service status; run these with sudo if the service owns the configuration or state files. Without an explicit path, an elevated command can select a different user's configuration and create a separate identity and network. On Windows, run nvpn service install from an elevated shell and check it with nvpn service status.

Linux update checks select packages for your distribution and architecture. Automatic downloads stay in the background and are remembered across app restarts. Choose Show download to open the folder and install the package; the app never opens an installer or browser automatically.

Containers and headless nodes

Use a Linux CLI archive matching the container architecture. Persist the config directory, including its key sidecars, and pass the same --config path to every command. The Umbrel image and Compose files provide an existing packaged daemon and web interface; Dockerfile.e2e is a source-built test fixture.

  • Run with root, /dev/net/tun, NET_ADMIN, and iproute2. Include nftables for exit protection and iptables for exit forwarding and legacy-rule cleanup. Version 4.1.18 also requires iptables for plain mesh startup. Current source skips the optional legacy sweep with a warning if the tool is missing; cleanup of forwarding rules owned by the session still requires it.
  • Container restrictions can still block route cleanup: a read-only /proc/sys/net/ipv4/route/flush makes ip route flush cache fail. Privileged mode is a workaround for an isolated test container, not a universal production requirement. Host networking gives the daemon access to the host's routes and firewall; choose it only when that is intended.
  • Without systemd-resolved, split MagicDNS may remain available only at the local address and port printed in the log (normally 127.0.0.1:1053). That does not make names resolve through the system resolver automatically. Linux secure DNS is separate: in Docker it can manage /etc/resolv.conf and use port 53. See #79 for the existing-local-resolver conflict; a working mesh alone does not verify DNS.
  • Use nvpn start --daemon --connect for nvpn status --json health checks. Require status_source == "daemon" before interpreting readiness. Foreground nvpn connect sessions do not publish daemon state; config-only readiness is unknown (null in JSON).

The quick start above works for headless enrollment. For a manually provisioned two-node lab, nvpn set --network-id sets and enables the active network; editing TOML with sed is unnecessary. Initialize each node separately, exchange its public device ID, then run on each node with the appropriate values:

nvpn set --config "$CONFIG" --network-id "$NETWORK_ID" \
  --device "$LOCAL_DEVICE_ID" --device "$PEER_DEVICE_ID" \
  --endpoint "$LOCAL_ENDPOINT" --fips-peer-endpoint "$PEER_DEVICE_ID=$PEER_ENDPOINT" \
  --fips-host-tunnel-enabled true --fips-advertise-public-endpoint false \
  --fips-nostr-discovery-enabled false --fips-bootstrap-enabled false \
  --fips-webrtc-enabled false --lan-discovery-enabled false
nvpn start --config "$CONFIG" --daemon --connect

Use the same network ID and device roster on both nodes. Endpoints are reachable IP:port pairs; their ports must match each node's listen port (51820 by default). Keep the independently generated private keys on their respective nodes.

Paid Exits

Buy or sell VPN bandwidth for Bitcoin. Providers can sell their own internet connection or a WireGuard VPN uplink. Providers advertise per-byte prices over Nostr; buyers fund Cashu Spilman channels and sign payment updates as they use bandwidth. Uploads count as sent; UDP replies and acknowledged TCP downloads count too, without double-billing TCP retransmissions. Choose a provider manually or automatically using connection quality, trusted ratings, and price.

By default, channels renew before a 12-hour service cutoff, when providers begin a

// HOW IT'S BUILT

// FILES

2 files shipped with this skill. Inline text is the catalog copy. The upstream link is the source repository.

  • plugins/nostr-vpn/skills/nostr-vpn/SKILL.md4.2 KBupstream ↗
    ---
    name: nostr-vpn
    description: Connect an authorized machine to a Nostr VPN private mesh, review device join requests, and verify access with the nvpn CLI. Use for account-free private networking between agent hosts, people, and servers.
    ---
    
    # Nostr VPN
    
    Use the `nvpn` CLI for private networking without a VPN provider account.
    Every device retains its own identity; network membership still needs an
    administrator's approval. This workflow does not require paid exits.
    
    ## Establish the execution context
    
    - Run `nvpn --version`, `nvpn join-requests --help`, and
      `nvpn join-request --help`. CLI approval and JSON join-request output are
      currently source-only and have not yet shipped in a release. Install a matching
      binary from https://github.com/mmalmi/nostr-vpn/releases/latest or use
      `cargo install nvpn`. Check the release's supported architecture.
    - Choose an explicit, persistent absolute config path and pass `--config` on
      every command, including elevated ones. Reuse a designated profile; never
      overwrite an existing one with `init --force`.
    - A Linux tunnel needs network privileges, `/dev/net/tun`, and `iproute2`;
      containers need `NET_ADMIN` and the TUN device. macOS/Windows need elevated
      platform networking setup. A cloud terminal or plugin does not confer this
      access. If unavailable, report that specific limitation and use an authorized
      machine with those capabilities.
    - Persist config and key sidecars privately. Give separate devices separate
      keys. Do not print private keys, copy the admin identity to a joining device,
      or put join links into public output.
    
    ## Create or join
    
    On a new admin host, run `nvpn init --config "$NVPN_CONFIG"`, then use the
    printed public `nostr_pubkey` with
    `nvpn set --config "$NVPN_CONFIG" --device "$ADMIN_DEVICE_ID"`.
    Start it with `nvpn start --config "$NVPN_CONFIG" --daemon --connect`.
    
    On the joining host, initialize its own config and start its daemon the same
    way, then run `nvpn join-request --config "$NVPN_CONFIG" --json`. The
    `join_request` field contains the link to share with the intended admin through
    the user's selected channel. JSON output returns immediately and does not
    resume a paused daemon. Keep the daemon connected; obtain a fresh link if a
    macOS/Linux joining daemon restarts.
    
    On the admin host, save the received link in a private file and run:
    
    ```sh
    nvpn join-requests inspect --request-file "$REQUEST_FILE" --json
    ```
    
    Inspection is read-only. Check the returned device ID with the intended joiner;
    the supplied name alone is not proof. When admission of that device to the
    config's active network is authorized, run:
    
    ```sh
    nvpn join-requests approve --config "$NVPN_CONFIG" \
      --request-file "$REQUEST_FILE" --json
    ```
    
    This signs the roster, queues delivery, and resumes an already-running paused
    admin daemon. If it is stopped, start it. `delivery: queued` is not a delivery
    receipt. Keep both daemons connected until the joiner receives approval.
    
    For inbound manual-join requests, use
    `nvpn join-requests list --config "$NVPN_CONFIG" --json`, then approve exactly
    the selected pending ID with `join-requests approve --device "$DEVICE_ID"` and
    the same config. Shared links need not appear in that list.
    
    If the installed CLI lacks `join-requests` or `join-request --json`, use the
    interactive `join-request` and the native admin app's **Devices → Add Join
    Request**, or build current source with
    `cargo install --locked --path crates/nostr-vpn-cli` from its checkout. Do not
    substitute private-key sharing or manual signed-roster edits.
    
    ## Verify the requested result
    
    Run `nvpn status --config "$NVPN_CONFIG" --json`. Interpret readiness only when
    `status_source` is `daemon`; config-only readiness is unknown. Use
    `nvpn ip --config "$NVPN_CONFIG" --peer --json` and
    `nvpn ping --config "$NVPN_CONFIG" "$PEER_TUNNEL_IP"`, then access the actual
    service the user requested using its normal authentication. Report queued
    approval, mesh reachability, DNS, and service access separately when they differ.
    
    Avoid changing default routes, selecting paid exits, or spending funds unless
    that is part of the user's request. Stop only a daemon started for a temporary
    task with `nvpn stop --config "$NVPN_CONFIG"`; preserve existing services.
    
    Detailed guide: https://github.com/mmalmi/nostr-vpn/blob/master/docs/agents.md
    
  • README.mdupstream ↗

    Contents are not inlined in the catalog. Open the upstream file.

Plain-text copy, no login: /skill-bundles/mmalmi-nostr-vpn-plugins-nostr-vpn-skills-nostr-vpn.md

// REPO STATS

1.2k stars