9/23/2026

Rule recipes for developer tools

Process rules are the reason to run Specola’s Linux eBPF backend: they route a tool no matter whether it honours HTTP_PROXY. The catch is that the name a rule sees is not always the command you typed. This post shows how to find the real name, then gives patterns for common tools.

All examples send traffic to a group called work; replace it with your own proxy or group.

Find the name the kernel sees

PROCESS-NAME matches the kernel comm field: the executable’s base name, cut to 15 visible bytes. Check it while the tool is running:

ps -o pid,comm,args -C git-remote-http
# or, for any PID
cat /proc/<pid>/comm

The Specola connection log shows the process for each connection as well, which is usually the fastest way to see what a rule needs to match.

When the name is ambiguous or too long, use PROCESS-PATH, which matches the absolute executable path and supports * and ?:

"PROCESS-PATH,/home/you/.local/share/mise/installs/*,work"

The kernel can pre-check exact names, but not paths or wildcards, so those rules make more connections take the trip through Core. Prefer exact names when you can.

git

git clone https://… does not open the connection itself: it starts the helper git-remote-https, whose comm is truncated to git-remote-http. SSH remotes use ssh.

"PROCESS-NAME,git-remote-http,work",
"PROCESS-NAME,ssh,work",

The ssh rule also routes your interactive SSH sessions. If you only want Git hosts, match the destination instead:

"DOMAIN-SUFFIX,github.com,work",
"DOMAIN-SUFFIX,gitlab.com,work",

Go

Module downloads are made by the go binary itself:

"PROCESS-NAME,go,work",

Language servers such as gopls fetch on their own; add them separately if you need them.

Cargo

"PROCESS-NAME,cargo,work",

rustup is a separate binary. Toolchain downloads need "PROCESS-NAME,rustup,work" as well.

Docker

The docker CLI only talks to the local daemon over a Unix socket. Image pulls are made by the daemon:

"PROCESS-NAME,dockerd,work",

If you have enabled the containerd image store, add containerd too and check the log to confirm which one opens the registry connections. Traffic from inside running containers lives in its own network namespace and is outside the scope of this recipe.

Node.js: npm, pnpm, yarn

These are JavaScript programs started through #!/usr/bin/env node, so the process is node:

"PROCESS-NAME,node,work",

That also catches every other Node program, including editor tooling. To be narrower, route the registry by domain instead:

"DOMAIN,registry.npmjs.org,work",

Python: pip and uv

Python tools depend on how they are launched, and virtual environments add their own paths. uv is a single native binary, which makes it easy:

"PROCESS-NAME,uv,work",

For pip, check the real comm with ps first, or route the package index by domain:

"DOMAIN,pypi.org,work",
"DOMAIN,files.pythonhosted.org,work",

Ordering

Rules are first-match, top to bottom. A layout that stays readable as it grows:

[rule]
list = [
  # 1. Hard blocks
  "PROCESS-NAME,telemetryd,REJECT",
  # 2. Exact process names
  "PROCESS-NAME,git-remote-http,work",
  "PROCESS-NAME,cargo,work",
  "PROCESS-NAME,go,work",
  # 3. Domains
  "DOMAIN-SUFFIX,corp.example,office",
  "DOMAIN,registry.npmjs.org,work",
  # 4. Networks
  "IP-CIDR,10.0.0.0/8,DIRECT,no-resolve",
  # 5. Paths and wildcards
  "PROCESS-PATH,/opt/internal/*,office",
  # 6. Everything else
  "FINAL,DIRECT",
]

Validate after every change with specola-core -t --config <profile> before reloading.