Synth
A zndx-constellation project: a federated gRPC engine with thin CLI, TUI, and MCP clients, a keiretsu-themed web UI with ghostty terminal control, and an airgap-ready Zarf deployment path.
Scope decisions (2026-08-03):
- Federation happens only via the signals-protocol
(
zndx.engine.v1.Engine); Apache Kudu, Atlas, and Ranger integration is deliberately deferred to thesignalsproject. - Submodules live under
components/(ratified over theexternal/naming used by gaius/atelier):oss-deluge,kvasir,signals-protocol. - Secrets are declared with secretspec and stored via SOPS/age.
- SBOM presence is asserted by the Zarf closure gate — a deliberate raise over the older repos.
Engine-First
The doctrine, inherited from gaius: the engine is the nervous system; CLI, TUI, and MCP are thin clients. A client does exactly three things:
- parse input,
await client.call(service, action, params),- format output.
No business logic in clients, no local algorithms, no engine-bypassing
fallbacks, no heavy dependencies (models, embedders). The CLI doubles as
the verification harness: every engine feature must be reachable and
verified through synth-cli.
The client layer (src/synth/client/) is the only import path clients
take: a generic GrpcEngineClient.call() plus typed proxies.
Federation
Synth federates via the signals-protocol
(components/signals-protocol), never via direct integration with peer
engines’ native surfaces.
The core law (learned 2026-07-03, first live cross-engine call): gRPC
method paths embed package+service, so a wire-identical peer still returns
UNIMPLEMENTED if only the native service is registered. Therefore
serve() registers both servicers on one port:
synth.engine.SynthEngine— the native, project-internal surface (src/synth/engine/proto/synth_engine.proto);zndx.engine.v1.Engine— the shared federation face (Complete/Status/Remediate).
Shared-host lattice
| Engine | gRPC port | vLLM range |
|---|---|---|
| gaius | 50051 | 8080+ |
| aegir | 50151 | 8100+ |
| atelier | 50251 | 8200+ |
| synth | 50351 | 8300–8399 |
Ratifying synth’s row in the signals-protocol adopters table is a pending follow-up (additive change to the submodule’s spec, PR’d upstream).
GPU co-tenancy uses the advisory lease directory /tmp/zndx-gpu-leases;
capability requests are forwarded to peers in preference to moving GPUs.
Development Environment
devenv (Nix) is the substrate; devenv.nix stays a pure service
declaration (operational logic lives in the justfile).
| Service/process | Port(s) | Notes |
|---|---|---|
| postgres 16 | 5566 | pgvector; DB synth; lattice: cyberphy 5438 · gaius 5444 · signals 5455 · atelier 5533 · aegir 5555 |
| qdrant (process) | 6365 http / 6366 grpc | readiness probe on /healthz; house norm is a process, not a service |
| rustfs | 9000/9001 (allocated) | MinIO replacement; always read RUSTFS_ADDRESS etc. at runtime — devenv allocates the real ports |
First devenv shell builds RustFS from source (flake input
github:rustfs/rustfs) — a one-time cost unless the store already has it.
Secrets: declared in secretspec.toml, provided per environment
(devenv.yaml defaults to the dotenv provider; SOPS/age for committed
encrypted files under config/secrets/).
Configuration
HOCON config/base.conf is the single source of truth. Env vars enter
only via ${?VAR} capture; application code reads
synth.config.load_config() and never os.environ.
The ritual:
just resolve-config # materialize -> build/config/synth.{env,json}
just preflight # deny/warn validation (bin/preflight.py)
just policy # conftest gate (policy/environment/synth.rego)
Preflight and the rego policy express the same deny/warn taxonomy, so the gates hold both in-process and in CI. Every deny message names its remediation.
Deployment (Zarf)
Airgap-ready packaging per the cyberphy laws — CLOSURE, CONSERVATION, RECONCILIATION, PARTIAL ROLLOUT:
- Everything the deploy needs is declared once in
zarf/artifacts.manifest.jsonand verified at build time byzarf/scripts/check-closure.py(just zarf-build) — nothing is discovered missing inside the air gap. - SBOMs are required. Never pass
--skip-sbom; the closure gate asserts the SBOM entry in the built package. This is a deliberate raise over the older repos (aegir skips SBOMs; cyberphy generates but never asserts). - Realized
*.closure.jsonfiles are committed per release as provenance. - Size budget: 2 GiB hard (GitHub release asset limit), warn at 1.75 GiB.
Deployment modalities follow the house taxonomy — laptop (k3d + Tilt) →
workstation (RKE2) → airgap — with policy/k8s/<modality>/ gating each as
it lands.
Open item: cosign/GPG signing (the whole constellation is SHA256SUMS-only today).