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

78 lines
3.7 KiB
Markdown

# Local VocaGateway from Vocalinux (optional)
Vocalinux can start a **local** [VocaGateway](https://github.com/VocaHQ/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](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.