All docs / npm packages

@decentnetwork/lan

Private virtual LAN over Peer transport: 10.86.x.x addresses, names, ACLs, files, proxy and a local console. CLI: agentnet.

Version
0.1.316 · npm
Platform
macOS · Linux · Windows (WSL2)
Source
Source repository not public yet
npm install -g @decentnetwork/lan

@decentnetwork/lan (also called decentlan or AgentNet) is the node-side daemon and CLI that puts machines on a private virtual LAN over the Decent Network. Each node owns a Carrier identity, registers a 10.86.x.x address with a Dora name server, and tunnels IP traffic to its Carrier friends — so SSH, HTTP and most TCP/UDP services work between machines behind NAT with no public IP and no port forwarding. The package ships two binaries, agentnet and decentlan (the same program), plus prebuilt TUN helpers for Linux, macOS and Windows.

When to use it

Pick @decentnetwork/lan when you want IP-level connectivity between machines: reach a service by 10.86.x.x or <name>.decent, grant a friend access to one port for a few hours, or run a CONNECT proxy exit for peers. It is an operator tool, not a library.

You wantUse
Messages, files and calls in your own Node or browser app@decentnetwork/peer
A chat app for people (no root, no virtual LAN)@decentnetwork/beagle
To run the name/IP registry the LAN uses@decentnetwork/dora
A virtual LAN between your machines, with ACLs@decentnetwork/lan

Creating the TUN interface needs root (or Administrator on Windows). Identity, configuration, friend management and a "mock TUN" daemon run as a normal user, which is what the quickstart below does.

Install

npm install @decentnetwork/lan@0.1.316
npx agentnet --help

The project's own guide installs it globally (npm install -g @decentnetwork/lan) so agentnet is on PATH for system services. A local install plus npx is enough to try it.

Quickstart

This creates an identity and config in a throwaway directory and starts the daemon without a real TUN device — no sudo. Commands that touch the OS (TUN, system service, DNS resolver) are listed separately below.

mkdir lan-demo && cd lan-demo
npm init -y
npm install @decentnetwork/lan@0.1.316

# 1. Config + identity in ./agentnet-demo (skip contacting the public doras for now)
npx agentnet init --name docs-demo --config-dir ./agentnet-demo --no-dora
npx agentnet identity show --config-dir ./agentnet-demo
npx agentnet status --config-dir ./agentnet-demo

# 2. Optional: run the daemon as a normal user (mock TUN, no virtual interface)
npx agentnet dora disable --config-dir ./agentnet-demo   # keep this test node off the public registry
npx agentnet up --config-dir ./agentnet-demo             # foreground; Ctrl+C to stop
# in a second terminal:
npx agentnet status --config-dir ./agentnet-demo
npx agentnet diag --config-dir ./agentnet-demo

Notes from the verification run:

  • The CLI talks to the daemon over a Unix socket at <carrier.dataDir>/daemon.sock. On macOS a socket path longer than about 104 bytes fails with listen EINVAL. Keep the project directory path short. Our scratch path was too long, so for the daemon step (marked … below) we set carrier.dataDir: carrier (a relative path) in config.yaml and ran up, status and diag from inside agentnet-demo with --config-dir ..
  • Do not start a second daemon on a machine that already runs agentnet. At startup on macOS the daemon tries to remove every other 10.86.x.x loopback alias it finds, and it treats those as leftovers from an earlier run. As a normal user the attempt fails harmlessly. Run as root, it would remove the live daemon's address.

Verified output (npm @decentnetwork/lan@0.1.316, 2026-11-06):

$ npx agentnet init --name docs-demo --config-dir ./agentnet-demo --no-dora
Created config directory: ./agentnet-demo
Created config: …/lan-demo/agentnet-demo/config.yaml
[IPAM] INFO: Creating new IPAM file: …/lan-demo/agentnet-demo/ipam.yaml
[IPAM] INFO: IPAM saved to …/lan-demo/agentnet-demo/ipam.yaml
[Policy] INFO: Policy saved to …/lan-demo/agentnet-demo/policy.yaml

Decent AgentNet initialized:
  Node name: docs-demo
  Config dir: ./agentnet-demo
  Subnet: 10.86.0.0/16
  Interface: agentnet0

Skipping dora friend-requests (--no-dora); the daemon joins a dora itself on first start.

Next: sudo agentnet service install   # or 'agentnet up --real-tun' to run in foreground

$ npx agentnet identity show --config-dir ./agentnet-demo
(generated new identity at …/lan-demo/agentnet-demo/carrier/keypair.json)
Address:    HYGw5jP6gVPKJ5Ggumss8GG5BRyyon1kZrJ3g74SscnAsndBWvkd
Public Key: 6fce427433ff79b62e4efd2e301bcef3f10ecc3948d9f30c8056f293d3655e07
User ID:    8XSfwqkwfduQBTS9HtA5fq7vVTYzqonAgjb2xdXZTgFG

$ npx agentnet status --config-dir ./agentnet-demo
Decent AgentNet
Node: docs-demo
Namespace: agentnet-main
Daemon: not running — everything below is CONFIGURED, not live
Subnet: 10.86.0.0/16
Interface: agentnet0
Virtual IP: 10.86.1.10
Bootstrap: 15 nodes
Peers (on disk): 0
ACL rules: 0

$ npx agentnet dora disable --config-dir ./agentnet-demo
Dora disabled. Restart the daemon to take effect.

$ npx agentnet up …        # mock TUN, dora disabled
[Daemon] INFO: Starting daemon (node: docs-demo)
[PeerManager] INFO: Peer started: HYGw5jP6gVPKJ5Ggumss8GG5BRyyon1kZrJ3g74SscnAsndBWvkd
[IPC] INFO: Listening on carrier/daemon.sock
[Daemon] INFO: Using deterministic fallback IP 10.86.84.251 (derived from userid; avoids the 10.86.1.10 init-default collision)
[TunDevice[agentnet0]] INFO: Using mock TUN mode (no real /dev/net/tun)
[Daemon] INFO: Daemon started successfully
Daemon started. Identity: HYGw5jP6gVPKJ5Ggumss8GG5BRyyon1kZrJ3g74SscnAsndBWvkd
Press Ctrl+C to stop.
[PeerManager] INFO: Joined Carrier network
[Daemon] INFO: Self-announce complete

$ npx agentnet status …    # second terminal, daemon up
Decent AgentNet
Node: docs-demo
Namespace: agentnet-main
Daemon: running
Userid: 8XSfwqkwfduQBTS9HtA5fq7vVTYzqonAgjb2xdXZTgFG
Address: HYGw5jP6gVPKJ5Ggumss8GG5BRyyon1kZrJ3g74SscnAsndBWvkd
Subnet: 10.86.0.0/16
Interface: agentnet0
Virtual IP: 10.86.84.251
  note: config.yaml still says 10.86.1.10 — the daemon's address wins
Bootstrap: 15 nodes
Peers (roster): 0
Friends: 0 (0 online)
Sessions: 0 active
Traffic: 0 sent / 0 received / 0 dropped
ACL rules: 0

$ npx agentnet diag …      # trimmed
{
  "identity": { "address": "HYGw5jP6…vkd", "userid": "8XSfwqkw…TgFG", … },
  "node": { "name": "docs-demo", "autoAccept": false, … },
  "tun": { "name": "agentnet0", "ip": "10.86.84.251", "subnet": "10.86.0.0/16" },
  "dht": { "bootstrapsConfigured": 15, "knownNodesCount": 73, "tcpRelayConnected": 3, … },
  "friends": [], "ipam": [], …
}

Joining the real network (documented, not run here)

These steps need root or Administrator. They were not run during verification. They are the documented path from the package's docs/INSTALL.md:

sudo npm install -g @decentnetwork/lan
agentnet init --name my-machine                              # also friends the default public doras
sudo agentnet service install --config-dir $HOME/.agentnet   # systemd / LaunchDaemon / Windows startup task
# or run in the foreground with a real TUN:
sudo agentnet up --real-tun --config-dir $HOME/.agentnet

agentnet ipam list           # roster from dora
agentnet diag                # your allocated 10.86.x.x
sudo $(which agentnet) dns install --config-dir $HOME/.agentnet   # optional: resolve *.decent system-wide

When you run with sudo, always pass --config-dir, because sudo changes $HOME. service install writes /etc/systemd/system/agentnet.service on Linux and /Library/LaunchDaemons/com.decentlan.agentnet.plist on macOS, or creates a Windows startup task. --real-tun also runs harden-ssh first, which makes SSH key-only for connections from 10.86.0.0/16.

API reference

@decentnetwork/lan has no programmatic API. Its package entry (dist/index.js) imports the CLI, so import "@decentnetwork/lan" parses process.argv and exits. Its index.d.ts exports nothing. To build on the same network from code, use @decentnetwork/peer. To talk to a running daemon from another process, @decentnetwork/beagle exports ipcCall() for the daemon's socket protocol.

CLI commands

Every command accepts --config-dir <dir> (default ~/.agentnet).

CommandWhat it does
init [--name N] [--no-dora]Create config.yaml, ipam.yaml, policy.yaml. By default it also sends friend requests to the default public doras. --no-dora skips that.
identity showPrint Address, Public Key and User ID (creates the keypair if missing).
up [--name N] [--real-tun]Start the daemon in the foreground. Without --real-tun it uses a mock TUN (no root, no virtual interface).
restartAsk the running daemon to re-exec itself.
status / diag / doctorHuman status, JSON snapshot over IPC, and a self-check that prints fixes.
peers list, ipam listPeer IP allocations (live from the daemon when it is up).
ipam assign --peer ID --name N [--ip IP] [--service name:proto:port]Register a peer with a virtual IP.
resolve <name>Name to virtual IP.
grant --peer P [--tcp PORT…] [--udp PORT…] [--expires 1h|24h|7d] [--purpose S]Add an ACL allow rule.
revoke --peer PRemove a peer's grants.
audit log [--tail N]Show the audit trail (default 50 lines).
friend-request <address> [--hello S] [--wait-ms N]Send a friend request. The target can be a Carrier address or a *.beagles.eth name.
friend-accept [userid]Accept a queued request (alias of friends accept).
friends list | pending | accept | reject | remove | alias | autoaccept [on|off]Manage Carrier friends.
chat send <userid> <text>, chat history [--peer P]Native Carrier text chat.
file send <userid> <path>Send a file. The receiver saves it to <config-dir>/downloads.
ui [--port 8765] [--host 127.0.0.1] [--dora-dir D]Local web UI for friends and requests.
consoleKeyboard-driven terminal UI.
dora enable --address A (or --userid U), dora disable, dora status, dora autofriend none|all|allow|remove|listDora registry integration.
dns install [--uninstall], dns hostsOS resolver wiring for *.decent (needs root), or /etc/hosts lines.
bootstrap show, bootstrap updateInspect or refresh the Carrier bootstrap node list.
proxy enable [--port 8888] | disable | status | allow-host | revoke-host | list-hosts | who | block | allow | use --peer P | whitelist | file-whitelist | busy-mbps | router | trust-caBuilt-in HTTP CONNECT proxy (exit) and a local multi-exit router.
service install [--uninstall] | uninstall | status | restartInstall the daemon as a system service (needs root).
harden-sshMake sshd key-only for the 10.86.0.0/16 subnet (needs root).
fix-perms [--user U]Repair root-owned files under the config dir (run with sudo).

agentnet --version prints the version from the nearest package.json above the binary's link, not the lan version. In a local install that is your own project's version. Use npm ls @decentnetwork/lan to check the installed version.

Configuration

Files

All files live in the config directory (~/.agentnet by default, or --config-dir):

FileContents
config.yamlNode name, Carrier data dir, bootstrap and express nodes, network, paths, proxy, friends, dora
ipam.yamlStatic peer to virtual-IP mappings (namespace, peers: [])
policy.yamlACL: defaultAction (allow after init) and rules
audit.logACL audit trail
carrier/keypair.jsonThe node's Carrier identity. Treat it like an SSH private key.
carrier/daemon.sockIPC socket while the daemon runs (a named pipe on Windows)

config.yaml as written by init in 0.1.316 (lists trimmed):

node:
  name: docs-demo
  namespace: agentnet-main
carrier:
  dataDir: /home/you/.agentnet/carrier
  bootstrapNodes:            # 15 public Carrier bootstrap nodes
    - host: 13.58.208.50
      port: 33445
      pk: 89vny8MrKdDKs7Uta9RdVmspPjnRMdwMmaiEW27pZ7gh
  expressNodes:              # offline-message relays
    - host: lens.beagle.chat
      port: 443
      pk: ECbs4GxwGzxGerNkmqDJFibEmevu8jAXqAZtikccvD95
    - host: tokyo.fi.chat
      port: 8443
      pk: EzpBtoUkjeMQfuLGWwTUbvzCn9rK4J648Ziy21EKxefo
network:
  interface: agentnet0       # macOS uses a kernel-assigned utun* instead
  ip: 10.86.1.10             # fallback; dora's allocation wins
  subnet: 10.86.0.0/16
  dnsDomain: decent
  dnsPort: 5354
paths:
  ipamFile: /home/you/.agentnet/ipam.yaml
  policyFile: /home/you/.agentnet/policy.yaml
  auditLog: /home/you/.agentnet/audit.log
proxy:
  enabled: false
  port: 8888
friends:
  autoAccept: false          # queue incoming requests for `friends accept`
dora:
  enabled: true
  userids: [ …3 public dora userids… ]
  refreshIntervalMs: 60000
  autoFriend: [ …8 userids… ]   # whitelist; also accepts "none" or "all"

policy.yaml rules look like this (fields from the package's docs/CONFIGURATION.md):

defaultAction: deny
rules:
  - peer: alice.decent          # name or carrier id
    direction: inbound          # inbound | outbound | both
    allow:
      - { proto: tcp, port: 22, purpose: "ssh" }
    deny: []
    expiresAt: 2026-12-31T23:59:00Z
    audit: true

Environment variables

VariableEffect
AGENTNET_LOG_LEVELdebug / info / warn / error (default info)
AGENTNET_LOG_FILEDaemon writes and rotates its own log file (used by service install on macOS)
AGENTNET_HANDSHAKE_TIMEOUT_MSPacket-session handshake timeout (default 30000)
AGENTNET_DORA_TIMEOUT_MSPer-request dora timeout (default 30000)
AGENTNET_TUNNEL_IDLE_MSIdle reaping for CONNECT-proxy tunnels (default 120000)
DECENT_DEBUG=1Verbose Carrier SDK logs (from @decentnetwork/peer)

Platforms

  • Node.js >= 20 (engines). Verified with Node 24.3.0 on macOS (arm64).
  • Prebuilt TUN helpers ship in bin/ for Linux x64/arm64, macOS x64/arm64 and Windows x64/arm64. The Windows builds include a signed wintun.dll.
  • A real TUN needs root on Linux and macOS and an Administrator PowerShell on Windows. Kernel NAT/masquerade is Linux-only.
  • Outbound TCP/UDP to the Carrier bootstrap nodes (port 33445, plus 443). No inbound port is needed.
  • Runtime dependencies include @decentnetwork/peer, @decentnetwork/peer-webrtc, @decentnetwork/dora, ink/react (TUI) and yargs.

Version, source and changelog