9/23/2026
Setting up Specola on Omarchy
This guide takes a fresh Omarchy install to a setup where git, cargo,
npm and docker pull go through your company proxy, everything else stays direct, and you can
flip routing on and off from a Hyprland keybinding with its state visible in Waybar.
It assumes specola-core is installed at /usr/local/bin/specola-core. An official AUR package is
on the roadmap; until then, install the Linux build and adjust the path if you put it elsewhere.
1. Check the kernel
Linux eBPF Enhanced Mode needs Linux 5.17 or newer, cgroup v2 mounted at /sys/fs/cgroup, and
kernel BTF. The stock Arch kernel on Omarchy has all three. Check anyway:
uname -r # 5.17 or newer
stat -fc %T /sys/fs/cgroup # cgroup2fs
test -r /sys/kernel/btf/vmlinux && echo BTF ok
2. Write a profile
Specola reads one TOML profile. Put it where the service will read it:
sudo mkdir -p /etc/specola
sudo nvim /etc/specola/profile.toml
[general]
route-mode = "rule"
log-level = "info"
port = 7890
protocols = ["socks5", "http"]
# Your company proxy. Replace with the server you are authorized to use.
[[proxy]]
name = "office"
type = "http"
server = "proxy.corp.example"
port = 8443
username = "you"
password = "replace-me"
[proxy.tls]
enabled = true
# Use the office proxy while it is healthy, otherwise go direct.
[[proxy-group]]
name = "work"
type = "fallback"
proxies = ["office", "DIRECT"]
url = "https://www.gstatic.com/generate_204"
interval = 300
[rule]
list = [
# Dev tools, matched by the kernel-visible process name.
"PROCESS-NAME,git-remote-http,work", # git over HTTPS (comm is truncated to 15 bytes)
"PROCESS-NAME,cargo,work",
"PROCESS-NAME,node,work", # npm, pnpm and yarn all run as node
"PROCESS-NAME,dockerd,work", # image pulls are made by the daemon, not the CLI
# Internal names.
"DOMAIN-SUFFIX,corp.example,office",
# Keep the LAN local.
"IP-CIDR,192.168.0.0/16,DIRECT,no-resolve",
"IP-CIDR,10.0.0.0/8,DIRECT,no-resolve",
"FINAL,DIRECT",
]
# The eBPF backend always runs Core's DNS module; Fake-IP keeps domain rules working.
[dns.tun]
mode = "fake-ip"
[enhanced-mode]
enable = true
type = "ebpf"
ip-version = "ipv4"
[enhanced-mode.ebpf]
process-routing = true
A few details that save debugging time:
PROCESS-NAMEmatches the kernelcomm, which is at most 15 visible bytes. Git’s HTTPS helpergit-remote-httpstherefore appears asgit-remote-http. For long names, usePROCESS-PATH.npmis a Node.js script, so the process isnode. That rule also routes other Node programs; usePROCESS-PATHif you need to be narrower.- With
FINAL,DIRECT, connections that match no rule are never touched: the kernel leaves the original socket alone and Core never sees them. Only candidates for a rule enter Core. - The eBPF backend currently captures IPv4 only; IPv6 traffic bypasses it.
- Domain rules work for transparently captured traffic because DNS queries from captured processes are answered by Core. See DNS for transparent routing.
Validate the profile as your normal user before touching the network:
specola-core -t --config /etc/specola/profile.toml
3. Run it as a service
Enhanced Mode needs root to load BPF programs and attach them to the cgroup hierarchy. A systemd unit keeps that privilege out of your desktop session:
# /etc/systemd/system/specola.service
[Unit]
Description=Specola traffic router
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/local/bin/specola-core --config /etc/specola/profile.toml
Restart=on-failure
[Install]
WantedBy=multi-user.target
systemd stops services with SIGTERM, which lets Specola detach its BPF programs cleanly. Never
stop it with SIGKILL.
sudo systemctl daemon-reload
sudo systemctl start specola
journalctl -u specola -f
A successful start logs:
Linux IPv4 process proxy started with cgroup eBPF socket redirection (IPv6 bypassed)
To let your user start and stop the service without a password prompt, add a polkit rule:
// /etc/polkit-1/rules.d/50-specola.rules
polkit.addRule(function (action, subject) {
if (action.id == "org.freedesktop.systemd1.manage-units" &&
action.lookup("unit") == "specola.service" &&
subject.isInGroup("wheel")) {
return polkit.Result.YES;
}
});
4. A Hyprland keybinding
Create a toggle script:
#!/usr/bin/env bash
# ~/.local/bin/specola-toggle
if systemctl is-active --quiet specola; then
systemctl stop specola && notify-send "Specola" "Routing off"
else
systemctl start specola && notify-send "Specola" "Routing on"
fi
pkill -RTMIN+8 waybar
chmod +x ~/.local/bin/specola-toggle
Then bind it in ~/.config/hypr/bindings.conf:
bind = SUPER SHIFT, N, exec, ~/.local/bin/specola-toggle
5. A Waybar status module
The service state is the status signal:
#!/usr/bin/env bash
# ~/.local/bin/specola-waybar
if systemctl is-active --quiet specola; then
echo '{"text": "SPC", "tooltip": "Specola: routing on", "class": "on"}'
else
echo '{"text": "SPC", "tooltip": "Specola: routing off", "class": "off"}'
fi
Add the module to ~/.config/waybar/config.jsonc: put "custom/specola" into modules-right,
then define it:
"custom/specola": {
"exec": "~/.local/bin/specola-waybar",
"return-type": "json",
"interval": 10,
"signal": 8,
"on-click": "~/.local/bin/specola-toggle"
}
And style it in ~/.config/waybar/style.css:
#custom-specola.off { opacity: 0.4; }
The signal: 8 line matches pkill -RTMIN+8 waybar in the toggle script, so the module updates
the moment you press the keybinding. A first-party Waybar module with mode switching is on our
roadmap; this script version works today.
6. Verify
git ls-remote https://github.com/git/git HEAD # appears in the log, routed via "work"
sudo bpftool cgroup tree /sys/fs/cgroup # lists Specola's attached programs
After systemctl stop specola, the programs should be gone from bpftool cgroup tree. If
anything looks wrong, the Linux eBPF manual lists the common failures
and what each one means.