Skip to main content

CLI Reference

Complete command-line reference for the noBGP agent. This covers all commands, options, and configuration methods.

Overview

The nobgp command-line tool provides:

  • User authentication — log in from your personal machine to manage your infrastructure
  • Remote operations — execute commands, open shells, and manage services on your nodes
  • File management — upload, download, list, and delete files on the shared filesystem
  • Network and node management — list, create, and register infrastructure
  • Agent runtime — registration, configuration, service control, and upgrades

Command Structure

nobgp [command] [options]

Root privileges

The agent's own state belongs to root: the profile configuration and the identity files beside it are mode 0600, the local API socket is owned by the configured user, and the system service and installed binary are root's as well. A command that has to read or write any of it says so and takes the privileges itself — you are never asked to retype it with sudo, and it never prints a partial view in place of the real one.

On Linux and macOS such a command prints Root privileges required, re-executing with sudo... and re-runs itself under sudo. Where sudo has no way to ask for a password — no interactive terminal, no SUDO_ASKPASS, and no cached or NOPASSWD credential — it fails immediately with that explanation instead of hanging on a prompt nobody can answer.

On Windows nothing self-elevates: a console cannot raise its own privileges, and re-launching through UAC would move the output and the exit code into a new window, away from the shell you typed in. The command refuses instead, naming the invocation to repeat from an Administrator terminal (right-click Windows Terminal or PowerShell → Run as administrator). The Windows installer is the one exception — it accepts that trade and requests UAC elevation for you.

Either way the refusal also names the unprivileged alternative: the status tool on the node's local MCP server answers read-only questions about the node without root, which is the path for scripts and unattended callers.

Needs rootNever needs root
agent, config, mcp install, register, remove, resolve, service (every subcommand, including logs), show, status, uninstall, upgradeevents, exec, file (all subcommands), list, login, logout, mcp status, mcp uninstall, network (all subcommands), notify, proxy (all subcommands), shell, version

The second column is deliberate, not an oversight. login and logout — and the router-facing commands exec, shell, file, network and proxy — authenticate with your credential from your own keyring, so elevating would consult root's keyring and report the wrong account. notify and events reach the agent through its local API socket, which exists precisely so an unprivileged consumer can use it (see Local API socket keys); a sudo re-exec there would break the scripts the event bus dispatches. mcp status and mcp uninstall touch only your own account's Claude configuration.

Commands

nobgp agent

Run the noBGP agent as a standalone process. If not yet registered, the agent will auto-register non-interactively (requires a registration key in config or environment).

Usage:

nobgp agent [profile] [options]

Examples:

# Run with default profile from /etc/nobgp/default.yml
sudo nobgp agent

# Run a specific profile
sudo nobgp agent production

# Run with debug output
sudo nobgp agent --debug

# Run with custom log level
sudo nobgp agent --log-level debug

Global Options (available on all commands):

OptionShortTypeDescription
--router-rstringRouter FQDN, optionally host:port (default: router.nobgp.com). The agent connects over wss:// (TLS); legacy wss:///ws:// URLs are still accepted and normalized to the bare FQDN
--insecurebooleanAllow a plaintext (ws://) control channel — for local or self-hosted routers without TLS only (default: false)
--transportstringControl-channel transport: auto, quic, or wss (default: auto). auto negotiates the fastest available transport and falls back to WebSocket automatically
--quic-routerstringQUIC control endpoint host:port override for self-hosted routers (default: unset — the endpoint is learned from the router, falling back to the router's own domain)
--debug-dbooleanEnable debug mode (shortcut for --log-level debug)
--log-level-lstringLog level: error, warning, info, debug, trace (default: info)
--compress-zbooleanEnable compression (default: true)
--encrypt-ebooleanRequire encryption (default: true)
--idle-ttldurationRecycle a peer session after this much idle time with no data traffic (default: 1h; 0 = defer to peer). Negotiated end-to-end as the shorter of the two peers' values; clamped to a 1m floor
--interface-istringNetwork interface to use (default: auto)
--pingdurationPing interval for health checks (default: 58s)
--mount-mstringFilesystem mount point (default: platform-dependent)
--userstringAccount that command sessions, dispatched bus commands and file operations run as whenever the caller does not ask to be elevated — independent of --allow-admin. Registration sets it to the installing account, so a default install does not run sessions as root. Leaving it unset on a node whose agent runs as root does not fall back to root: unelevated calls are refused there until an account is named or the caller passes admin (unelevated never means root). --allow-admin=false additionally pins work to this account. An account that does not exist on the machine — or one that resolves to uid 0, --user root included — is refused at the point of setting rather than failing at every unelevated session later. Also settable as NOBGP_USER, which outranks the config file. On Windows the account is the node's second identity from agent 0.4.44, covering file operations first and execution as well from agent 0.4.46 (0.4.44 and 0.4.45 refused an unelevated execution call rather than running it as LocalSystem)
--overlay-cidrstringOverlay TUN CIDR: a /20 within 100.64.0.0/10 (auto-picked if unset)
--mtuintOverlay TUN MTU in bytes (default: 8000; clamped to [1280, 8000]; lower for a constrained path)
--fs-cache-ttldurationHow long the shared drive may serve a cached directory listing before re-asking noBGP (default: 30s; clamped to [0, 10m]). A staleness window, not a performance dial. 0 re-asks every time and still serves unchanged file contents from the local cache. Requires a unit — 30 is thirty nanoseconds. Read by the nfs, fuse and winfsp backends (agent 0.4.67; nfs alone in 0.4.66), where it also sets the timeouts the kernel caches with — those are mount options, so a change reaches them on the next mount. Not persisted by nobgp config: set fs-cache-ttl in the profile file or NOBGP_FS_CACHE_TTL to make it durable. Agent 0.4.66+
--allow-toolsstringsCapability domains this node serves: fs, command (default: both). Covers MCP tools, event-bus sources and published terminal services alike. Domains, not tool names — see Capability keys
--allow-rootsstringsFilesystem roots the fs tools and the fs event source may touch (default: the whole filesystem). The agent's own configuration directory is refused whatever this is set to — see Capability keys
--allow-adminbooleanPermit execution and file access as root / Administrator (default: true). false refuses anything that would run as uid 0 instead of downgrading it — an elevated request, or a terminal service published to run as root — and drops the rest to --user. On a node with no usable --user it is an off switch rather than a narrowing, since unelevated work is refused there too

Exit Codes:

CodeMeaning
0Clean shutdown
1General error
10The profile's configuration file changed on disk, so the agent exited for a reload. On Linux and macOS the agent normally re-executes itself in place instead, so this code is mainly seen on Windows and when a profile is removed. The process manager treats it as a prompt reload, with no crash backoff
11The router rejected this node's credentials (node deleted, key revoked, network removed). Permanent until an operator acts, so the process manager retries every 15 minutes instead of using the crash backoff. Re-register the node, or uninstall the agent

nobgp register

Register the agent with a noBGP network. Opens your browser for OAuth login by default. For Docker/automation, pass a registration key with --key for non-interactive registration.

Usage:

nobgp register [profile] [options]

Examples:

# Register via OAuth browser login (recommended)
sudo nobgp register

# Register with a specific name and network
sudo nobgp register --name "my-server" --network "production"

# Register a named profile
sudo nobgp register production --name "prod-server-1"

# Register with a key (for Docker/automation)
sudo nobgp register --key "<YOUR_REGISTRATION_KEY>" --name "my-server"

Options:

OptionTypeDescription
--keystringRegistration key for the network
--namestringNode name (defaults to hostname)
--networkstringNetwork name (for OAuth login flow)

After successful registration the agent installs and starts the system service itself — there is no prompt. Re-registering a machine whose service has gone missing repairs it, so an uninstall/reinstall cycle converges instead of leaving a registered node with nothing running. Inside a container it prints Container detected — skipping service setup. and leaves the agent to the entrypoint.

Running nobgp register on a node that is already registered prints Agent is already registered. and converges the service. Since agent 0.4.72 that is no longer true of an expired token: a node whose registration token expired while it was offline cannot authenticate, and it cannot refresh either — every refresh path needs the authenticated connection the expired token can no longer make. Such a node prints Registration token expired <instant> — re-registering. and enrolls again under the same identity key, replacing the stale token. Before 0.4.72 that command reported success and changed nothing, on exactly the node that needed it. nobgp status and nobgp show both name an expired token, so you can tell this case from an ordinary stopped agent.

Registration and service setup are reported separately, because they can diverge:

Exit codeMeaning
0Registered, and the service is installed and running
3Registered, but the service could not be installed or started — the reason is printed above it. Fix the cause, then run sudo nobgp service install. The node itself is enrolled; nothing needs re-registering
otherRegistration itself failed

The install script branches on the same codes, so a machine that enrolled but has no running service says so rather than reporting success.

Minimum supported version

The router refuses registration from agents older than 0.3.51, rejecting them with the reason agent version no longer supported. Reinstall the current agent, or run sudo nobgp upgrade -f, before registering such a machine. See Minimum Supported Agent Version.


Login vs Register

The nobgp CLI has two authentication concepts that serve different purposes:

nobgp loginnobgp register
PurposeAuthenticate you (the user)Authenticate a device (the node)
When to useOn your personal machine — laptop, desktop, workstationOn a remote server, VM, Raspberry Pi, or container
What it doesStores your user credentials so you can run management commands (network, node, exec, shell, proxy)Enrolls the machine as a node in a noBGP network and starts the agent
RequiresA browser (opens OAuth login)Either a browser or a registration key (--key)
Runs asYour user accountRoot / Administrator

Typical workflow:

  1. On your laptop, run nobgp login to authenticate yourself. This lets you manage your infrastructure from the command line.
  2. On a remote server, run sudo nobgp register (or use a registration key) to join the machine to your network as a node.
  3. From your laptop, use nobgp exec, nobgp shell, nobgp proxy, etc. to operate on your remote nodes.

You don't need to install or run the agent on your laptop — nobgp login is enough to use the management commands.


nobgp login

Log in to noBGP from your personal machine. Opens your browser for OAuth sign-in and stores credentials in your system keyring.

Usage:

nobgp login

Credentials are stored in the platform's native credential store:

  • macOS: Keychain
  • Linux: Secret Service (GNOME Keyring, KDE Wallet, etc.)
  • Windows: Credential Manager

Once logged in, you can use all management commands (network, node, exec, shell, proxy) without the agent running locally.

Example:

$ nobgp login
Opening browser for login...
Login successful!
tip

If the browser doesn't open automatically, the CLI prints a URL you can copy and paste.


nobgp logout

Clear stored credentials from your system keyring.

Usage:

nobgp logout

Example:

$ nobgp logout
Logged out.

nobgp network

Manage noBGP networks. Requires nobgp login first.

Usage:

nobgp network <subcommand> [options]

Subcommands:

list

List all your networks, nodes, and published services. Nodes are grouped by online/offline status.

nobgp network list [options]

Options:

OptionTypeDescription
--networkstringFilter by network name
--jsonbooleanOutput as JSON

Examples:

# List everything
nobgp network list

# Filter to one network
nobgp network list --network production

# Machine-readable output
nobgp network list --json

Example output:

production
Online:
web-server (debian) 550e8400-e29b-41d4-a716-446655440000
staging-app https://a1b2c3d4.nobgp.com
api-server (debian) 660f9500-f39c-42e5-b827-557766550111

Offline:
old-server (ubuntu) 770a0600-a40d-53f6-c938-668877660222

create

Create a new network.

nobgp network create --name <name> [options]

Options:

OptionTypeDescription
--namestringNetwork name (required, DNS-compatible)
--jsonbooleanOutput as JSON

Example:

nobgp network create --name staging

add-node

Generate install commands for adding a node to your network. The output is meant to be copied and run on the target machine.

nobgp network add-node [options]

Options:

OptionTypeDescription
--networkstringNetwork name (uses default if you have one network)
--nodestringNode name (defaults to hostname on the target machine)
--jsonbooleanOutput as JSON

Example:

$ nobgp network add-node --network production --node web-server-2

── Run on the target machine ──────────────────────────

Linux/macOS:
curl -fsSL https://downloads.nobgp.com/agent/install.sh | sudo NOBGP_KEY=... NOBGP_NAME=web-server-2 sh

Windows:
powershell -NoProfile -Command "Set-Item env:NOBGP_KEY '...'; Set-Item env:NOBGP_NAME 'web-server-2'; irm https://downloads.nobgp.com/agent/install.ps1 | iex"

────────────────────────────────────────────────────────
note

The registration key shown above (NOBGP_KEY=...) can be reused across multiple nodes and does not expire. You can disable it at any time from the noBGP dashboard.


nobgp exec

Execute a command on a remote node and print the output. The remote process's exit code is propagated to the local shell.

Requires nobgp login first.

Usage:

nobgp exec -c <command> [options]

Options:

OptionShortTypeDescription
--command-cstringCommand to execute (required)
--node-idstringNode UUID
--networkstringNetwork name
--nodestringNode name
--adminbooleanRun elevated. Omit it to run as the node's configured user. Refused where the owner set allow-admin: false — and omitting it is refused too on a node whose unelevated identity would be the superuser, which is the node saying to pass --admin or configure a user on it (unelevated never means root)
--workdirstringWorking directory
--timeoutintMax seconds to wait for command to complete (default 300)

You must specify the target node using either --node-id or both --network and --node.

Examples:

# Run a command by network and node name
nobgp exec -c "df -h" --network production --node web-server

# Run a command by node UUID
nobgp exec -c "systemctl status nginx" --node-id 550e8400-e29b-41d4-a716-446655440000

# Run elevated in a specific directory
nobgp exec -c "git pull" --network production --node api-server --admin --workdir /srv/app

# Run a long command with a custom timeout (default is 300s)
nobgp exec -c "pg_dump mydb | gzip > /tmp/backup.sql.gz" --network production --node db-server --timeout 600

Exit codes: The remote command's exit code is forwarded. This means you can use nobgp exec in scripts:

nobgp exec -c "test -f /etc/nginx/nginx.conf" --network prod --node web-1 && echo "nginx configured"

nobgp shell

Open an interactive shell session on a remote node. Provides a full terminal experience with raw mode, resize handling, and support for interactive programs (vim, htop, etc.).

Requires nobgp login first.

Usage:

nobgp shell [options]

Options:

OptionTypeDescription
--node-idstringNode UUID
--networkstringNetwork name
--nodestringNode name
--commandstringShell command (default: login shell)
--adminbooleanRun elevated; omit to run as the node's configured user
--workdirstringWorking directory

You must specify the target node using either --node-id or both --network and --node.

Examples:

# Open a shell on a node
nobgp shell --network production --node web-server

# Open an elevated shell
nobgp shell --network production --node api-server --admin

# Run a specific command instead of the default shell
nobgp shell --network production --node db-server --command "psql -U postgres"
Platform Support

nobgp shell is not yet supported on Windows clients. All other CLI commands work on Windows. You can still open shells on Windows nodes — the limitation is on the client side.


nobgp proxy

Manage published services (proxy and terminal endpoints). Requires nobgp login first.

Usage:

nobgp proxy <subcommand> [options]

Subcommands:

list

List all published services across your networks.

nobgp proxy list [options]

Options:

OptionTypeDescription
--networkstringFilter by network name
--jsonbooleanOutput as JSON

Example:

$ nobgp proxy list

staging-app https://a1b2c3d4.nobgp.com production / web-server svc_abc123
my-terminal https://x9y8z7w6.nobgp.com production / api-server svc_def456

publish

Publish a new service with a public HTTPS URL.

nobgp proxy publish [options]

Options:

OptionTypeDescription
--node-idstringNode UUID
--networkstringNetwork name
--nodestringNode name
--titlestringService title
--proxy-urlstringURL to proxy (e.g. http://localhost:8080)
--commandstringTerminal command to run
--adminbooleanRun elevated; omit to run as the node's configured user
--workdirstringWorking directory
--no-authbooleanDisable authentication (public access)
--sharestringComma-separated email addresses to authorize
--jsonbooleanOutput as JSON

You must specify the target node using either --node-id or both --network and --node. Provide --proxy-url for a proxy service or --command for a terminal service. Omit both for a default shell terminal.

Examples:

# Publish a web app
nobgp proxy publish --network production --node web-server --proxy-url http://localhost:8080 --title "My App"

# Publish a browser terminal
nobgp proxy publish --network production --node api-server --title "Admin Shell"

# Publish a public (no auth) service
nobgp proxy publish --network production --node web-server --proxy-url http://localhost:3000 --no-auth

# Publish a terminal that runs a specific command
nobgp proxy publish --network production --node db-server --command "psql -U postgres" --title "DB Console"

Example output:

https://a1b2c3d4.nobgp.com svc_abc123 (proxy)

update

Update an existing service.

nobgp proxy update --id <service-id> [options]

Options:

OptionTypeDescription
--idstringService ID (required)
--titlestringService title
--proxy-urlstringURL to proxy
--commandstringTerminal command
--adminbooleanRun elevated; omit to run as the node's configured user
--workdirstringWorking directory
--enabledbooleanEnable or disable service
--no-authbooleanDisable authentication (public access)
--jsonbooleanOutput as JSON

Examples:

# Disable a service (the = is required for boolean false values)
nobgp proxy update --id svc_abc123 --enabled=false

# Re-enable a service
nobgp proxy update --id svc_abc123 --enabled

# Change title and make public
nobgp proxy update --id svc_abc123 --title "Production App" --no-auth

# Re-enable authentication on a public service
nobgp proxy update --id svc_abc123 --no-auth=false
tip

Boolean flags like --enabled and --no-auth default to true when present. To set them to false, you must use the = syntax: --enabled=false, --no-auth=false. Writing --enabled false won't work as expected.

delete

Delete a published service.

nobgp proxy delete --id <service-id> [options]

Options:

OptionTypeDescription
--idstringService ID (required)
--jsonbooleanOutput as JSON

Example:

nobgp proxy delete --id svc_abc123

share

Manage the authorized email list for a service.

nobgp proxy share <action> --id <service-id> [emails...]

Actions:

ActionDescription
listShow authorized emails for a service
addGrant access to one or more email addresses
removeRevoke access for one or more email addresses
revokeClear all authorized emails

Options:

OptionTypeDescription
--idstringService ID (required)
--jsonbooleanOutput as JSON

Examples:

# List authorized emails
nobgp proxy share list --id svc_abc123

# Grant access to specific people
nobgp proxy share add --id svc_abc123 alice@example.com bob@example.com

# Grant access to an entire domain
nobgp proxy share add --id svc_abc123 '*@company.com'

# Revoke access for one person
nobgp proxy share remove --id svc_abc123 bob@example.com

# Revoke all access
nobgp proxy share revoke --id svc_abc123

nobgp file

Transfer files to and from your network's shared filesystem. Works from any machine where you're logged in — the agent doesn't need to be running locally.

When the shared filesystem is mounted (e.g., /Volumes/nobgp on macOS, /mnt/nobgp on Linux), commands operate on the local mount directly for best performance. Otherwise, they fall back to the noBGP API automatically.

Requires nobgp login first.

Remote paths on a machine with the drive mounted

Remote paths are relative to the network's share — except on a machine where the drive is mounted, where they are relative to the mount root. Since agent 0.4.54 that root holds node/ and networks/<name>/ rather than a single network's files (see What the mount contains), so nobgp file ls / there lists those two folders. Name the network folder explicitly — nobgp file ls /networks/production/ — to reach the same files the API path would give you.

Usage:

nobgp file <subcommand> [options]

Subcommands:

upload

Upload one or more local files to the remote filesystem. The shell expands globs, so *.txt becomes multiple file arguments.

nobgp file upload <local-path>... [remote-path]

If the last argument starts with /, it's treated as the remote destination. For multiple files, the remote path must end with / (a directory).

Options:

OptionTypeDescription
--networkstringNetwork name (auto-detected if you have one network)
-R, --recursivebooleanUpload directories recursively

Examples:

# Upload a single file to the root
nobgp file upload report.pdf

# Upload to a specific remote path
nobgp file upload report.pdf /docs/report.pdf

# Upload to a remote directory (note the trailing /)
nobgp file upload report.pdf /docs/

# Upload multiple files (shell expands the glob)
nobgp file upload *.log /logs/

# Upload specific files to a directory
nobgp file upload app.log error.log /logs/

# Upload a directory recursively
nobgp file upload -R ./project/ /backups/project/

download

Download files from the remote filesystem. Supports glob patterns for downloading multiple files at once — quote the pattern to prevent shell expansion.

nobgp file download <remote-path> [local-path]

Options:

OptionTypeDescription
--networkstringNetwork name (auto-detected if you have one network)
-R, --recursivebooleanDownload directories recursively

Examples:

# Download a single file to the current directory
nobgp file download /docs/report.pdf

# Download to a specific local path
nobgp file download /docs/report.pdf ./downloads/report.pdf

# Download to a local directory
nobgp file download /docs/report.pdf ./downloads/

# Download multiple files with a glob pattern (quote to prevent shell expansion)
nobgp file download '/logs/*.log' ./local-logs/

# Download all files matching a pattern
nobgp file download '/backups/db-*.sql.gz' ./

# Download a directory recursively
nobgp file download -R /backups/project/ ./local/
tip

Always quote remote glob patterns ('*.log') so your shell doesn't try to expand them locally.

ls

List files and directories in the remote filesystem.

nobgp file ls [remote-path] [options]

Options:

OptionTypeDescription
--networkstringNetwork name (auto-detected if you have one network)
--longbooleanShow detailed listing (type, size, modification time)
--jsonbooleanOutput as JSON

Examples:

# List root directory
nobgp file ls

# List a subdirectory
nobgp file ls /docs/

# Detailed listing
nobgp file ls /docs/ --long

# Machine-readable output
nobgp file ls /docs/ --json

Example output:

$ nobgp file ls /docs/ --long
d 0 2026-03-15 12:00:00 drafts/
- 10240 2026-03-14 09:30:00 report.pdf
- 256 2026-03-12 15:45:00 notes.txt

rm

Remove files or directories from the remote filesystem. Supports multiple paths and glob patterns.

nobgp file rm <remote-path>... [options]

Options:

OptionTypeDescription
--networkstringNetwork name (auto-detected if you have one network)

Examples:

# Remove a single file
nobgp file rm /tmp/old-backup.sql

# Remove multiple files
nobgp file rm /tmp/old-backup.sql /tmp/cache.dat

# Remove files matching a glob pattern
nobgp file rm '/logs/*.log'

# Remove a directory and its contents
nobgp file rm /tmp/scratch/
warning

nobgp file rm deletes files permanently. There is no confirmation prompt or trash/recycle bin.


nobgp config

Create or update the agent configuration file. If the agent is not yet registered and you are running the command by hand, it offers to register the node for you.

Usage:

nobgp config [profile] [options]

Examples:

# Interactive configuration for default profile
sudo nobgp config

# Configure a named profile
sudo nobgp config production

# Enable or disable a profile
sudo nobgp config production --enabled
sudo nobgp config staging --disabled

Options:

All global options from nobgp agent are supported, plus:

OptionTypeDescription
--enabledbooleanEnable this profile (default: true)
--disabledbooleanDisable this profile
--yes / -ybooleanNon-interactive; answer the confirmations for settings that can cut off remote access (agent 0.4.52+)

--yes is deliberately not saved to the config file: it is a statement about one invocation, and persisting it would leave a node pre-answered for every future veto.

Values provided are saved to the platform config file (see Configuration File). Setting a value that happens to be the default — --log-level=info, say — leaves that key as a commented reference line rather than writing it, so the node keeps tracking the default. Uncomment the line by hand if you want the value pinned regardless of what a later release defaults to.

On an unregistered node the registration offer is made only to an interactive caller — one whose standard input and standard error are both terminals. Bare nobgp register opens a browser and waits out the sign-in, which a script or provisioning step has no way to answer, so a non-interactive run prints the two commands and exits 0 instead of blocking:

Configuration saved to /etc/nobgp/default.yml

This node is not registered. To register it:
nobgp register # opens a browser to authenticate
nobgp register --key KEY # unattended, with a network key

That keeps the ordinary provisioning order working — configure first (router, user, capability keys), then register with a key.

If the saved configuration leaves this node serving no MCP commands or no MCP file access — an empty --allow-tools, or one that drops command or fs — the command warns immediately after writing, rather than letting you discover it when the next call is refused.

Example output:

Configuration saved to /etc/nobgp/default.yml

⚠ This node now serves NO commands and NO file access.
Published terminal services are refused too — they answer the same allow-tools.
The remaining way back in is on-device:
sudo nobgp config --allow-tools=fs,command

Dropping only fs is a warning, not a refusal: narrowing a node is legitimate. Dropping command is different, and since agent 0.4.52 it is confirmed before the write — see below. Plan the recovery either way: dropping command closes the browser terminal along with the MCP tools, so the way back in is on the machine itself. See Capability keys.

Three settings are vetted before they are written, because each fails far from the mistake otherwise — and because two of them can take away the only remaining way onto the node. Nothing is saved if a check refuses or a prompt is declined:

  • --user naming an account that does not exist on the machine is refused — left to run, it becomes a refusal on every later unelevated session. Since agent 0.4.41 an account that resolves to uid 0 is refused too, --user root included: unelevated never means root, so that configures a node which refuses every unelevated call while looking like it narrowed them. Name an unprivileged account, or leave --user unset and have callers pass admin. Since agent 0.4.44 the name is accepted on Windows too, where it is the node's second identity — file operations first, and from agent 0.4.46 execution as well (0.4.44 and 0.4.45 refused an unelevated execution call rather than running it as LocalSystem). If NOBGP_USER is set in the agent's environment and names a different account, the command saves your value and warns that the environment outranks it for as long as it is set.

    Clearing it is --user "". That is the one way to say "no account" through the CLI — an empty value with no --user flag is read as "never set" and re-filled with the account that ran the install. Since agent 0.4.52 the clear states what it costs first: on a node whose agent is the superuser, removing the account means every admin: false call is refused rather than narrowed, so unelevated execution stops and callers must ask for admin: true. An interactive run asks for confirmation (--yes answers it); a scripted one proceeds, having printed the warning. The clear survives restarts but not a reinstall — nobgp service install and the post-registration path re-capture the install account deliberately, since a fresh enrollment has no other way to learn one.

  • --allow-tools that drops command is refused without confirmation, since agent 0.4.52. It is the one guard here that stops a scripted caller: the change takes the command tool, dispatched bus commands and published terminal services away together, so unlike --allow-admin=false it leaves nothing to get back in with, and undoing it needs console, RDP or SSH access to the machine. An interactive run asks for confirmation. A non-interactive one must pass --yes — and a command arriving over MCP is non-interactive by construction, which is the case this closes: the warning printed after the write went out on the very session the write had just turned off.

  • --allow-admin=false prints what it costs, and it asks the resolver rather than reading the name. With a usable unprivileged user configured it just states that sessions will run as that account and superuser requests will be refused. Where the unelevated identity would itself be the superuser — no user on a container or a bare-root install, a user that no longer resolves, user: root, or a Windows node offering only LocalSystem — it means remote execution is off entirely (command, command_subscribe and terminal sessions all refused), so an interactive run asks for confirmation and a scripted one proceeds with a warning in the log. Both halves close on each other there: the elevated path is vetoed, and the unelevated one has no identity to land on (unelevated never means root). The same warning is repeated at every config load, naming which case the node is in — and since agent 0.4.52 it names a third: on a node whose agent is not itself the superuser, allow-admin: false locks nothing out, because an elevated request resolves to the same unprivileged identity and is served. The agent says so rather than claiming a refusal it will not make.


nobgp status

Show agent status including system information and per-profile runtime status.

Usage:

nobgp status [options]

Like nobgp show and nobgp config, this command needs root: the agent's local API socket is mode 0600, owned by the configured user (root when that is unset), so an unprivileged run by any other account could read no profile at all and would render an empty profile block that looks exactly like "no agent configured". On Linux and macOS it re-executes itself under sudo; on Windows run it from an Administrator terminal. When sudo has no way to ask for a password — no terminal, no SUDO_ASKPASS, no cached or NOPASSWD credential — the command fails immediately with that explanation instead of hanging on a prompt nobody can answer. For an unprivileged read of the same state, use the status tool on the node's local MCP server.

Options:

OptionShortDescription
--json-jOutput as JSON
--yml-yOutput as YAML (default)
--ping-pProbe each target with ICMP and report reachability and round-trip time

Example output (YAML):

version: "1.0.0"
platform: debian
architecture: amd64
hostname: web-server-1
config_dir: /etc/nobgp
service: running
profile:
pid: 1234
uptime_secs: 3600
registration:
node_id: "550e8400-e29b-41d4-a716-446655440000"
agent_key_id: "a1b2c3d4"
signing_key: "e5f6g7h8"
router:
url: "router.nobgp.com"
connected: true
transport: quic
remote_addr: "203.0.113.10:443"
quic_endpoint: "router.nobgp.com:443"
quic_endpoint_source: learned
quic_pin: provisioned
quic_fallbacks: 0
quic_reconnects: 0
network:
local_ip: "192.168.1.5"
gateway_ip: "100.64.16.1"
tun_device: "nobgp0"
domain: "550e8400-e29b-41d4-a716-446655440000.nobgp.net"
dns: local
ipv6: true
targets:
- name: web-server
node_id: "770e8400-e29b-41d4-a716-446655440222"
address: "100.64.16.2"
local: false
- name: db-primary
node_id: "660e8400-e29b-41d4-a716-446655440111"
address: "100.64.16.3"
local: false
sessions:
- node_id: "660e8400-e29b-41d4-a716-446655440111"
name: db-primary
target: postgres
encrypted: true
compressed: true
- node_id: "660e8400-e29b-41d4-a716-446655440111"
name: db-primary
target: metrics
encrypted: true
compressed: true
mcp:
granted: true
running: true
port: 51843
allow:
tools:
- fs
- command
roots:
- /
admin: true
unelevated: deploy
fs:
type: fuse
mount: /mnt/nobgp
mounted: true
selected: auto
backends:
- "fuse: available"
- "nfs: unavailable (this kernel has no NFSv4 client (no `nfs4` in /proc/filesystems after a module load; OpenWrt: `opkg install kmod-fs-nfs-v4` matching the running kernel))"
- "webdav: available"

When a profile cannot be read at all, it is reported with the reason rather than silently omitted — an absent profile block would be indistinguishable from a profile that does not exist:

unavailable:
production: "agent is not running — start it with 'nobgp service start'"

The other reason is agent did not answer in time — it may be wedged; check 'nobgp service logs'.

Since agent 0.4.72, when the profile's registration token has expired the reason says so instead of leaving you with generic advice that cannot work:

unavailable:
production: "agent is not running — start it with 'nobgp service start'; registration token expired 2026-07-30T09:14:02Z — the router refuses its connections; re-register with 'nobgp register'"

That is the case where starting the agent changes nothing: it starts, the router rejects the expired token, and the process manager paces the next attempt fifteen minutes out — so nobgp status almost always catches the node between attempts and, before 0.4.72, reported an ordinary stopped agent. The token is read straight off disk, which is what lets the reason appear while the daemon is down — the only time it is needed. The fix is nobgp register.

The router block reports the control-channel connection to the noBGP router:

FieldDescription
urlThe router FQDN this node connects to
connectedtrue if the control channel is currently up
transportThe active control-channel transport: quic or wss. Answers "is this node on QUIC?" — omitted when disconnected
remote_addrThe remote address of the live control connection. Shows which address family is actually in use (native IPv6 vs IPv4) and which router endpoint is serving the node. Omitted when disconnected
quic_endpointThe host:port the QUIC leg dials. Omitted when the router URL cannot be parsed
quic_endpoint_sourceWhere the quic_endpoint value came from: override (a --quic-router/NOBGP_QUIC_ROUTER setting), learned (supplied by the router at registration), or derived (the router URL's own host, used before an endpoint is learned)
quic_pinTrust state of the certificate pin that gates every QUIC attempt: provisioned (set via NOBGP_QUIC_CERT_PIN), learned (received at registration), or none. none means QUIC is skipped entirely until a pin is learned — the usual reason a node stays on wss
quic_fallbacksHow many times this process attempted QUIC but registered over WebSocket instead (for example, on a network that blocks UDP). 0 means QUIC has been used throughout, or the node is on a wss-only transport
quic_reconnectsHow many times this process re-established a dropped QUIC control connection. A rising count means the QUIC connection keeps flapping even though recovery succeeds
udp_blockedPresent and true only when the agent has recently observed that UDP is blocked on this network, which makes the next connect skip the QUIC attempt. Omitted otherwise
tls_interceptedPresent only when something re-signed the control channel's TLS connection — antivirus HTTPS scanning or a corporate proxy. The value names the certificate authority that did it (inspection products identify themselves there). Omitted on a clean connection, and never reported for a self-hosted router, whose certificate authority is yours to choose. See Security software and VPNs

The mcp block is the on-box answer to "why is my MCP off" — see Local MCP Server:

FieldDescription
grantedThe router's word: true when an Owner or Admin has given this node a live node_grant role. The grant is what starts the local server, so false means there is nothing on 127.0.0.1 yet
runningtrue when the listener is actually up. granted true with running false means the listener could not bind
portThe port the server actually bound, which is not always the configured mcp-port — a taken port moves. Shown only while running is true, so a remembered port never reads as a live endpoint

The allow block is the node owner's veto set as the agent is applying it right now — what this box will refuse before you dispatch anything to it. It covers every remote path onto the node: the MCP tools and event-bus sources a node_grant reaches, and, since agent 0.4.33, a published terminal service as well. See Capability keys:

FieldDescription
toolsThe capability domains this node serves: fs, command. Both by default
rootsThe filesystem roots the fs tools and the fs event source may touch. / (the whole filesystem) by default, always excluding the agent's own configuration directory
admintrue when callers may ask for elevated execution and admin-level file access. false refuses such a request rather than downgrading it
unelevatedThe account an admin: false call actually runs as on this node. Since agent 0.4.42. Read from the same gate the real call uses, not from the user key — a configured account that was deleted, or one that resolves to uid 0, reads fine in the config and is refused at the call
unelevated_refusalPresent instead of unelevated when unelevated work cannot run here at all, naming which shape it is: no account on a root-running agent, an account that no longer resolves, or user: root. Its presence is the whole "every call on this node needs admin: true" signal — see unelevated never means root. Since agent 0.4.42
unelevated_is_adminPresent only when true: the account in unelevated is itself administrative — a member of the local Administrators group on Windows — so admin: false on this node drops from the ambient identity to Administrator and no further. It is the on-box half of the directory's info.user_is_admin, and its absence is not a claim that the account is ordinary. Since agent 0.4.46
warningPresent only when the owner has narrowed this node's own remote remedy — an empty tools, or one without command. It names what is off and says plainly that published terminal services are refused along with the tools, so re-enabling is an on-device job

The fs block reports the shared drive on this node:

FieldDescription
typeThe backend that actually mounted the drive: nfs, fuse, winfsp or webdav. Empty when nothing is mounted
mountWhere it is mounted. This is the path in use, which on Windows is not always the configured drive letter — a letter already holding a foreign drive is left alone and the mount moves to the first free one
mountedThe backend's own liveness answer, not a guess from the mount table. A mount must fail three consecutive probes, about a minute apart, before the agent tears it down and remounts
selectedThe fs config key in effect: auto unless the backend is pinned. Agent 0.4.57+
backendsOne line per backend, in this platform's preference order, best-first, saying whether it could serve on this host and, when it could not, what is missing and what to install. From agent 0.4.65 it holds only the backends that can run on this platform at all, so a Mac lists nfs and webdav (in that order from agent 0.4.66; 0.4.65 alone had webdav first) and a Windows node winfsp and webdav. A backend that could serve but is not finished enough for auto to choose it — winfsp, whose locks are node-local and whose file creates are slow — says both halves and names the pin that would use it. That reason has been rewritten as each item was closed: agent 0.4.74 alone also named case-sensitivity, which 0.4.74 itself had fixed and 0.4.75 took off the line; through 0.4.72 it named a write path that lost data, already fixed in 0.4.69; and before that one that had never been exercised on a real mount. From agent 0.4.73 a WinFsp older than 1.10 reads as unavailable here, naming the version found and the upgrade, rather than as a backend the node could mount with. Reported even when nothing is mounted, which is when it matters most. From agent 0.4.70 a Linux node's nfs line is about the kernel's client alone, since the mount helper package is no longer required there — see what nfs needs on a host. Agent 0.4.57+
errorWhy there is no mount, when there is none — a pinned backend the host cannot serve, a failed mount, a backend that declined. Empty while mounted and empty before the first attempt, so its absence means nothing has gone wrong yet rather than that nothing is wrong. Without it the block contradicts itself: mounted: false beside a backend reported as available. From agent 0.4.70 an nfs mount point that is busy but is no longer a mount names the processes holding it, which is what must exit before the drive can come back; from agent 0.4.75 that answer comes from webdav too, where a webdav mount previously reported the wedge as a decline and left this field empty, and from agent 0.4.78 from fuse, the leading backend on Linux. winfsp is the exception and stays one: a refused WinFsp mount reports no reason at all, so this field is empty there whatever went wrong. Agent 0.4.59+

The network.dns field reports how overlay hostnames resolve on this host:

ValueMeaning
localThe agent's built-in resolver answers overlay names on this node — resolution stays on the box and peer names resolve normally.
unavailableThe agent could not configure DNS on this host, so overlay names do not resolve here. You can still reach peers by their overlay IP (see nobgp status targets), and all other functionality is unaffected.

The network.ipv6 field is true when the host holds a usable global IPv6 address, which is what lets the QUIC transport attempt an IPv6 connection. A false value explains why a node only ever connects over IPv4: there is no usable global IPv6 source on this host (a private or link-local–only address does not count).

On macOS and Windows, the network block also includes a route field reporting overlay datapath route health:

ValueMeaning
okThe kernel routes the overlay slice through the noBGP TUN interface — the datapath is healthy.
missingOverlay-bound packets would escape via another interface instead of the TUN, so the node is silently unreachable on the overlay. What to do about it depends on the platform — see below.

On macOS, missing means the route was lost (typically an upgrade-restart race between the old and new TUN). The agent re-installs it automatically within about a minute; if a missing value persists, run sudo nobgp service restart.

On Windows, the overlay route is the TUN adapter's own connected route, so it cannot simply be lost — missing means another product's route is winning, almost always a full-tunnel VPN client. The agent deliberately does not try to repair this: it cannot outbid the other route, and deleting someone else's route is not its call. Fix it on the VPN side (split-tunnel, or exclude 100.64.0.0/10) — see Security software and VPNs.

The field is omitted on Linux, where the overlay route cannot be stranded the same way, and on any platform where the probe itself could not run.

Peer targets

The network.targets list shows the peers this node currently holds a live overlay address for — one row per peer name, with the address your local applications use to reach it.

FieldDescription
nameThe peer's name
node_idThe peer's node ID — the identifier that is valid everywhere, and what every MCP tool takes as a target. Omitted for a target with no node behind it
addressThe overlay address to use for this peer from this node. Unlike node_id this is node-local: each node mints its own handles, so the same peer has a different address on every node and an address carried elsewhere points at the wrong machine
localtrue when the target is served by this node itself

Only peers present in the most recent directory update from the router are listed. When a peer goes offline (or is deleted), it drops out of targets on the next update — but its overlay address is reserved, not released: the agent keeps the address held for that name so a returning peer gets the same address back, and any app still holding the old answer keeps working. A reserved address is not shown in nobgp status because the node is no longer known to be reachable. Reserved addresses are released after 7 days without the router vouching for the name.

If a name a peer used to serve is republished on this node, targets shows the local row for it, and the peer's reserved address is released as soon as the router confirms the peer no longer advertises the name — immediately when that peer is online, on its return otherwise.

Name lookups behave the same way — see nobgp resolve.

Peer sessions

The network.sessions list shows the live mesh sessions this node currently holds to its peers. Idle sessions that have been recycled are not listed.

FieldDescription
node_idThe peer node's ID
nameThe peer's name, resolved from the mesh directory (omitted if unknown)
targetThe target this session carries traffic for
encryptedtrue if the session's data channel is encrypted
compressedtrue if the session's data channel is compressed

Every session is scoped to a single target, so a peer may appear on more than one row — one per target you are actively communicating with.

Probing reachability

Pass --ping (or -p) to send one ICMP echo to each target and report whether it responded and how long the round trip took. Reachability is an end-to-end test: the probe routes through the overlay mesh to the peer and back. Because the first echo to a cold peer triggers mesh-session setup, the probe allows up to a few seconds per unresponsive target before giving up.

nobgp status --ping

When probing is enabled, each target gains two fields:

FieldDescription
reachabletrue if the target answered the ICMP echo, false otherwise
rtt_msRound-trip time in whole milliseconds (integer); present only when reachable is true
targets:
- name: web-server
node_id: "770e8400-e29b-41d4-a716-446655440222"
address: "100.64.16.2"
local: false
reachable: true
rtt_ms: 12
- name: db-primary
node_id: "660e8400-e29b-41d4-a716-446655440111"
address: "100.64.16.3"
local: false
reachable: false
note

Probing each target requires raw-socket privileges (CAP_NET_RAW, root, or Administrator). If the probe cannot run, targets are returned without the reachable/rtt_ms fields rather than being reported as down.

Environment interference

Other software on the machine is the most common cause of a node that registers fine but behaves oddly. The environment block names what the agent found:

environment:
tunnels:
- "Norton VPN Wintun (169.254.12.7)"
security_products:
- "Norton 360"
FieldDescription
tunnelsAnother product's active VPN or tunnel adapter, with the address it currently holds — the usual reason overlay traffic goes missing while the control channel looks healthy. Only adapters holding a real address are listed; the agent's own interface, sibling profiles, and other overlays inside 100.64.0.0/10 are excluded, as are the operating system's own transition adapters (Teredo, ISATAP, 6to4) and PPP-based uplinks such as DSL or mobile broadband, which are the machine's own internet connection rather than someone else's tunnel
security_productsThird-party antivirus or firewall registered with the operating system. Windows reads Security Center; macOS lists activated security and network system extensions; Linux reports nothing here

The block is deviation-only: when nothing is detected it is omitted from the output entirely, so its absence on a healthy machine is the expected result, not a missing field. Stock Windows Defender is filtered out of security_products for the same reason — this is a report of what differs from a normal machine, not an inventory of installed software.

The list is answered from a cache refreshed in the background, so nobgp status keeps answering promptly even when the operating system's own query is slow, and a product installed moments ago can take a few minutes to appear.

See Security software and VPNs for what to do about each kind of interference.


nobgp resolve

Resolve a peer name to its overlay address by asking the running agent directly. The answer comes straight from the mesh directory, bypassing the operating system's DNS resolver — so you get the deterministic overlay address even on hosts where a LAN search domain shadows bare names or where overlay DNS is otherwise unavailable (see nobgp status).

Usage:

nobgp resolve <name>

The name may be a bare peer label (web-server) or the node's own fully qualified overlay name. The resolved address is printed alone on stdout, so it composes cleanly into scripts:

ssh admin@$(nobgp resolve web-server)

Options:

OptionShortDescription
--json-jOutput as JSON (adds the node_id, fqdn and found fields)

Examples:

# Print the overlay address for a peer
nobgp resolve web-server
# 100.64.16.2

# JSON output with the canonical FQDN
nobgp resolve web-server --json

JSON output:

{
"name": "web-server",
"node_id": "770e8400-e29b-41d4-a716-446655440222",
"address": "100.64.16.2",
"fqdn": "web-server.550e8400-e29b-41d4-a716-446655440000.nobgp.net",
"found": true
}

node_id is the peer's node ID — the identifier that is valid everywhere, and what MCP tools take as a target. The address is node-local by design: the same name resolves to a different address on every other node, so never carry one between machines.

A name that exists in the directory but has no address assigned yet returns "found": true with an empty address — retry shortly. A name that is not part of this node's overlay reports as not found.

Peers that are currently offline. When a peer drops out of the router's directory, the agent keeps its overlay address reserved (see Peer targets) but stops treating it as a served answer. On the next lookup — through nobgp resolve or overlay DNS — the agent asks the router to arbitrate:

  • The peer still exists (it is merely offline): you get its address back — the same one it had before.
  • The peer was deleted: the name reports as not found immediately, rather than waiting out the 7-day reservation window.
  • The router cannot be reached: the reserved address is served anyway, so a transient control-channel blip never makes a known peer disappear from DNS.

A name that has moved onto this node resolves to the local address as soon as the name is published here, without waiting for the peer's reservation to be settled — see Overlay addressing.

Exit Codes:

  • 0 - The name resolved to an address
  • 1 - The name was not found, is not in this node's overlay, or no agent is running
note

Like nobgp status, this command queries the agent over its local API socket, which is owned by root — so it elevates the same way, re-executing itself under sudo on Linux and macOS.


nobgp mcp

Connect this node's local MCP server to Claude Desktop or Claude Code.

A registered node can run an MCP server on 127.0.0.1, started by an operator's node_grant (see Local MCP Server). nobgp mcp is how you register that server with the machine's Claude clients, check that it is answering, and take the registrations back out again.

Usage:

nobgp mcp <subcommand> [options]

Persistent option (available on every subcommand):

OptionTypeDescription
--profilestringAgent profile to bridge to (default: default)

install

Register this node with the Claude clients installed on the machine.

nobgp mcp install [--yes]

Options:

OptionShortDescription
--yes-ySkip the confirmation prompt (for scripts)

The command names the account whose Claude configuration it is about to write and asks for confirmation. It only touches configuration files that already exist, and a file it cannot parse is left alone and reported rather than replaced.

Before writing anything it makes sure there is a server to register — it flips no switch of its own. It starts the agent service if it isn't running, fails fast on the state waiting cannot fix (no node_grant yet — grant first, install second), then waits up to 30 seconds for an endpoint that answers. If none does, the install fails instead of registering a port nobody holds.

After registering it opens one throwaway session against the live endpoint and reports how many router tools the grant exposes — 15 at the observe tier, 22 at manage, on top of the node's own two local tools. A count it cannot obtain right now is reported as such, not as a missing grant.

Requires root: on Linux and macOS the command re-executes itself under sudo; on Windows run it from an Administrator terminal.

Example:

$ nobgp mcp install
Root privileges required, re-executing with sudo...
Installing the noBGP MCP server for user "alice" (profile "default"):
Claude Desktop /Users/alice/Library/Application Support/Claude/claude_desktop_config.json
Claude Code /Users/alice/.claude.json

Continue? [Y/n] y
registered with Claude Desktop (...)
registered with Claude Code (/Users/alice/.claude.json)

MCP server ready at http://127.0.0.1:51843/mcp
This node holds an operator-granted role — 15 network tools available through the proxy.

Restart Claude to pick this up — it reads its server list only at launch.

The entry it writes carries the endpoint and bearer token — a direct HTTP entry for Claude Code, a nobgp mcp stdio bridge with the values in its env for Claude Desktop. See what gets written.

That token belongs to the profile, not to the account: running the install for a second account on the machine hands out the same credential, so access can't be withdrawn from one account alone. See one token per node.

It configures your account's Claude, not root's

This writes a per-user file, so the wrong account succeeds silently. Under sudo it resolves your real account (SUDO_USER), writes that account's configuration and hands the file back to you. Run as root with no SUDO_USER and it configures root's Claude, so it warns and the prompt defaults to "no".

uninstall

Remove this node's entry from every Claude configuration on the machine.

nobgp mcp uninstall

Everything else in those Claude files is left untouched, and so is the agent: the server keeps running, and the profile keeps its mcp-port and mcp-token — they are the running server's state, and other accounts on the machine may still be registered against them. To stop the server itself, node_revoke the node's grant at the router — the only control that starts or stops it.

No root required — this touches only your own account's Claude configuration. Restart Claude afterwards.

status

Show the local MCP endpoint and where it is registered.

nobgp mcp status

Example:

$ nobgp mcp status
server: http://127.0.0.1:51843/mcp (responding, per the Claude Desktop registration)
Claude Desktop: registered (/Users/alice/Library/Application Support/Claude/claude_desktop_config.json)
Claude Code: not registered (/Users/alice/.claude.json)

No root required: the endpoint is read out of the registration in your own Claude configuration, falling back to the agent's own root-owned state — the line names whichever it used. It then probes that endpoint rather than trusting it, so a registration pointing at a port the agent no longer holds reports — NOT responding (re-run nobgp mcp install to repair it) instead of a false "running". server: unknown means nothing is registered yet and the agent's own state isn't readable by this user.

It reports the registration, not the grant. For whether the router has granted this node a role — the one thing that starts the server — use the mcp block of nobgp status.


nobgp notify

Publish an event into the subscription this process belongs to. Aliased as nobgp publish.

This is how a script dispatched by command_subscribe reports its own progress: the agent exports $NOBGP_SUBSCRIPTION into every command it runs for the event bus, so the script never has to be told an identifier.

Usage:

nobgp notify [key=value ...] | [message] | [json] | -

Options:

OptionDescription
--subscriptionSubscription id (defaults to $NOBGP_SUBSCRIPTION) — for a process the agent did not start
--statusok (the default) is a progress report and ends nothing. Any other value (completed, failed, timeout, cancelled) is this node's last word: it settles this node's share of the subscription and never touches the work running on other nodes

Payload forms. The shape is chosen by what you actually wrote, so a shell script never has to hand-assemble JSON:

# key=value pairs become a typed JSON object —
# numbers and true/false are not quoted
nobgp notify status=ok count=3 host="$(hostname)"

# a plain message is sent verbatim
nobgp notify "migration finished"

# JSON is passed through untouched
nobgp notify '{"stage":"schema","ok":true}'

# stdin, same rules
./migrate.sh | nobgp notify -

# settle this node's share of the dispatch
nobgp notify --status failed "backup aborted: disk full"

The key=value form applies only when every argument is one, so a message that happens to contain an equals sign is not silently reinterpreted.

Publishing nothing is an error

Passing no payload and no --status fails with exit 1 and the message nothing to publish. The usual cause is a pipe missing its -:

./migrate.sh | nobgp notify # ✗ exit 1 — stdin is only read when asked
./migrate.sh | nobgp notify - # ✓

A bare nobgp notify --status completed is still valid — settling this node's share of the subscription is the message.

In a dispatched script:

#!/bin/sh
nobgp notify phase=start
pg_dump mydb | gzip > /backups/db.sql.gz
nobgp notify phase=done bytes="$(stat -c %s /backups/db.sql.gz)"
Publishing to nobody is not an error

With no subscription in context, or one that has already closed, nothing is sent and the exit status is still 0. A script under the bus is not supposed to fail because the reader went away.

Exit Codes:

  • 0 - Published, or there was no subscription to publish into
  • 1 - Nothing to publish (no payload and no --status), no running agent to publish through, the payload was rejected, or the agent is too old for this command

nobgp events

Show the events this node is publishing to the network event bus.

Usage:

nobgp events [-f]

Options:

OptionShortDescription
--follow-fKeep streaming live events (Ctrl-C to stop)

Without --follow, the command prints the node's recent events (the newest 256 are kept) and exits. With --follow, it prints those and then keeps streaming.

# What has this node reported recently?
nobgp events

# Watch it live while testing a subscription
nobgp events -f
This is the emitting view

nobgp events shows what this node is publishing — the half that is invisible from the machine producing it. It is not a subscriber feed: subscribers read events at the router, where the queue lives, with event_tail. Use this to confirm a node is actually reporting what you expect.


nobgp list

List all available profiles.

Usage:

nobgp list

The command itself needs no root, but the directory it reads is root's. If it cannot be read, nobgp list exits 1 naming the directory and the reason rather than reporting No profiles found. — an empty list from a permission error is not the same claim as a node with no profiles, and on a fully configured node the second one is simply false:

$ nobgp list
cannot read the profile directory /etc/nobgp: open /etc/nobgp: permission denied
Run as root (sudo nobgp list) — this is not the same as having no profiles.

nobgp show

Show the configuration and status details of a profile.

Usage:

nobgp show [profile]

If no profile is specified, shows the default profile.

Alongside the profile's configured settings and identity keys, the output includes the QUIC, IPv6 and MCP state that is otherwise machine-managed. Each line below appears exactly once, and the settings that resolution can override are shown as resolved rather than as the raw value the flag or config file holds — router, insecure and quic-router all follow that rule:

LineDescription
quic-routerYour QUIC endpoint override, printed only when it is what the QUIC leg actually dials — that is, when quic-endpoint below reports (override). A value sitting in the config that endpoint resolution ignores prints no line at all, so it can never read as an override the next line contradicts with (learned). Older agents wrote a now-retired built-in value into config files, and nothing removes it, so this suppression is what an upgraded node relies on
quic-endpointThe host:port the QUIC leg dials, followed by its source (override, learned, or derived) in parentheses
quic-cert-pinShown only when the pin deviates from the healthy path: none (no pin yet, so this node stays on wss until its next registration delivers one) or provisioned (a NOBGP_QUIC_CERT_PIN value outranking the router's). A normally learned pin prints no line — the (learned) on quic-endpoint already covers it, since both arrive together
quic-udp-blockedShown as true only when the agent has recently observed UDP being blocked on this network
ipv6true when the host holds a usable global IPv6 address (which lets the QUIC transport attempt an IPv6 connection), false otherwise
token-expiredDeviation-only: printed only when the profile's registration token has already expired, as the expiry instant plus the remedy — 2026-07-30T09:14:02Z — re-register with 'nobgp register'. A live token prints no line. Agent 0.4.72+
mcp-portThe loopback port the local MCP server prefers, or any (assigned at start) when none has been recorded yet
allow-toolsThe capability domains this node serves, or none (nothing permitted) for an empty list — a value an operator must never read as "not configured"
allow-rootsThe filesystem roots the fs tools and the fs event source may touch, rendered the same way
allow-adminWhether this node permits execution and file access as root / Administrator
fsThe shared drive backend this profile is pinned to — auto (the default), off, nfs, fuse, winfsp or webdav. Printed even at its default, since it decides whether the node mounts anything at all. It shows what resolution decided rather than the raw value: a typo prints auto ("nsf" is not a valid value; ignored), because an unparseable value falls back to auto rather than taking the node's filesystem away
fs-backendThe backend that actually mounted and where it landed — nfs (mounted at /Volumes/nobgp) — or webdav (mount at /mnt/nobgp is not responding) when the mount is still in the kernel's table but the backend's own probe says it is dead. Agent 0.4.61+
fs-errorWhy there is no mount, when there is none. Deviation-only, so a healthy node prints no line. Agent 0.4.61+

The QUIC lines mirror the router block of nobgp status; the three allow- lines mirror its allow block and are printed even at their permissive defaults, since a setting you cannot read back is one nobody trusts. The three fs lines sit last and read as one block: fs is configuration and appears whether or not an agent is running, while fs-backend and fs-error are the running agent's answer and are silent when no agent responds — the full picture, including every backend this platform could use and why each can or cannot serve, is the fs block of nobgp status. See nobgp status for the full meaning of each value. The mcp-port line is the persisted profile view — it does not say whether the router has granted this node a role, which is what actually starts the server. For that, and for a live port, use the mcp block of nobgp status or nobgp mcp status.

The last two lines are the running agent's answer, not the config file's, so they are read from the agent over its local socket and are silent when no agent is running — which agent is up is nobgp status's question. They are never both silent while something is wrong: a node with no mount says why on fs-error, and both are absent only before the first mount attempt or under fs: off, where the fs line above already says everything that is true. They mirror fs.type/fs.mount/fs.mounted and fs.error in nobgp status, which additionally lists every backend and whether it could serve.

note

Like nobgp status and nobgp config, nobgp show reads the profile's root-owned configuration and therefore requires root privileges. On Linux and macOS it automatically re-executes itself with sudo (prompting for your password if needed); on Windows it must be run from an Administrator terminal. Without these privileges the command would fall back to the built-in default values rather than the profile's actual settings.


nobgp remove

Remove a profile and all associated files: its configuration (.yml), identity (.key, .jwt), the profile's local API socket, and the per-profile state and runtime files the agent keeps beside them (remembered overlay addresses, cached transport state, PID and lock files).

Usage:

nobgp remove [profile] [options]

Options:

OptionDescription
--forceForce removal without confirmation

Every path it deletes is root-owned, so — like nobgp status, nobgp show and nobgp config — the command requires root and re-executes itself under sudo on Linux and macOS; on Windows run it from an Administrator terminal. Other profiles on the machine keep their own files and keep running.


nobgp uninstall

Remove the noBGP agent from the machine. This stops and removes the system service, deletes the installed binaries, and clears runtime files (logs, PID/socket, locks). Requires root / Administrator.

By default, nobgp uninstall keeps the profile's identity files (.key, .jwt, .yml) so that reinstalling the agent later re-enrolls the machine as the same node. Pass --purge to also remove the configuration directory — this deletes the node's identity, so a subsequent reinstall enrolls a brand-new node.

Usage:

sudo nobgp uninstall [options]

Options:

OptionShortDescription
--purgeAlso remove all configuration and credentials (identity is gone; a reinstall enrolls a new node)
--force-fSkip the confirmation prompt

Examples:

# Remove the agent but keep the node identity for a later reinstall
sudo nobgp uninstall

# Fully remove the agent, including configuration and credentials
sudo nobgp uninstall --purge

# Skip the confirmation prompt (useful in scripts)
sudo nobgp uninstall --purge --force
note

Unless --force is given, the command asks for confirmation before removing anything. On Windows the running executable cannot delete itself, so binary removal is scheduled to run just after the process exits.

tip

nobgp uninstall makes removal self-sufficient — you don't need to run nobgp service uninstall or a package-manager uninstall first. If you installed via a package manager and prefer the tidy path, apt purge nobgp / brew uninstall nobgp / etc. still work.


nobgp upgrade

Upgrade the agent to its release-channel version (default) or a specific version.

Without a version, nobgp upgrade installs this node's release-channel version — the same version automatic upgrades install. On a machine that isn't registered, the stable channel is used. A test or pre-release build is never installed unless you name its version explicitly.

Anything off the safe path asks for confirmation first (with a "no" default), and there are two kinds of confirmation answered by two different flags:

  • A backwards install — a downgrade, or a reinstall of the version already installed — asks a force prompt. Answer it non-interactively with -f (--force). -y alone does not authorize it: nobgp upgrade <older-version> -y fails outright, so a stray -y in a script can never downgrade a node.
  • A forward off-path install — a version ahead of the node's channel, or one that can't be verified against the channel — warns with a "no" default. Either -y or -f answers it.

-f answers every prompt (it covers everything -y does); -y answers only the safe/forward ones. Running bare nobgp upgrade -f converges a node that is ahead of its channel back down to the channel version — the manual convergence path for nodes with auto-upgrade disabled.

Usage:

nobgp upgrade [version] [options]

Examples:

# Upgrade to this node's channel version
sudo nobgp upgrade

# Same, without confirmation prompts
sudo nobgp upgrade -y

# Upgrade to a specific version (confirms if off-channel)
sudo nobgp upgrade 1.2.3

# Downgrade or reinstall without prompts (requires -f, not -y)
sudo nobgp upgrade 1.2.3 -f

# Converge an ahead-of-channel node back down to its channel version
sudo nobgp upgrade -f

Options:

OptionShortDescription
--yes-yNon-interactive; answer yes to the safe/forward prompts (a version ahead of the channel, or one that can't be verified). Does not authorize a downgrade or reinstall.
--force-fNon-interactive; answer yes to every prompt, including the force prompt for a downgrade or reinstall. Covers everything -y does.

If the named version has no package for this machine, the upgrade stops with version X is not published for <platform>/<arch> — usually a typo or a release that was never cut. Run nobgp upgrade without a version to install the one your channel is on. The same message for a channel version means the release is probably still uploading; retry in a few minutes.

That check runs before any confirmation prompt: the command asks the download origin whether the package exists, so a version that isn't there is rejected without first walking you through a downgrade or off-channel warning. Only a definitive answer counts — an unreachable origin, a timeout or no network at all lets the upgrade proceed, so recovering an offline node with sudo nobgp upgrade <version> -f never stalls on the check.


nobgp service

Manage the noBGP agent as a system service.

note

Most service commands depend on a service manager and do not work in Docker containers. The underlying service manager differs by platform: systemd on most Linux distributions, OpenRC on Alpine Linux, procd on OpenWRT, launchd on macOS, and Windows Service Manager on Windows. The exception is nobgp service logs, which also works for a containerized agent running in the foreground by reading the agent's mirrored log file.

Usage:

nobgp service <subcommand>

Subcommands:

install

Install the noBGP agent as a system service.

sudo nobgp service install

Registers the agent as a system service and enables it to start on boot. On Linux this creates a systemd unit (or OpenRC/procd script depending on the init system); on macOS a launchd plist; on Windows a Windows Service.

uninstall

Remove the noBGP system service.

sudo nobgp service uninstall

Stops the service, disables it, and removes the service unit file.

start

Start the noBGP service.

sudo nobgp service start

stop

Stop the noBGP service.

sudo nobgp service stop

restart

Restart the noBGP service.

sudo nobgp service restart

Equivalent to stop followed by start. If the service is currently stopped, restart simply starts it rather than failing.

status

Check the status of the noBGP service.

sudo nobgp service status

Exit codes:

  • 0 - Service is running
  • 1 - Service is stopped
  • 2 - Service is not installed

logs

View service logs.

sudo nobgp service logs

Like the rest of nobgp service, this requires root — on Linux and macOS it re-executes itself under sudo, and on Windows it must be run from an Administrator terminal (see Root privileges). The systemd journal only shows an unprivileged caller their own messages, so a non-root run returned a journal slice with none of the agent's lines in it and nothing saying they were missing rather than absent.

Options:

OptionShortDefaultDescription
--lines-n100Number of lines to show
--follow-ffalseFollow log output in real-time
# Show last 50 lines
sudo nobgp service logs -n 50

# Follow logs in real-time
sudo nobgp service logs -f

The command reads from the platform's native log source (systemd journal, OpenRC/launchd log files, or syslog). When no service manager captures the agent's output — inside a container running in the foreground as PID 1, or on Windows, where the Service Control Manager captures nothing — the agent mirrors its output to an on-disk log file (<config-dir>/nobgp.log, capped at 10 MiB with one rotated generation), so nobgp service logs works there too.

note

On Windows the Service Control Manager does not capture a service's output, so the agent mirrors its logs to C:\ProgramData\nobgp\nobgp.log (capped at ~10 MiB with one rotated .1 generation). nobgp service logs reads that file back with PowerShell Get-Content, and --follow is supported (via -Wait). The same on-disk logfile is used when the agent runs in the foreground or inside a container, where no service manager is available to capture its output.


nobgp version

Display the noBGP agent version.

Usage:

nobgp version

Example output:

nobgp version 1.0.0

nobgp help

Display help information.

Usage:

nobgp help [command]

Examples:

# General help
nobgp help

# Help for specific command
nobgp help agent
nobgp help config
nobgp help service

Configuration File

Location

The configuration file path depends on the platform:

PlatformPath
Linux/etc/nobgp/default.yml
macOS/usr/local/etc/nobgp/default.yml
WindowsC:\ProgramData\nobgp\default.yml

Format

YAML, and the agent owns the file — it rewrites it whenever it records something for itself: the port it bound for the local MCP server, the overlay slice it picked, the QUIC endpoint the router told it about, from agent 0.4.59 the loopback port its NFS server bound (nfs-port, so an existing mount survives a restart), and from agent 0.4.78 where the drive was last mounted (last-mount-point, so a mount left behind by a killed agent can still be found).

The agent also watches this file, so an edit you make is applied without a restart. From agent 0.4.78 it tells its own write apart from yours: through 0.4.77 a value it recorded after startup — nfs-port and mcp-port — read back to it as your edit and restarted the agent for a reload, which on a node that had just mounted tore the drive down again about five seconds later. An edit of yours is still picked up exactly as before.

Since agent 0.4.35 a key is written only when its value differs from the default. Everything else is emitted as a commented line, so the file reads as the choices someone actually made rather than a frozen snapshot of every default the release happened to ship with:

domain: a1b2c3d4-....nobgp.net
overlay-cidr: 100.64.0.0/20
mcp-port: 51843
mcp-token: ...
user: deploy

## Defaults. Shown for reference, NOT read. Uncomment a line (single #)
## to pin that value; leave it commented and the agent tracks the
## default, which may change between releases.
# auto-upgrade: true
# compress: true
# encrypt: true
# fs: auto
# fs-cache-ttl: 30s
# interface: auto
# log-level: info
# mount: /mnt/nobgp
# router: router.nobgp.com
# transport: auto

## event-sources was migrated into allow-tools and is no longer read.
## event-watch-roots was migrated into allow-roots and is no longer read.

(Abbreviated — the defaults block lists every key the agent knows, one per line, in alphabetical order.)

Uncomment one of the single-# lines to pin that value: the agent then treats it as your choice and keeps writing it. Leave it commented and the node tracks the default, including a default that changes in a later release. Explanatory text uses ##, so uncommenting a whole block cannot drag prose into the YAML.

Two consequences worth knowing:

  • The file is agent-generated. Comments you add by hand do not survive the next time the agent records something, which can be minutes later.
  • Keys the agent no longer reads are swept out on that rewrite, with a note naming the successor where there is one.

Older agents wrote every default into the file as an explicit setting the first time nobgp config ran, which is why a node installed before 0.4.35 lists keys nobody chose — and why a value it froze then would have stayed pinned through a later change of default. Since agent 0.4.36 the agent tidies that when it starts, so an upgraded node is converted on its first restart; a 0.4.35 agent rewrote the file only the next time it had something of its own to record, which on a settled node may never come. Nothing changes about the effective settings, and the file is replaced atomically, so an upgrade restart or a power cut in the middle of a write cannot leave a node without its router, its overlay slice or its MCP token.

The rewrite happens only when the contents would actually differ. A file that already says the right thing is left alone — same bytes, same timestamp — so the startup sweep costs a settled node nothing, and an agent running with no config file at all (the env-only container shape) has nothing to sweep and says nothing about it.

note

The registration-key and node-name fields are only used during initial registration and are automatically removed from the config file after successful registration. Agent identity is stored separately in .key and .jwt files alongside the config.

Local MCP server keys

These control the MCP server the agent runs on 127.0.0.1 — see Local MCP Server.

mcp-port: 51843 # port to prefer; the agent writes back whatever it binds
mcp-token: "..." # bearer token; minted and written back by the agent, one per profile

What starts the local server is a node_grant from an Owner or Admin at the router: an ungranted node serves nothing on 127.0.0.1, and a revoked grant stops the server within seconds. A grant does not expire on its own — it stands until node_revoke takes it back. That grant is the only control — there is no on-device key that turns the endpoint on or off, so an operator can provision the fleet from the app with nothing to run on each box. What the node will serve once granted is bounded by the capability keys below.

mcp-port and mcp-token are agent-written state, not settings you edit — nobgp mcp install reads them as root and copies them into the Claude configuration for you. They persist across a revoked grant, so a re-grant rebinds the same endpoint and registered clients keep working.

Capability keys

These are the node owner's say over what this machine serves — for event-bus sources, for the direct command / fs_* tools, and for published terminal services. All three are permissive by default; narrowing them is how you take capability away from a box — see Monitoring & Events.

allow-tools: # capability domains served here: fs, command
- fs
- command
allow-roots: # where the fs domain may reach
- /
allow-admin: true # false refuses elevated requests instead of downgrading them

allow-roots matches a path against each root after resolving symlinks, so a link cannot smuggle a call outside them, and /etc does not admit /etcetera. The whole-filesystem value is / — or \, and either spelling is accepted on either platform, so the permissive default means everything on Windows too. Narrower Windows roots are drive-qualified, e.g.:

allow-roots:
- C:\ProgramData\app
- D:\logs

One tree is off-limits whatever allow-roots says: the agent's own configuration directory (/etc/nobgp, /usr/local/etc/nobgp on macOS, C:\ProgramData\nobgp on Windows). The fs tools and the fs event source refuse every path inside it — widening allow-roots does not open it and neither does allow-admin: true, and a symlink pointing into it is resolved and refused the same way. That directory holds the node's key and JWT and the config these keys are read from, so serving it would put both one fs_read away on a node still carrying the permissive defaults. The command domain is unaffected: a session that runs as an account with permission to read those files still can, and allow-roots does not confine a command's workdir either — the command string is unconstrained, so a confined working directory would refuse nothing that cd does not reach anyway.

A tool that walksfs_list --recursive, fs_glob, fs_grep — asks the same question at every directory it descends into and prunes the subtree on a refusal. Agents before 0.4.37 vetted only the root they were handed, so a recursive walk starting at a permitted path reached the agent's own directory beneath it; fs_grep was not checked against allow-roots at all, since its roots travel in a different field from the one the guard read.

allow-tools: [] serves neither domain. The values are capability domains, not tool names — a value that isn't one (fs_read, commands) is ignored with a log line naming it, and if nothing valid is left the permissive default is restored, so a typo never leaves a NAT'd box serving nothing. These keys were called event-sources and event-watch-roots in earlier agents, where they were empty by default; an upgraded agent migrates the old names without tightening anything the node already served.

allow-admin: false drops command sessions, dispatched commands and file operations to the user account, and refuses anything that would run as uid 0 outright rather than downgrading it.

The user key is the other half of that, from agent 0.4.41. A node resolves what an unelevated call would run as — the configured account if there is one, its own ambient identity otherwise — and refuses the call as failed_precondition when that resolves to uid 0, rather than running the work as root. That covers a node with no user whose agent runs as root (a service install, a bare-root container, and a Windows node offering only LocalSystem, which reports as uid 0), a user that no longer resolves, and user: root spelled out. Either remedy clears it: nobgp config --user <account> on the box, or the caller passing admin. An agent already running unprivileged is unaffected — it serves unelevated calls as itself, since elevating there would reach the same uid. So on a container or a bare-root install, allow-admin: false turns remote work off rather than narrowing it: both paths refuse. See Unelevated never means root.

For the fs_* / file tools it is a genuine privilege drop, not an authorization check standing in for one: the agent drops privilege before opening the file, so a file written unelevated is owned by that account, and the destination of an atomic write is checked against that account as well. A user that no longer resolves — deleted, or a config copied off another machine — is refused rather than silently falling back to the agent's ambient identity, which on a service install is root. The refusal names the account it could not resolve, so a deleted account reads as what it is instead of turning every later call into an unannounced root call.

Assuming another account is the superuser's privilege, so an agent that is not running as root cannot do it. On such a node — a foreground nobgp agent started by an ordinary user, say — a file operation that would run as user is refused naming the reason, rather than running as the agent's own account instead. Either run the agent as root or clear user. A service install runs as root, so this does not arise there.

What these keys govern

Every remote path onto the node: the MCP tools and event-bus sources a node_grant reaches, and a published terminal service.

Services were exempt before agent 0.4.33, on the reasoning that a terminal keeps a locked-down node recoverable. The exemption also meant allow-admin: false could not do the thing its name promises — a service published to run as root handed out root on a node that had refused exactly that — so the exemption is gone and the recovery advice moved to the moment of the change: nobgp config warns when a write leaves the node serving no commands or no file access, and asks before turning off superuser execution on a node where that removes remote execution entirely.

Plan the way back before you narrow a node

allow-tools without command refuses the browser terminal too, and allow-admin: false on a node with no usable user set (a container, a bare-root install, a Windows node that captured no install account) refuses every session outright — the elevated path by the veto, the unelevated one because it would resolve to root. A Windows node that has an account stays operable as that account from agent 0.4.46; on 0.4.44 and 0.4.45 it keeps its file tools and loses execution. On those nodes the only way back is console, RDP or SSH access to the machine — so on a box behind CGNAT, set user to a working unprivileged account rather than relying on a terminal service surviving the change.

allow-admin: false on Windows is an off switch for execution

A Windows node offers one identity by default, so there is no unprivileged account to drop to. Since agent 0.4.33 the setting is still honoured there — as the only answer the platform can give: false refuses remote execution on that node entirely rather than being ignored. Earlier agents logged that they could not apply it and kept admin enabled.

It stops being true on a Windows node that has a user, once its agent reaches 0.4.46: allow-admin: false there drops both file operations and execution to that account, exactly as on Unix, so the veto narrows the node instead of switching it off. On 0.4.44 and 0.4.45 only the file half dropped — fs_* and file ran as the account while execution was still refused.

The second identity on Windows

Since agent 0.4.44 a configured user gives a Windows node the second identity Unix nodes have — no separate key, the same one-switch rule as every other platform. The account is obtained from the local security authority by name and impersonated for the operation: nothing prompts for a password and no credential is stored on the machine, which is why an unattended install can capture one.

Since agent 0.4.46 it covers every surface, on the same one-switch rule as Unix:

Operationadmin: false, agent 0.4.44–0.4.45admin: false, agent 0.4.46+
file and the fs_* toolsruns as user, a genuine privilege dropruns as user
command, terminal sessions, dispatched bus commandsrefused, failed_preconditionruns as user, with that account's own profile and environment

Through 0.4.45 execution was refused rather than served because a Windows spawn still happened in the service's own context — serving it would have run as LocalSystem while the node had just answered with the configured account. The refusal named the way out: admin: true, to run as LocalSystem deliberately.

Two consequences of running a process as that account. The first unelevated command for an account that has never signed in at the machine creates its profile directory (C:\Users\<account>), which nobgp uninstall leaves in place; file operations need no profile and create nothing. And because the identity is obtained by name with no stored password, it carries no network credentials — a command that reads a file share, or reaches anything else that authenticates it over the network, works with admin: true and fails with admin: false.

An account in the local Administrators group is the common case, since the installer captures the interactive desktop user. Such an account gets an unfiltered admin token, so admin: false there drops from LocalSystem to Administrator and no further. It is not refused — the two identities stay distinct, unlike a Unix user: root — and it is reported both ways: nobgp status carries allow.unelevated_is_admin on the box (agent 0.4.46+), and network_directory reports it fleet-wide as info.user_is_admin.

Agent 0.4.46 also strips such a token of its Windows privileges before anything uses it. Without that, an administrative account's token carries SeBackupPrivilege enabled, and a read taken with backup privilege never consults the file's ACL — so on 0.4.44 and 0.4.45 an unelevated fs_read on such a node could read files the account's own permissions did not allow. It now reaches exactly what that account is permitted to reach.

A user the node cannot obtain a token for leaves it with one identity and logs why, rather than reporting an account it cannot become. Execution fails closed for the same reason: a token, profile or environment that cannot be built refuses the call rather than running it in the service's context.

Agent 0.4.43 shipped this behind a windows-user-identity key. That key is retired and an upgrading agent removes it from the config file; a Windows node that recorded an install account under 0.4.43 gains the second identity when it upgrades.

See A second identity on Windows.

Local API socket keys

On Unix the agent's local API socket is 0600, owned by the configured user — which registration sets to the installing account, so dispatched commands running as that account can already call nobgp notify. It falls back to root-owned when user is unset or the account no longer exists. These keys open it to a group as well, so an additional user-land consumer can reach the event stream:

api-socket-group: nobgp # group name or numeric gid
api-socket-mode: "0660" # octal; only meaningful with a group set

Unlike nobgp status and nobgp resolve, the commands a dispatched script uses — nobgp notify and nobgp events — do not elevate. Run by an account the socket does not admit, they say so specifically ("the agent is running but its API socket denied access"), naming these two keys and running as root as the fixes, rather than sending you to check a daemon that is perfectly healthy.

Filesystem keys

These two decide how — and whether — this node mounts the shared drive, and how stale a directory listing it may show you. They arrived separately: fs in agent 0.4.57, fs-cache-ttl in agent 0.4.66, so a node between those two releases has the first key and not the second.

fs: auto # auto | off | nfs | fuse | webdav | winfsp
fs-cache-ttl: 30s # duration; clamped to [0, 10m]. ⚠ ALWAYS write the unit

fs picks the backend, and it is config-only — there is no flag and no environment variable for it, so sudo nobgp config or an edit of the profile is how it is set. auto is the default and is what every existing profile already does: the agent walks its platform's list and mounts with the first backend that can serve on this machine right now. See Which filesystem you get for the per-platform order and what each backend needs.

  • An explicit value is honoured or refused out loud, never silently swapped. Someone who writes fs: nfs is pinning a backend or diagnosing one, and both are defeated by quietly mounting something else — the pin does not hold, and the diagnosis measures the wrong thing. If the named backend cannot serve on this box, the node mounts nothing and says what is missing.
  • fs: off mounts nothing, deliberately. A node that cannot mount warns and keeps retrying; a node told not to mount does neither. From agent 0.4.78 it also clears a mount an agent that was killed left behind, on Linux and macOS — the kernel is asked what is mounted there and only a mount this agent's own backends make is removed (details). Through 0.4.77 such a mount stayed until someone unmounted it by hand, so on those versions restart the service first and then set the key. Everything else about the node is unaffected.
  • A misspelled value is not a pin. It warns and falls back to auto, because a typo must not take a node's filesystem away. nobgp show prints what resolution decided, so a value that is not in force never reads as one.
  • Changing it takes effect on the next mount. A healthy mount is left alone, so run sudo nobgp service restart after the change.

fs-cache-ttl is a staleness window, not a speed dial: it is how long this node may keep showing you a folder that another node has already changed. It is settable three ways — --fs-cache-ttl, NOBGP_FS_CACHE_TTL, or the key above — and clamped to [0, 10m], with a value past the ceiling reduced to 10m and a negative one to 0.

A bare number means nanoseconds

fs-cache-ttl: 30 is thirty nanoseconds, not thirty seconds — a listing cache that expires before it is read, and a node quietly back on a round trip per lookup. Always write the unit: 30s, 2m.

The agent catches the obvious shape of this: any value below one second that is not exactly 0 is treated as a missing unit, so it warns, tells you to write 30s, and keeps the default rather than acting on it. A value that is unparseable altogether does the same.

0 is a real setting rather than "off": it re-lists every time while still serving unchanged bytes from cache, which is the honest answer for a hot shared tree. On an NFS mount the value is read at mount time, so a change there needs a service restart to take effect.

Shared drive proxy keys

The agent runs a small local proxy for the shared drive on 127.0.0.1:19840, falling back to a port the operating system assigns when that one is taken. It is what lets the machine's own WebDAV client mount the share without knowing anything about noBGP credentials: the proxy stamps the node's token onto every request it forwards to the router. It listens whether or not the drive is currently mounted, and on nodes whose backend is not WebDAV at all. The two cases where it is not started, from agent 0.4.57: fs: off, and a node with no mount point at all (a Windows profile that found no free drive letter). In both the mount is the proxy's only consumer, so leaving a credential-stamping listener up would give away the node's access to the share while serving nothing.

So any local process that can open a loopback socket holds the node's access to everything the drive presents — since agent 0.4.54 that is the node's own storage area as well as each network's share (see What the mount contains) — and on macOS, that reaches past the drwx------ on /Volumes/nobgp. Since agent 0.4.53 the proxy asks the kernel which account is on the other end of each connection, and you can bound it to a set of them:

webdav-proxy-owners: # numeric uids allowed to reach the local proxy
- 0
- 501
  • An empty list — the default — observes rather than allows. The agent logs the uid of the first caller it sees, once per run (WebDAV proxy: peer uid 501 (observe-only; set webdav-proxy-owners to enforce)), and refuses nothing. Which uid the OS WebDAV client connects as is not documented on any platform — macOS mounts through a helper process of its own — so read the uid out of the log on the node in question, then set it. Guessing takes the drive off the node.
  • A non-empty list enforces. Only the uids named are served; every other caller gets 403 Forbidden, including one whose uid the kernel will not report. Negative entries are ignored with a log line rather than wrapping into a uid that would match nobody.
  • It is uids, not account namesid -u <account> on the node.
  • Linux and macOS only. Windows has no peer credentials for a loopback TCP connection, so the agent can never learn who is calling there; a non-empty list on a Windows node refuses every request and takes the drive offline. Leave the key unset on Windows.

There is no command-line flag for this — edit the config file, and the agent reloads.

Configuration Priority

Configuration is loaded in this order (later values override earlier):

  1. Configuration file (/etc/nobgp/default.yml on Linux, /usr/local/etc/nobgp/default.yml on macOS, C:\ProgramData\nobgp\default.yml on Windows)
  2. Environment variables
  3. Command-line options

Example Configuration Files

These show the settings part of the file — the keys whose values differ from the default. The agent adds its own recorded state (domain, overlay-cidr, mcp-port, mcp-token, nfs-port, last-mount-point, the learned QUIC values) and the commented defaults block described under Format.

Minimal (a stock node after registration): no settings at all — every value is the default, so the file holds only what the agent recorded for itself.

Production:

log-level: "warning"
user: "deploy"
allow-admin: false

Development:

debug: true
log-level: "debug"
compress: false

Self-hosted router:

router: "router.example.com"

Environment Variables

All configuration options can be set via environment variables:

VariableEquivalent ConfigExample
NOBGP_KEYregistration-keyexport NOBGP_KEY="abc..."
NOBGP_NAMEnode-nameexport NOBGP_NAME="server1"
NOBGP_ROUTERrouterexport NOBGP_ROUTER="router.nobgp.com"
NOBGP_INSECUREinsecureexport NOBGP_INSECURE=true
NOBGP_TRANSPORTtransportexport NOBGP_TRANSPORT="wss"
NOBGP_QUIC_ROUTERquic-routerexport NOBGP_QUIC_ROUTER="router.example.com:443"
NOBGP_DEBUGdebugexport NOBGP_DEBUG=true
NOBGP_LOG_LEVELlog-levelexport NOBGP_LOG_LEVEL="debug"
NOBGP_COMPRESScompressexport NOBGP_COMPRESS=true
NOBGP_ENCRYPTencryptexport NOBGP_ENCRYPT=true
NOBGP_IDLE_TTLidle-ttlexport NOBGP_IDLE_TTL="30m"
NOBGP_INTERFACEinterfaceexport NOBGP_INTERFACE="eth0"
NOBGP_PING_INTERVALping-intervalexport NOBGP_PING_INTERVAL="120s"
NOBGP_OVERLAY_CIDRoverlay-cidrexport NOBGP_OVERLAY_CIDR="100.64.16.0/20"
NOBGP_MTUmtuexport NOBGP_MTU="1500"
NOBGP_FS_CACHE_TTLfs-cache-ttlexport NOBGP_FS_CACHE_TTL="30s"
NOBGP_USERuserexport NOBGP_USER="nobgp"
NOBGP_RUNTIME_DIRruntime-direxport NOBGP_RUNTIME_DIR="/etc/nobgp"

NOBGP_FS_CACHE_TTL takes a duration and needs its unit — a bare number is read as nanoseconds, and the agent treats a sub-second value as a missing unit rather than an instruction. There is deliberately no environment variable for the fs key: which backend a node mounts with is a property of that machine, set on the box or in its profile — see Filesystem keys.

NOBGP_USER is the unelevated identity as provisioning material — an image or task definition ships an unprivileged account and names it here, so the node serves admin: false from its first boot instead of refusing every unelevated call until someone creates an account by hand. Since agent 0.4.44 it is honoured on an installed agent too, not only an env-only one: before that a node silently reverted to no account on its first restart, once registration had written a config file. The environment outranks the config file for the agent's process lifetime, so change it in the service or task definition rather than with nobgp config — which saves your value and warns that the variable is winning. The value is not copied into the profile: a provisioned node's config file stays free of a user it never chose.

Usage:

export NOBGP_KEY="<YOUR_NETWORK_KEY>"
export NOBGP_NAME="server1"
export NOBGP_DEBUG=true

sudo -E nobgp agent
tip

Use -E flag with sudo to preserve environment variables.


Log Levels

The --log-level option controls output verbosity:

LevelDescriptionUse Case
errorOnly errorsProduction (minimal logging)
warningErrors and warningsProduction (standard)
infoInformational messagesDefault
debugDetailed debug informationDevelopment
traceVery detailed tracingTroubleshooting

Examples:

# Minimal output
nobgp agent --log-level error

# Standard production
nobgp agent --log-level warning

# Development
nobgp agent --log-level debug

# Detailed troubleshooting
nobgp agent --log-level trace

Exit Signals

The agent handles standard Unix signals on Linux and macOS:

SignalBehavior
SIGTERMGraceful shutdown
SIGINT (Ctrl+C)Graceful shutdown
SIGHUPReload configuration
SIGUSR1Increase log level
SIGUSR2Decrease log level
Windows

Unix signals are not supported on Windows. Use nobgp service stop or the Windows Service Manager to stop the agent — this performs a graceful shutdown that reverts the DNS suffix, unmounts the network drive, and resets firewall rules before exiting, the same cleanup SIGTERM triggers on Linux and macOS. Configuration reloads require a service restart.

Examples (Linux/macOS):

# Gracefully stop the agent
sudo pkill -TERM nobgp

# Reload configuration without restarting
sudo pkill -HUP nobgp

# Temporarily increase logging
sudo pkill -USR1 nobgp

Logging

Service Logs

The simplest cross-platform way to view logs is with the built-in nobgp service logs command (see above). Platform-specific methods are also available:

Linux (systemd):

sudo journalctl -u nobgp.service -f

Linux (OpenRC / Alpine):

tail -f /var/log/nobgp.err

Linux (OpenWRT):

logread -e nobgp

macOS (launchd):

tail -f /var/log/nobgp.err.log

Windows (PowerShell):

The Windows Service Control Manager does not capture a service's output, so the agent mirrors its logs to a file that nobgp service logs reads back. To view it directly:

Get-Content -Path C:\ProgramData\nobgp\nobgp.log -Tail 50 -Wait

Containers (no service manager):

# The agent's stdout/stderr
docker logs <container>

# Or read the file the agent mirrors its logs to (also what `nobgp service logs` reads)
tail -f /etc/nobgp/nobgp.log

Standalone Logs

When running standalone, logs go to stdout/stderr:

# Standard output
sudo nobgp agent

# Redirect to file
sudo nobgp agent > /var/log/nobgp.log 2>&1

# Pipe to logger
sudo nobgp agent 2>&1 | logger -t nobgp

Node Targeting

Several commands (exec, shell, proxy publish) require specifying a target node. There are two ways:

By name — specify both the network and node name:

nobgp exec -c "uptime" --network production --node web-server

By UUID — use the node ID directly (useful in scripts or when node names aren't unique across networks):

nobgp exec -c "uptime" --node-id 550e8400-e29b-41d4-a716-446655440000

You can find node IDs with nobgp network list.


JSON Output

Most management commands support a --json flag for machine-readable output. This is useful for scripting or piping to tools like jq:

# List networks as JSON
nobgp network list --json

# Get service details as JSON
nobgp proxy list --json | jq '.networks[].nodes[].services[]'

# Create a network and capture the ID
nobgp network create --name staging --json | jq -r '.network_id'

Platform Support

The management commands (login, logout, network, node, exec, proxy) work on all platforms — macOS, Linux, and Windows. The one exception is nobgp shell, which is not yet supported on Windows clients.

CommandmacOSLinuxWindows
login / logoutYesYesYes
network list / createYesYesYes
network add-nodeYesYesYes
execYesYesYes
shellYesYesNot yet
file upload / download / ls / rmYesYesYes
proxy list / publish / update / deleteYesYesYes
note

The shell limitation is on the client side only. You can still open shells on Windows nodes using browser terminals or from a macOS/Linux client.


Common Usage Patterns

Personal Machine Setup

# Log in once on your laptop
nobgp login

# Now manage your infrastructure from the command line
nobgp network list
nobgp exec -c "uptime" --network production --node web-server
nobgp shell --network production --node api-server

Remote Server Setup

# Generate install commands from your laptop
nobgp network add-node --network production --node new-server

# Copy the output and run it on the remote machine
# (installs agent, registers with network, starts service)

Development Workflow

# Register interactively and run with debug
sudo nobgp register --name "dev-$(whoami)"
sudo nobgp agent --debug

# Monitor logs in another terminal
sudo nobgp service logs -f

Production Deployment

# 1. Register the agent (opens browser for OAuth login)
# Registration installs and starts the service itself; exit 3 means
# the node enrolled but the service did not come up.
sudo nobgp register --name "$(hostname)"

# 2. Only needed if step 1 reported a service failure (exit 3)
sudo nobgp service install

# 3. Verify
nobgp status

Containerized Deployment

# Use registration key (browser login not available in containers)
docker run -d --restart unless-stopped \
--name nobgp \
--cap-add NET_ADMIN \
--cap-add SYS_ADMIN \
--device /dev/net/tun \
--device /dev/fuse \
--security-opt apparmor=unconfined \
-e NOBGP_KEY="KEY" \
-e NOBGP_NAME="container-$(hostname)" \
nobgp/nobgp

Debugging Connection Issues

# Run with maximum logging
sudo nobgp agent --log-level trace --debug

# Check if router is reachable
curl https://router.nobgp.com

# Check registration status
nobgp show

Configuration Best Practices

1. Register Before Running

Good:

sudo nobgp register --name "server"
sudo nobgp service install

Avoid:

sudo nobgp agent # Running without registration

2. Set Appropriate Log Levels

  • Production: warning or error
  • Development: info or debug
  • Troubleshooting: debug or trace

3. Use Descriptive Node Names

Good:

  • web-server-prod-1
  • api-server-staging
  • db-primary-us-east

Avoid:

  • server1
  • node
  • test

4. Secure Your Configuration File

# Restrict permissions
sudo chmod 600 /etc/nobgp/default.yml
sudo chown root:root /etc/nobgp/default.yml

5. Monitor Service Health

# Add to cron or monitoring system
*/5 * * * * systemctl is-active nobgp.service || systemctl restart nobgp.service

Troubleshooting Commands

Check Configuration

# View current config
cat /etc/nobgp/default.yml

# Validate YAML syntax
python3 -c "import yaml; yaml.safe_load(open('/etc/nobgp/default.yml'))"

Test Connectivity

# Check router accessibility
curl -v https://router.nobgp.com

# Test WebSocket connection
websocat wss://router.nobgp.com # If websocat is installed

Diagnose Service Issues

# Check service status
sudo nobgp service status

# View service logs
sudo journalctl -u nobgp.service -n 100

# Check for errors
sudo journalctl -u nobgp.service -p err

# Restart service
sudo nobgp service restart

Resource Usage

# Check process
ps aux | grep nobgp

# Memory usage
ps -o pid,user,%mem,rss,command -C nobgp

# CPU usage
top -p $(pgrep nobgp)

Advanced Topics

Custom Router

Enterprise Feature

Custom routers are available for enterprise deployments only. Contact sales@nobgp.com for more information.

For enterprise deployments with custom routers:

router: "custom-router.example.com:8080"

Multiple Profiles

Connect a single machine to multiple noBGP networks using profiles. Each profile is a separate YAML file in the config directory:

# Register profiles for different networks (interactive)
sudo nobgp register production --name "server-prod"
sudo nobgp register staging --name "server-staging"

# List all profiles
nobgp list

# Show a specific profile
nobgp show production

# When running as a service, all profiles are managed automatically
sudo nobgp service install
sudo nobgp service start

Interface Selection

By default (interface: "auto"), the agent picks the primary interface automatically. It first chooses the first up, non-loopback interface that has a hardware (MAC) address and an IP address in the required family. If no such interface exists, it falls back to MAC-less point-to-point links — such as cellular modems (wwan0), PPP, and tun devices — ranking them by carrier state and whether they hold a global-unicast address. This means nodes that only have a cellular or point-to-point connection are now selected automatically.

To override auto-selection, force a specific network interface:

interface: "eth0" # Or eth1, wlan0, wwan0, etc.

An explicit interface name always wins: the agent uses exactly that interface and never re-homes to another.


Next Steps

See Also

  • Configuration directory: /etc/nobgp/ (Linux), /usr/local/etc/nobgp/ (macOS), C:\ProgramData\nobgp (Windows)
  • Binary location: /usr/bin/nobgp or /usr/local/bin/nobgp (Linux/macOS), C:\Program Files\noBGP\nobgp.exe (Windows)