Configuration
The daemon reads one TOML file, /etc/netscope/config.toml. It is root-owned and intended to be edited only by an administrator.
The shipped default is:
[daemon]
socket_path = "/run/netscope/control.sock"
state_path = "/var/lib/netscope/state.json"
cgroup_root = "/sys/fs/cgroup/netscope"
poll_interval_ms = 1000
refresh_interval_secs = 5
max_profiles = 32
log_level = "info"
probe_interval_ms = 2000
probe_target = "1.1.1.1:443"
probe_target6 = "[2606:4700:4700::1111]:443"
Keys
| Key | Default | Meaning |
|---|---|---|
socket_path | /run/netscope/control.sock | Unix socket for CLI requests. Root-owned, group netscope, mode 0660. |
state_path | /var/lib/netscope/state.json | Persistent profile state. Do not edit it by hand. |
cgroup_root | /sys/fs/cgroup/netscope | Parent cgroup for profiles. It must be an absolute path inside /sys/fs/cgroup. |
poll_interval_ms | 1000 | How often the late-attach scanner looks for matching processes. Bounded to 100–10000 ms. |
refresh_interval_secs | 5 | How often targets are re-resolved and matching processes are attached. Bounded to 1–300. |
max_profiles | 32 | Profile ceiling, 1–32. Lowering it prevents new profiles above the new limit. |
log_level | info | error, warn, info, debug, or trace. |
probe_interval_ms | 2000 | How often ordered links are probed for failover. Bounded to 500–60000 ms. |
probe_target | 1.1.1.1:443 | IPv4 address:port used for link probes. |
probe_target6 | [2606:4700:4700::1111]:443 | IPv6 target for link probes; only used when the profile carries IPv6 and a link has a global IPv6 source address. |
Unknown keys are rejected. The file is parsed with deny_unknown_fields, so a typo such as poll_interval stops the daemon instead of being silently ignored. Check the journal after editing:
sudo systemctl restart netscoped
systemctl status netscoped
journalctl -u netscoped -n 50 --no-pager
systemctl reload is not used. Editing the file requires a restart, which also re-reads the profile state.
Changing the socket or state path
Both paths are created and owned by the daemon. If you move socket_path, update the directory’s permissions so the netscope group can reach it, and restart the daemon. If you move state_path, move the existing state file first or the daemon starts with no profiles.
The CLI accepts a hidden --socket <path> override. It exists for tests and for a daemon started with a non-default path; it is not needed in normal use.
Changing the cgroup root
cgroup_root must remain under /sys/fs/cgroup, because the nftables selector is built from the path relative to the cgroup mount. A value outside the cgroup2 hierarchy is rejected at startup. If you change the cgroup root while profiles are active, the old directory is left behind; stop the daemon, remove the old directory, and start it again.
State file
state_path holds a versioned JSON document with one entry per profile. It stores the target kind and identity — a native executable path, a Windows PE path for a --wine profile, a process name for a --process profile, or a Steam app ID — each consumer’s claim and policy, the profile’s mark, table, priority, and index, pre-open fanotify marks, and timestamps. Version 1 profiles are migrated on load: their single mode becomes the netscope-cli consumer’s policy with a persistent claim. Peer claims belong to a live control connection and are dropped on load, so a crashed consumer cannot leave a scope claimed forever. A Wine or name profile records the canonical PE paths with device and inode, so the daemon can re-stat the files and move a fanotify mark when a game updates. The daemon owns it:
- Do not edit it while
netscopedis running. - Back it up before an upgrade if you have profiles you cannot recreate easily.
- A state version newer than the daemon is rejected rather than partially loaded.
- Deleting it while the daemon is stopped removes all profile definitions. The host rules can be cleaned with
sudo netscope cleanup --allbefore starting the daemon again.
Traffic counters and rates
Each profile’s nftables table carries six named counters: one pair for
outbound packets that reach the profile, one pair for inbound packets, and
counters for routed and dropped traffic. The daemon samples them once per
second with a single nft -j list ruleset call and keeps the totals and
smoothed rates in memory; status and list only read that cache.
- Totals are a counter window that starts at
since_unix, the daemon’s start time; every profile reports the same value. A daemon restart starts a new window, and the first sample after it adopts the kernel counters as a baseline, so traffic written before the restart is not reported as new. - Loopback is accepted before the counter selectors in both directions:
outbound
127.0.0.0/8and::1/128destinations, inbound the same source ranges. Local traffic therefore never inflatestx_bytes,rx_bytes, or theTRAFFICrates. - Recreating a table resets its kernel counters. The daemon treats the new value as the next delta, so reported totals never move backwards; rates can dip for a sample.
- Disabling a profile removes its table, so
statsbecomesnull; enabling it installs a fresh table that counts from zero while the daemon’s totals keep their previous value plus the new traffic. - Nothing is persisted; the totals and rates live in memory for the daemon’s lifetime.
Limits
- At most 32 profiles, regardless of
max_profiles. Loweringmax_profilesdoes not delete existing profiles, but adding beyond the limit fails. - Profile IDs are 1–32 characters:
[a-z0-9][a-z0-9-]*. - Marks, tables, and rule priorities are allocated from fixed ranges. Exhausting the 32 indexes fails the add. A route table used by an active profile is refused; a table left by a removed profile is cleaned up on the next daemon reconcile.
- One shared profile has one route policy and any number of filter policies, one per consumer. Policies are replaced with
netscope policy; the target itself is not edited in place.
Logging
Runtime logs go to the journal:
journalctl -u netscoped -f
netscope logs -n 100
netscope logs is a bounded view (default 50 lines, -n to change it). It does not follow. Raw daemon logs may include profile paths and interface names but never packet contents.
Example: quieter polling
For a machine with many processes, reduce wakeups by polling less often. Pre-exec attachment is unaffected; only late attachment gets slower.
[daemon]
poll_interval_ms = 2000
refresh_interval_secs = 10
log_level = "warn"
Restart after the change and confirm with sudo netscope doctor.