[EPIC] Reproducible self-hosted zara-server container distribution and GHCR release path #153

Open
opened 2026-08-22 21:51:43 +00:00 by lost-rob0t · 1 comment
lost-rob0t commented 2026-08-22 21:51:43 +00:00 (Migrated from github.com)

Goal

Make the long-lived Zara service easy to deploy as a reproducible, secret-free container artifact built from the same Nix source of truth as the rest of the repository.

This epic owns distribution/deployment, not assistant architecture. It must not move microphones, speakers, desktop commands or other client-device capabilities into the container merely because the service is packaged there.

Existing child issue #149 already owns the first concrete slice: build a Nix OCI image and publish ghcr.io/lost-rob0t/zara through GitHub Actions. Do not create a duplicate image issue.

Architecture baseline

The desired runtime split is:

container / server host
  zara-server
  STT/VAD
  semantic/server Prolog
  conversation + memory
  agent/LLM
  server API services/tools/MCP
  TTS synthesis

client device
  wake/manual activation
  microphone capture
  speaker playback
  UI
  typed local device capabilities

The image therefore must not require PulseAudio/PipeWire/desktop Qt merely to run the headless daemon unless a concrete server-side dependency truly needs it.

Dependencies and ordering

  • lifecycle/protocol/security baseline: #128-#130 completed;
  • production multi-user state requires #131;
  • client migration remains #133;
  • #149 is the first packaging child and may be implemented independently where it does not claim daemon Voice parity;
  • typed server/device capability separation from #150 should be reflected before calling the container a complete multi-device distribution;
  • no Prolog-RLM.

Nix image contract

Prefer a Nix-native OCI/Docker image output such as dockerTools.buildLayeredImage after research verifies closure size/layer behavior.

Required output:

nix build .#zara-server-image

The artifact must:

  • run zara-server as the default service entrypoint;
  • include the required Python runtime, SWI-Prolog/PySWIP runtime assets, Zara Prolog KB/modules and actual server dependencies;
  • omit source/build dependencies not needed at runtime where practical;
  • use a deterministic working/state/config layout;
  • run without baking credentials, conversation DBs, memory DBs, client keys, server CURVE private keys, provider API keys or user config into an image layer;
  • expose/mount writable state explicitly;
  • have deterministic UID/permissions policy researched rather than assuming root;
  • preserve owner-private key/state permissions under container mounts;
  • support read-only root filesystem where practical, with declared writable mounts/tmp paths;
  • fail clearly when required configuration/state is unavailable.

Runtime state model

Define and document container paths/volumes for at least:

  • Zara config;
  • Prolog user configuration where supported;
  • conversation SQLite/state;
  • memory/vector persistence;
  • CURVE server identity/key registry;
  • logs/metrics if persistent logging is configured;
  • optional plugin/module mounts;
  • runtime IPC/socket directory where applicable.

Separate immutable image assets from mutable principal/private state. Migration behavior must be tested against persisted volumes across image upgrades.

Network/listener security

Container convenience must not weaken #130:

  • wildcard/TCP bind remains explicit and secure-only;
  • no unauthenticated public default port;
  • CURVE/ZAP enrollment/revocation remains authoritative;
  • image metadata/docs must not suggest publishing an insecure listener;
  • loopback/IPC/container-network semantics must be documented honestly;
  • health checks must not expose private status, transcripts, principal IDs or secrets;
  • administrative actions remain separately authorized.

GHCR publishing

Publish to:

ghcr.io/lost-rob0t/zara

Use GitHub Actions with GITHUB_TOKEN and minimum packages: write permission. Do not require a long-lived personal registry credential.

Tag policy must include an immutable commit-SHA reference. Research/define safe convenience tags for master, semantic releases and/or versions without allowing an untrusted PR to overwrite trusted tags.

Publishing must never occur from untrusted fork/PR code with package-write authority.

Capture provenance/metadata available through the chosen GitHub/Nix path where practical, while keeping the image build reproducible and understandable.

Container smoke/integration gates

Automate at least:

  • Nix image build;
  • inspect archive/config and verify intended entrypoint;
  • load/run using an available OCI/Docker-compatible engine in integration CI where practical;
  • start headless with no Qt/display/audio device;
  • readiness/liveness behavior;
  • clean SIGTERM shutdown;
  • duplicate/lock behavior appropriate to container runtime paths;
  • mounted config/state/key permissions;
  • restart with persisted state;
  • upgrade with persisted schema/state;
  • no secrets in image history/layers/config/environment defaults;
  • no repository-local test keys copied into release image;
  • non-root/read-only-root behavior if selected by design;
  • bounded logs and sanitized startup failure;
  • exact packaged zara-server version/provenance query.

Deployment examples

After the image itself is proven, add minimal maintained examples rather than a giant orchestrator stack:

  • docker run / Podman example;
  • Compose example if it materially helps state/key mounts;
  • reverse-proxy guidance only if actually needed by a supported protocol path;
  • local-LAN deployment with authenticated CURVE client enrollment;
  • optional external Ollama/provider endpoints configured by secret/env/config mounts;
  • explicit note that client microphones/speakers stay on clients.

Examples must never contain real secrets or encourage --privileged/host-device mounting without a concrete reviewed requirement.

Server model/runtime assets

Research deployment policy for STT/TTS/model assets:

  • build-time image asset vs runtime mounted/downloaded cache;
  • deterministic version/hash where assets are bundled;
  • image-size implications;
  • offline startup profile;
  • writable model cache location;
  • multiple image/profile possibility only if it avoids a giant all-in-one artifact without fragmenting runtime semantics.

Do not silently download mutable unpinned code/models during every container startup if a reproducible/cacheable alternative exists.

Plugin/tool policy in containers

Tools and plugins remain available, but container location is part of capability semantics.

  • server tools/services may execute inside the container under explicit capabilities;
  • Linux/Android device actions must not accidentally execute inside the container;
  • user plugin mounts have explicit ownership/permissions;
  • plugin child processes remain bounded by existing plugin runtime policy;
  • no assumption that xdg-open, desktop apps, clipboard, screen or systemd host control exist in the image;
  • host administration must use explicitly designed APIs/capabilities rather than mounting /var/run/docker.sock or arbitrary host filesystem by default.

Adversarial deployment matrix

Cover:

  • world-readable mounted private key rejected;
  • missing/corrupt key registry;
  • read-only state path;
  • DB migration failure/recovery;
  • stale socket/lock after abrupt container death;
  • repeated restart loop;
  • malformed configuration;
  • resource exhaustion/low disk where deterministic simulation is possible;
  • incorrect published port/auth configuration;
  • untrusted plugin mount;
  • image scan proving no known fixture secrets/private credentials from repo;
  • client device action requested while only headless server capability exists -> typed unavailable/routed-to-client behavior once #150 lands.

RAGE/TDD

Each child follows repository RAGE with immutable start SHA/issue in Org log, adversarial research, explicit design/rejected alternatives/invariants, tests-first evidence, full Nix/repo gate, container-specific focused gate, changed-code coverage review and exact-head Actions.

Ordered slices

  1. #149 — Nix OCI image + GHCR publish workflow;
  2. state/config/key volume contract + non-root/permission/read-only-root hardening;
  3. deterministic container lifecycle/readiness/persistence/upgrade integration gate;
  4. model/cache/plugin deployment policy and minimal Docker/Podman/Compose documentation;
  5. final secret-leak/listener/capability-location deployment security gate.

Completion rule

A user can pull an immutable GHCR Zara image, mount fresh private configuration/state, enroll authenticated clients, run zara-server headlessly and persist/restart/upgrade it without secrets baked into layers, insecure public defaults, or device-local commands accidentally executing inside the container.

## Goal Make the long-lived Zara service easy to deploy as a reproducible, secret-free container artifact built from the same Nix source of truth as the rest of the repository. This epic owns **distribution/deployment**, not assistant architecture. It must not move microphones, speakers, desktop commands or other client-device capabilities into the container merely because the service is packaged there. Existing child issue #149 already owns the first concrete slice: build a Nix OCI image and publish `ghcr.io/lost-rob0t/zara` through GitHub Actions. Do not create a duplicate image issue. ## Architecture baseline The desired runtime split is: ```text container / server host zara-server STT/VAD semantic/server Prolog conversation + memory agent/LLM server API services/tools/MCP TTS synthesis client device wake/manual activation microphone capture speaker playback UI typed local device capabilities ``` The image therefore must not require PulseAudio/PipeWire/desktop Qt merely to run the headless daemon unless a concrete server-side dependency truly needs it. ## Dependencies and ordering - lifecycle/protocol/security baseline: #128-#130 completed; - production multi-user state requires #131; - client migration remains #133; - #149 is the first packaging child and may be implemented independently where it does not claim daemon Voice parity; - typed server/device capability separation from #150 should be reflected before calling the container a complete multi-device distribution; - no Prolog-RLM. ## Nix image contract Prefer a Nix-native OCI/Docker image output such as `dockerTools.buildLayeredImage` after research verifies closure size/layer behavior. Required output: ```text nix build .#zara-server-image ``` The artifact must: - run `zara-server` as the default service entrypoint; - include the required Python runtime, SWI-Prolog/PySWIP runtime assets, Zara Prolog KB/modules and actual server dependencies; - omit source/build dependencies not needed at runtime where practical; - use a deterministic working/state/config layout; - run without baking credentials, conversation DBs, memory DBs, client keys, server CURVE private keys, provider API keys or user config into an image layer; - expose/mount writable state explicitly; - have deterministic UID/permissions policy researched rather than assuming root; - preserve owner-private key/state permissions under container mounts; - support read-only root filesystem where practical, with declared writable mounts/tmp paths; - fail clearly when required configuration/state is unavailable. ## Runtime state model Define and document container paths/volumes for at least: - Zara config; - Prolog user configuration where supported; - conversation SQLite/state; - memory/vector persistence; - CURVE server identity/key registry; - logs/metrics if persistent logging is configured; - optional plugin/module mounts; - runtime IPC/socket directory where applicable. Separate immutable image assets from mutable principal/private state. Migration behavior must be tested against persisted volumes across image upgrades. ## Network/listener security Container convenience must not weaken #130: - wildcard/TCP bind remains explicit and secure-only; - no unauthenticated public default port; - CURVE/ZAP enrollment/revocation remains authoritative; - image metadata/docs must not suggest publishing an insecure listener; - loopback/IPC/container-network semantics must be documented honestly; - health checks must not expose private status, transcripts, principal IDs or secrets; - administrative actions remain separately authorized. ## GHCR publishing Publish to: ```text ghcr.io/lost-rob0t/zara ``` Use GitHub Actions with `GITHUB_TOKEN` and minimum `packages: write` permission. Do not require a long-lived personal registry credential. Tag policy must include an immutable commit-SHA reference. Research/define safe convenience tags for `master`, semantic releases and/or versions without allowing an untrusted PR to overwrite trusted tags. Publishing must never occur from untrusted fork/PR code with package-write authority. Capture provenance/metadata available through the chosen GitHub/Nix path where practical, while keeping the image build reproducible and understandable. ## Container smoke/integration gates Automate at least: - Nix image build; - inspect archive/config and verify intended entrypoint; - load/run using an available OCI/Docker-compatible engine in integration CI where practical; - start headless with no Qt/display/audio device; - readiness/liveness behavior; - clean SIGTERM shutdown; - duplicate/lock behavior appropriate to container runtime paths; - mounted config/state/key permissions; - restart with persisted state; - upgrade with persisted schema/state; - no secrets in image history/layers/config/environment defaults; - no repository-local test keys copied into release image; - non-root/read-only-root behavior if selected by design; - bounded logs and sanitized startup failure; - exact packaged `zara-server` version/provenance query. ## Deployment examples After the image itself is proven, add minimal maintained examples rather than a giant orchestrator stack: - `docker run` / Podman example; - Compose example if it materially helps state/key mounts; - reverse-proxy guidance only if actually needed by a supported protocol path; - local-LAN deployment with authenticated CURVE client enrollment; - optional external Ollama/provider endpoints configured by secret/env/config mounts; - explicit note that client microphones/speakers stay on clients. Examples must never contain real secrets or encourage `--privileged`/host-device mounting without a concrete reviewed requirement. ## Server model/runtime assets Research deployment policy for STT/TTS/model assets: - build-time image asset vs runtime mounted/downloaded cache; - deterministic version/hash where assets are bundled; - image-size implications; - offline startup profile; - writable model cache location; - multiple image/profile possibility only if it avoids a giant all-in-one artifact without fragmenting runtime semantics. Do not silently download mutable unpinned code/models during every container startup if a reproducible/cacheable alternative exists. ## Plugin/tool policy in containers Tools and plugins remain available, but container location is part of capability semantics. - server tools/services may execute inside the container under explicit capabilities; - Linux/Android device actions must not accidentally execute inside the container; - user plugin mounts have explicit ownership/permissions; - plugin child processes remain bounded by existing plugin runtime policy; - no assumption that `xdg-open`, desktop apps, clipboard, screen or systemd host control exist in the image; - host administration must use explicitly designed APIs/capabilities rather than mounting `/var/run/docker.sock` or arbitrary host filesystem by default. ## Adversarial deployment matrix Cover: - world-readable mounted private key rejected; - missing/corrupt key registry; - read-only state path; - DB migration failure/recovery; - stale socket/lock after abrupt container death; - repeated restart loop; - malformed configuration; - resource exhaustion/low disk where deterministic simulation is possible; - incorrect published port/auth configuration; - untrusted plugin mount; - image scan proving no known fixture secrets/private credentials from repo; - client device action requested while only headless server capability exists -> typed unavailable/routed-to-client behavior once #150 lands. ## RAGE/TDD Each child follows repository RAGE with immutable start SHA/issue in Org log, adversarial research, explicit design/rejected alternatives/invariants, tests-first evidence, full Nix/repo gate, container-specific focused gate, changed-code coverage review and exact-head Actions. ## Ordered slices 1. #149 — Nix OCI image + GHCR publish workflow; 2. state/config/key volume contract + non-root/permission/read-only-root hardening; 3. deterministic container lifecycle/readiness/persistence/upgrade integration gate; 4. model/cache/plugin deployment policy and minimal Docker/Podman/Compose documentation; 5. final secret-leak/listener/capability-location deployment security gate. ## Completion rule A user can pull an immutable GHCR Zara image, mount fresh private configuration/state, enroll authenticated clients, run `zara-server` headlessly and persist/restart/upgrade it without secrets baked into layers, insecure public defaults, or device-local commands accidentally executing inside the container.
lost-rob0t commented 2026-08-22 22:00:05 +00:00 (Migrated from github.com)

Canonical child issue map

This epic reuses the already-created #149 as its first slice:

  • #149 — Nix-built zara-server OCI image + GHCR publish workflow
  • #180 — state/config/key mounts, non-root and read-only-root hardening
  • #181 — deterministic image lifecycle/readiness/persistence/restart/upgrade gate
  • #182 — model/cache/plugin deployment policy + maintained Docker/Podman/Compose examples
  • #183 — final secret/listener/provenance/capability-placement release gate

#183 additionally depends on #161 so the final container claim proves device actions do not accidentally execute inside the headless server. Do not create a duplicate OCI-image issue.

## Canonical child issue map This epic reuses the already-created #149 as its first slice: - [ ] #149 — Nix-built `zara-server` OCI image + GHCR publish workflow - [ ] #180 — state/config/key mounts, non-root and read-only-root hardening - [ ] #181 — deterministic image lifecycle/readiness/persistence/restart/upgrade gate - [ ] #182 — model/cache/plugin deployment policy + maintained Docker/Podman/Compose examples - [ ] #183 — final secret/listener/provenance/capability-placement release gate #183 additionally depends on #161 so the final container claim proves device actions do not accidentally execute inside the headless server. Do not create a duplicate OCI-image issue.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nsaspy/zara#153
No description provided.