Skip to content

Architecture

Velle Sinclair edited this page Jul 26, 2026 · 3 revisions

Architecture

SynapseOS runs a local LLM daemon as a system service and lets the rest of the system talk to it over a Unix socket. The model is a piece of system infrastructure, like a logger or an init system — not an application you launch.

  User
   │
   ▼
 synsh ─── natural language / commands ──┐
   │                                     │
   ▼                                     │
 synapd  (local LLM — Mistral 7B)        │
   │  inference over SYN socket protocol │
   ├──► synguard      security verdicts ─┤
   ├──► synnet        network policy     │
   └──► synapse_kmod  kernel sysfs       │
            │                            ▼
            ▼                    synui (Wayland)
     /sys/kernel/synapse/
     syscall_log, ai_hints, stats,
     status, config, version

No network calls, no API keys. The model lives on the machine.


The components

Component Role
synapd Local LLM inference daemon (llama.cpp). Owns the model; every other component is a client.
synsh AI-native shell. Type a command, or just say what you want.
synui Wayland compositor on wlroots 0.20, rendering through scenefx 0.5. Tiling + monocle, per-output workspaces, XWayland, layer-shell.
synguard Security monitor. Classifies syscall events, scores threats, publishes verdicts on a feed.
synnet Network policy daemon with nftables integration.
synapse_kmod Kernel module (DKMS). Syscall monitoring + AI scheduling hints via sysfs.

Supporting: syn-install, syn-firstboot, syn-model, scenefx/ (the vendored scene-graph fork synui renders through), synui/quickshell/ (the bar and desktop widgets), archiso/.


The wire protocol

synsh, synui, synguard, synnet and the kernel module all speak to synapd over a Unix socket using a fixed 28-byte binary header, defined in synapd/include/synapd.h:

#pragma pack(push, 1)
/* 28-byte header, then payload */
#pragma pack(pop)

It's a packed struct, so the header layout is load-bearing across five components. There's a unit test guarding it — keep it that way. If you change the header, everything that speaks it must be rebuilt together.

The socket is the only interface. That's what makes the model a system service rather than a library: synui doesn't link llama.cpp, it opens a socket.


Two consequences of the socket design worth internalising

1. Anything can subscribe. synguard publishes verdicts on a feed and synui holds a live subscription to it, which is how the compositor can render security state without knowing anything about syscall classification. Same for synapd's activity — the Super+A neural overlay is a live feed, not a poll.

2. A dead socket is the failure mode to look for. When something "isn't using the AI", the question is almost never is the model loaded — it's did this client's socket connection survive. Two real bugs of exactly this shape:

  • synguard's security feed leaked a client slot per departed subscriber, wedged at 16 clients, and then refused every new subscriber ("at capacity") while logging it forever. Fixed with a poll()-based reap.
  • systemd's IgnoreSIGPIPE=yes (the default) meant a write to a dead pipe didn't kill the writer as intended, and a feature stayed silently dead for 21 hours.

Services

Everything starts on boot:

systemctl status synapd      # AI inference daemon
systemctl status synguard    # security monitor
systemctl status synnet      # network policy
lsmod | grep synapse_kmod    # kernel module
cat /sys/kernel/synapse/status

Dependency gotcha: synnet has a Requires= on synapd. Stopping synapd (which game mode does, to free VRAM) therefore also stopped synnet until that was fixed. Check Requires= before you stop a unit.

See also: synapd, synui, synguard and synapse_kmod.

Clone this wiki locally