File map
Where things physically live: packages, proto trees, CLI entry points, and the webui's unwired inventory. This is the descriptive companion to docs/architecture.md — that document is normative (boundaries, ownership, invariants); this one just tells you where the files are.
Runtime layout
The voice runtime ships as one logical package, packages/aleph/, with the two languages it needs:
packages/aleph/server/— Go orchestrator (audio routing, satellite plane, settings store, webui, sidecar supervision). Modulealeph/server. Entry pointcmd/aleph/.packages/aleph/sidecar/— Python ML pipeline (stateless calc server): STT viaParakeetSTT/ onnx-asr by default, with opt-inWhisperCppSTT/ pywhispercpp; Silero VAD; livekit_turn + smart_turn ONNX (raw scores, no statemachine); local LLM inference via llama.cpp/xllamacpp; TTS; the one shared text embedder (memory + tool arming) and the speaker embedder. The sidecar is llama.cpp-only for LLM — remote providers (Anthropic and OpenAI-compatible: OpenAI, DeepSeek, Gemini, Ollama, LM Studio, vLLM) never reach the sidecar. They're served byinternal/core/provider/apillmin the Go server, which talks to the API directly over HTTP; API keys live only in the Go settings store. The end-of-speech statemachine lives in the Go server (internal/core/endpoint/); the sidecar'sClassifyTurnRPC is a stateless calc verb that returns raw turn-detector probabilities. Seedocs/concepts/models.mdfor the full model topology and CPU/GPU residency table. Importable modulealeph_sidecar, packaged withpyproject.toml+uv.lock.
Both are released and developed together. The server supervises the sidecar subprocess; envprep (packages/aleph/server/internal/sidecar/envprep/) provisions the sidecar venv before launch.
Sibling packages outside the merged runtime:
packages/protocol/alabama-go/— Alabama 3.0 wire-protocol Go library (framing, Noise transport, generated stubs, conformance exerciser) shared with the satellite side.packages/satellites/reference/— Go reference satellite; doubles as the shared satellite client library (desktop and discord build on it).packages/satellites/desktop/— Wails desktop client (Go + Vue frontend).packages/satellites/android-kiosk/— Android satellite app (Echo Show kiosk launcher; always-on wakeword + AEC3).packages/satellites/android-companion/— Phone-side Aleph companion (Digital Assistant role, on-demand mic, phone-control tools).packages/satellites/android-rokid/— Phone-side companion for the Rokid RV101 glasses: streams the glasses mic in and TTS out, renders live captions on the heads-up display, exposes the glasses camera as a WEBCAM image-capture source, and carries an in-app drag-and-drop glasses-UI editor. The real glasses link (CXR-L SDK) is gated behind-PenableCxr; the default build compiles a stub link and stays green.packages/protocol/alabama-android/— Shared Kotlin Alabama 3.0 library (Wire-generated v3 stubs, Noise XX transport,AlabamaSession) consumed by the Android apps via Gradle composite build.packages/satellites/esp32-s3-audio-board/— ESP32-S3 firmware satellite (Waveshare audio board, ES7210/ES8311, esp-sr AFE for AEC + microWakeWord).packages/satellites/respeaker/— ESP32-S3 firmware satellite for the Seeed reSpeaker XVF3800 4-mic array (hardware AEC/beamforming, microWakeWord, LED ring via XVF3800 I2C).packages/protocol/esp32-components/— shared ESP-IDF components (alabama wire client, ggwave decoder) consumed by both firmware satellites.packages/aleph/webui/— Svelte 5 management UI embedded into thealephbinary viago:embed(built bytask webui-build).
Server internal layout
packages/aleph/server/internal/ is being grouped into concern sub-directories instead of one flat pile of packages, along the role axis of docs/architecture.md. internal/core/ (the pipeline brain — deciders, stores, identity, memory, turn loop) already nests this way; the rest of the tree is migrating cluster by cluster. Within a group with a natural hub the hub package sits at the group root and its extensions nest beside it (e.g. sink is the registry hub, sinkfactory and upnpsink live under it). Most moves are import-path-only — the package identifier is unchanged — but a package that reads better under a cleaner name is renamed outright (transport's alabama/listener).
internal/playback/— the assistant→device output plane (architecture §2 SPEAK side):audio/— PCM utilities/resampler (hub), withaudioenc/(container encoding) andaudiorouter/(fan one PCM source out to N sinks) beside it.sink/— audio-sink identity + live registry (hub), withsinkdiscovery/,sinkfactory/,sinkhttp/,upnpsink/, andsatellitesink/.speak/— spoken-output behaviours:announce/,voicegate/,greeting/.routing/— Output Routing (sinks): resolves a source satellite's configured output assignment into concreteaudiorouter.PlayTargets. It is a §9 decider (system=core), but lives here with the sinks it routes, exactly as the Provider/Model Router lives incore/provider/with the backends it routes — physical home follows the domain, not thesystemtag.
internal/transport/— the satellite↔core wire ingress (architecture §6):alabama/— Alabama wire-session adapter (wassatellitealabama).listener/— the satellite-connection server + turn driver (wasserver).lannet/— what of the server is reachable from the LAN.
internal/satellite/— the satellite domain (architecture §3): the session/state-machinesatellitepackage is the hub, withdevices/(device registry),satellitetools/,softsat/(software-satellite catalog), andpairingidentity/(Noise identity + admission) nested under it.internal/sidecar/— the sidecar runtime: bring it up, supervise it, self-heal it (architecture §5/§11). The gRPC-clientsidecarpackage is the hub, withenvprep/(venv/torch/wheel provisioning),binprep/(per-tool binary/wheel fetch:uv/,whispercpp/,opencode/,librespot/),hwdetect/(accelerator probe),proctree/(kill-group subprocess spawning),supervisor/(env-healing orchestration),status/(provisioning-status broadcast),healthlog/(resilience-event log),src/(the embedded Python source tree, wassidecarsrc), andembedtree/(embedded-tree materializer, wasembedsrc— notembed, which collides with the stdlib package) nested under it.internal/tools/— the tool plane (architecture §8): everything federates into thetoolsregistry, which is the hub at the group root.providers/(the tool-provider registry + every provider: the multi-instancehomeassistant/,calendar/,spotify/,mcp/,iobroker/,memory_recall/, plus the singleton-provider implscoding/,lists/,websearch/) nests under it —tools/providerscleanly disambiguates fromcore/provider(the LLM backends). The shared, non-provider tool infra stays as peers besideproviders/:servertools/(image-capture orchestrator tools),imagecache/(tool-produced image store),entityindex/(shared entity index the satellite/transport planes also read), andtooldispatch/(dispatch context flags).internal/obs/— the observability/telemetry plane:tracing(the OTel tracer + recording processor),livespans(in-process span pub/sub),spanschema(generated span binding),metrics(Prometheus), andprettylog(the coloured slog handler). No hub — five peer packages.internal/scheduling/— background and scheduled work:jobs(scheduled background jobs),scheduler(timers, reminders, recurring), andbackground(long-lived maintenance workers).internal/webui/— the server-side management plane. Thewebuipackage is the hub: it serves the browser over Connect. Its one read-model nests under it —threads/, the span-backed conversation index that subscribes toobs/livespansand feeds the WebUI's conversation view (a consumer of the telemetry plane, not part of it).static/holds the embedded webui dist.
A handful of genuinely cross-cutting utilities stay flat at the internal/ root on purpose — grouping unrelated packages into a platform/ bucket would just push the flatness down a level: config (bootstrap env), i18n, slug, topology (the wiring extractor), testutil, and settings (the settings store, read by server, sidecar, and webui alike). pb/ (generated stubs) likewise stays put. recorder (the per-turn input+output recording) is a turn concern that folds into core/ when that boundary is next revisited.
Tool providers
The Go server hosts a tools/providers.Registry at packages/aleph/server/internal/tools/providers/. It owns all server-side tool providers: each one implements TypeID() / Cardinality() / Reconcile(...) and produces one or more Profiles with their own Tools() for the LLM. The registry subscribes to the settings store, re-reconciles every provider on each change, and republishes the aggregated tool set into tools.Registry. Singleton providers (websearch, conversation_control, display, image, coding, …) live as single files in the providers root. Multi-instance providers — mcp/, homeassistant/, calendar/, spotify/ — get their own sub-package because their implementation grows beyond one file; the MCP provider supervises one client per configured server from Settings.Tools.Providers.McpServers (the per-transport client is still the disconnected stub until the stdio/HTTP transports land).
Satellite tools are deliberately not providers: they are device-owned and session-scoped, advertised over Alabama when the device connects and registered directly with tools.Registry under the device's own owner key (internal/tools/registry.go). The trust model — why every satellite-advertised tool is stamped peer-trusted at registration — is an architecture invariant; see the tools section of docs/architecture.md.
The Home Assistant provider at packages/aleph/server/internal/tools/providers/homeassistant/ reads exposure from HA's own assist-pipeline registry over WebSocket (no Aleph-side whitelist) and exposes four tools to the LLM: homeassistant_get_state (state + sensor value/unit + cover position), homeassistant_set_state (lights/switches: on/off, color, brightness), homeassistant_set_cover (covers: open/close/stop/set_position via an action enum — a tight schema keeps small local models reliable), and homeassistant_list_entities. The optional location parameter selects between multiple configured HA instances.
Proto layout
Proto sources live in two trees, one per logical package:
packages/aleph/proto/aleph/ — server/sidecar/webui surface:
settings/v1/— settings tree consumed by server, sidecar, and webui. Go inpackages/aleph/server/internal/pb/aleph/settings/v1/, Python inpackages/aleph/sidecar/aleph/settings/v1/, TS inpackages/aleph/webui/src/lib/pb/aleph/settings/v1/.sidecar/v1/— server↔sidecar gRPC surface. Go inpackages/aleph/server/internal/pb/aleph/sidecar/v1/; Python inpackages/aleph/sidecar/aleph/sidecar/v1/.user/v1/—Userentity shared by the settings tree and the webui surface.webui/v1/—WebUIServiceConnect service consumed by the browser (GetSettings,UpdateSettings,SubscribeSettings).
packages/protocol/alabama-go/proto/aleph/alabama/v3/ — Alabama wire protocol, generated into packages/protocol/alabama-go/v3/pb/ (Go) and the nanopb ESP component (C).
docs/protocol/proto/ — the canonical .proto source for the Alabama 3.0 wire protocol, source of truth for the specification. task spec-alabama injects these into the Bikeshed spec (docs/protocol/alabama-3.0.bs) and renders it to docs/public/spec/, which the docs site serves at /spec/. The schema in the spec is never hand-copied, so it cannot drift from the .proto.
aleph CLI
The Go binary built from packages/aleph/server/cmd/aleph/ dispatches on the first positional argument; a leading flag falls through to server for ergonomic invocations like aleph -debug.
aleph server(default) — runs the orchestrator. Loads bootstrap + settings, spawns and supervises the Python sidecar, serves the satellite plane, the webui, and the settings gRPC service.aleph sidecar— runs envprep then execs the Python sidecar in the foreground (stdio attached, no supervision). Useful for profiling, standalone runs, or a future remote-sidecar deployment. Flags:-no-envprep,-gpu-extra,-settings-addr,-debug.aleph device-code <device-id>— prints the captive-portal access code for a paired satellite. Read-only.
WebUI: unwired inventory
The Svelte webui (packages/aleph/webui/) renders every page from the real design dump, but some interactive elements have no backend to call — either the proto has no field/RPC for the concept at all, or the field exists but nothing server-side acts on it yet. Those elements are wrapped in <Unwired topic="..."> (packages/aleph/webui/src/lib/components/ Unwired.svelte): the control renders, looks real, but clicks are swallowed (stopPropagation/preventDefault in capture phase) and a small dot marks it as inert. This is a deliberate data-honesty rule — the UI never fabricates state or fakes a working control. Regenerate this table with rg -o 'topic="[a-z-]+"' packages/aleph/webui/src -r '$1' | sort -u (excluding the "x" topic, which only exists in a component test harness).
| Topic | Feature | Fehlendes Backend |
|---|---|---|
attribution | Nutzer → Zuordnung tab: reassign a turn's speaker, merge two users | No turn-reassignment RPC and no user-merge RPC exist; only listing users (to pick a merge target) is real |
identity-inbox | Nutzer page: inbox of unlinked identities awaiting attribution | No unlinked-identity/inbox data source exists anywhere in the proto; the section is a static example row |
identity-links | Nutzer → Identität tab: pairing PIN, linked external accounts (email/phone) | No account-linking concept or RPC exists in the proto at all |
identity-self | Nutzer: "DU" badge marking the operator's own profile | No operator/"this is me" signal exists anywhere |
mcp-crud | Werkzeuge → MCP section: add/enable-toggle/remove an MCP server | Providers.mcp_servers is a real settings map, but the Go MCP provider's per-transport (stdio/HTTP) client is still a disconnected stub — writing entries wouldn't connect anything |
pairing-words | Satelliten pairing modal: play spoken verification words for an already-connected guest | No RPC derives/speaks a word triple for a connected-but-unapproved device (GeneratePairingToken is a different, not-yet-built "add a brand-new device" flow) |
providers | Einstellungen → LLM "weitere Maschine verbinden" row; Anbieter page's "Maschinen" / "Verbundene Dienste" cards | No settings model exists for registering additional inference machines or generic connected services |
sat-blocklist | Satelliten pending list: deny a request, remove a blocked device | No deny/block/reject RPC exists; ApproveSatellite has no counterpart, so the blocked list is always empty |
sat-fqn | Satelliten detail: fingerprint button meant to reveal a second identifier | Satellite carries only one identifier (device_id); there is no second value to reveal |
sat-output-routing | Satelliten detail: picking an audio-output sink | Satellite has no output-sink-binding field and no RPC persists one; the sink list itself is real (ListSinks/ScanOutputDevices) |
sat-remove | Satelliten detail: "Entfernen" (forget device) | No remove/forget-satellite RPC exists |
sat-room | Satelliten detail: Raum/Zone field | Satellite has no room/zone field at all |
tool-acl | Werkzeuge tab: per-tool access-scope select (Nur ich/Alle/Bestimmte) | No per-tool ACL/access-scope path exists anywhere in settings.proto |
tool-acl-people | Werkzeuge tab: specific-people picker for a tool's access scope | Same gap as tool-acl — no people-scoping field or RPC exists |
These are backend gaps, not webui bugs — closing one means adding the proto field/RPC and its server-side implementation first, then removing the Unwired wrapper.