@decentnetwork/lan
Private virtual LAN over Peer transport: 10.86.x.x addresses, names, ACLs, files, proxy and a local console. CLI: agentnet.
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 want | Use |
|---|---|
| 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 --helpThe 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-demoNotes 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 withlisten EINVAL. Keep the project directory path short. Our scratch path was too long, so for the daemon step (marked…below) we setcarrier.dataDir: carrier(a relative path) inconfig.yamland ranup,statusanddiagfrom insideagentnet-demowith--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 other10.86.x.xloopback 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-wideWhen 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).
| Command | What 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 show | Print 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). |
restart | Ask the running daemon to re-exec itself. |
status / diag / doctor | Human status, JSON snapshot over IPC, and a self-check that prints fixes. |
peers list, ipam list | Peer 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 P | Remove 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. |
console | Keyboard-driven terminal UI. |
dora enable --address A (or --userid U), dora disable, dora status, dora autofriend none|all|allow|remove|list | Dora registry integration. |
dns install [--uninstall], dns hosts | OS resolver wiring for *.decent (needs root), or /etc/hosts lines. |
bootstrap show, bootstrap update | Inspect 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-ca | Built-in HTTP CONNECT proxy (exit) and a local multi-exit router. |
service install [--uninstall] | uninstall | status | restart | Install the daemon as a system service (needs root). |
harden-ssh | Make 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):
| File | Contents |
|---|---|
config.yaml | Node name, Carrier data dir, bootstrap and express nodes, network, paths, proxy, friends, dora |
ipam.yaml | Static peer to virtual-IP mappings (namespace, peers: []) |
policy.yaml | ACL: defaultAction (allow after init) and rules |
audit.log | ACL audit trail |
carrier/keypair.json | The node's Carrier identity. Treat it like an SSH private key. |
carrier/daemon.sock | IPC 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: trueEnvironment variables
| Variable | Effect |
|---|---|
AGENTNET_LOG_LEVEL | debug / info / warn / error (default info) |
AGENTNET_LOG_FILE | Daemon writes and rotates its own log file (used by service install on macOS) |
AGENTNET_HANDSHAKE_TIMEOUT_MS | Packet-session handshake timeout (default 30000) |
AGENTNET_DORA_TIMEOUT_MS | Per-request dora timeout (default 30000) |
AGENTNET_TUNNEL_IDLE_MS | Idle reaping for CONNECT-proxy tunnels (default 120000) |
DECENT_DEBUG=1 | Verbose 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 signedwintun.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) andyargs.
Version, source and changelog
- Verified version: 0.1.316 (
lateston npm, published 2026-11-06). - npm: https://www.npmjs.com/package/@decentnetwork/lan
- Source repository is not public yet. The package code, including
docs/INSTALL.md,docs/INSTALL-WINDOWS.mdanddocs/CONFIGURATION.md, can be browsed at https://www.npmjs.com/package/@decentnetwork/lan?activeTab=code - License: GPL-3.0-or-later (per
package.json). - Changelog: none is published in the package.