Files
voculinux/docs/GATEWAY_EMBED.md
T
Jatin K MalikandGitHub fb9166ed9e feat(settings): run local VocaGateway from Advanced (podman-first) (#774)
* feat(settings): run local VocaGateway from Speech Engine (podman-first)

Add an optional Local VocaGateway panel next to Remote Server so Linux users
can start the pinned v0.1.0 compose project with podman (docker fallback),
watch Live/Pairable/Ready status, pair a phone without loopback QR URLs, and
preset remote_api to the gateway transcriptions endpoint. Stop preserves
volumes; Flatpak fails closed; tray offers Stop only when this session manages
the gateway. Default on-device engine stays whisper.cpp.

* fix(gateway-embed): drop nonexistent compose cpu profile

VocaGateway v0.1.0 exposes default service gateway plus native/cuda/vulkan
profiles, not cpu. Start and stop the default service without --profile, keep
the latest image pin rejection, and prefer save_config for the remote_api preset.

* fix(gateway-embed): keep compose --profile cpu per embed contract

v0.1.0 has no cpu profile entry, but Compose still starts the unprofiled
gateway service when --profile cpu is set, and keeps cuda/vulkan/native off.
That matches the frozen desktop embed contract and stays forward-compatible.

* fix(gateway-embed): harden embed path for settings and compose

Keep podman/docker probing off the GTK thread, reject link-local and
loopback public URLs for phone QR, sanitize compose .env image overrides,
and cap bootstrap token reads so a hostile file cannot blow memory.

* fix(gateway-embed): keep LAN toggle and phone QR in sync

When Allow LAN flips while compose is already managed, rewrite publish
host and PUBLIC_URL and recreate the gateway before Pairable comes back.
Also reject default docker/podman bridge addresses for phone QR.

* test(gateway-embed): pin bridge and LAN-gate pairing cases

Cover 172.17.0.2 / link-local not becoming Pairable, and the mid-run
LAN toggle path that must wait for compose republish before advertising
a phone URL.

* style(gateway-embed): black format embed modules

* fix(gateway-embed): reject short-form loopback and localhost.* for QR

Align Pairable/QR host checks with vocagateway desktop-embed policy so
127.1 / localhost.* never become phone-reachable display URLs.

* fix(gateway-embed): detach Settings status listener on destroy

Gateway poll callbacks were left registered after Settings closed, so
idle_add could touch destroyed GTK widgets while compose stayed up.

* fix(gateway-embed): pass host_env to bridge ip probe

_bridge_iface_ips shells out to host ip; without host_env the AST
gate fails and AppImage bundles can hand the wrong GLib to ip.

* fix(packaging): ship gateway_embed in the AppImage

The explicit setuptools packages list omitted the new package, so the
bundle booted from source trees but died with ModuleNotFoundError on
every distro boot test.

* style(packaging): black the gateway_embed package guard

* fix(settings): pack local VocaGateway into the Advanced island

The rebase onto main auto-merged textually onto the simple Speech Model
page. Keep the gateway card on advanced_box, next to Remote Server.

* fix(gateway-embed): treat missing Compose as unavailable

When podman or docker is present but Compose is not, stop advertising Run.
Return empty compose_args, show an install-Compose hint, and cover it with a unit test.

Also add module loggers on the new gateway_embed modules and annotate the
gateway Settings/tray callbacks Greptile flagged. just lock found no lockfile drift.

* fix(gateway-embed): keep Allow LAN bind matched to the switch

Snapshot the LAN flag for start/republish, record what compose actually
got, then republish again if the switch moved mid-flight so 0.0.0.0 never
lingers while Allow LAN reads off.

* fix(gateway-embed): detect leftover compose after Quit

Quit does not stop compose, so a LAN-published gateway can keep running.
Settings and the tray treated that as Stopped because managed_by_us is
only process memory. Probe compose after runtime detection, show Stop
when it is actually up, and let Stop work even if this session did not
start it.

* fix(gateway-embed): restore LAN switch from leftover compose

Quit leaves compose running, so the next launch now reads
VOCAGATEWAY_PUBLISH_HOST from the embed .env, adopts the project as
managed, and keeps Allow LAN honest so republish can close 0.0.0.0.

* fix(gateway-embed): restore .env if LAN republish fails

compose recreate can fail after write_env_file already pointed
VOCAGATEWAY_PUBLISH_HOST at loopback. Restore the previous .env so
orphan adopt still sees a live 0.0.0.0 bind, and snap Allow LAN back
to the running publish mode.

* style(gateway-embed): type helper args and black MagicMock

* refactor(types): move Readable protocol to common_types

AGENTS.md keeps Protocol interfaces in common_types. Pairing now imports
the shared Readable surface instead of a local _Readable helper.

* fix(gateway-embed): satisfy mypy on rebased embed code

* fix(audio): keep duck restore point when a set reports failure
2026-09-29 15:33:27 -07:00

3.7 KiB

Local VocaGateway from Vocalinux (optional)

Vocalinux can start a local VocaGateway container from Settings → Speech Model → Advanced, next to Remote Server. This is an optional power-user path. It is not on-device processing: microphone audio still leaves the desktop client and is transcribed by the gateway process on your machine (or LAN).

The default speech engine remains whisper.cpp on-device. Enabling a local gateway never changes that default by itself. Use Use this Gateway only when you want Vocalinux to talk to the gateway over the existing Remote API settings (remote_api_*, endpoint /v1/audio/transcriptions). See HTTP_REMOTE.md.

Requirements (Linux MVP)

  • podman (preferred) or docker, with a working compose plugin (or podman-compose / docker-compose).
  • Vocalinux does not bundle a container engine inside the AppImage.
  • Flatpak: the sandbox cannot reach the host container socket. Run the native package or AppImage, or start VocaGateway on the host yourself and point Remote Server at it.

What Vocalinux does

  1. Pins upstream VocaGateway v0.1.0 (git tag) under your XDG cache and uses that checkout's compose.yaml with Compose project name vocagateway.
  2. Writes a private bootstrap token under ~/.config/vocalinux/gateway_embed/ (mode 600) and a matching Compose .env.
  3. Runs compose -f compose.yaml --profile cpu up -d for the gateway service. The v0.1.0 GitHub release has no published container image assets, so the first start builds the local image vocagateway:v0.1.0 from that tag (override with VOCAGATEWAY_IMAGE; never latest). Later starts reuse the image/volume.
  4. Polls GET /health/live and GET /health/ready, and (when live) authenticated GET /v1/admin/pairing for phone pairing.
  5. Stops with compose down without -v, so the named data volume (models, config) is kept.

Status labels

Status Meaning
Stopped Not running.
Starting Compose up in progress, or live probe not green yet.
Live Process answers /health/live.
Pairable Live, plus a non-loopback phone URL and bearer token (QR-safe).
Ready /health/ready is 200 (model selected in the gateway WebUI).
Error Start/stop or probe failure (see the subtitle).

Pairable can appear before Ready: you can pair a phone while a model is still downloading.

LAN / phone

By default the published host is 127.0.0.1 (desktop-only). Turn on Allow LAN access for Phone to set VOCAGATEWAY_PUBLISH_HOST=0.0.0.0 and to advertise a LAN URL in pairing. Open port 8765 in your firewall only on trusted networks. Never put 127.0.0.1 / localhost in the phone QR. If a LAN toggle fails, compose keeps the previous bind, Vocalinux restores the embed .env, and the switch snaps back with an error (Stop and Run again, or retry).

Tray and Quit

The tray menu offers Stop local Gateway whenever compose is actually running (Starting, Live, Pairable, Ready, or Error), including a leftover from a previous Vocalinux session. Vocalinux does not auto-start the gateway on login in v1.

Quit does not stop compose. The pinned compose file uses restart: unless-stopped, so a LAN-published gateway on 0.0.0.0:8765 keeps running after you quit Vocalinux. The next launch restores the Allow LAN switch from the running compose publish host. Use Stop local Gateway in Settings or the tray when you want it gone. Do not assume Quit tears it down.

Honesty

VocaGateway is optional self-hosted infrastructure for the Voca family. There is no Voca account and no hosted Voca cloud. Prefer a trusted LAN, Tailscale, or HTTPS. Do not expose port 8765 to the public internet.