Architecture
chatz is a servicepack-based application. Servicepack supplies the process lifecycle and the in-process service manager. chatz registers one HTTP service that owns the API, the embedded web app, the database connection, the MCP manager and the model upstream registry.
The HTTP service
internal/pkg/services/http-server builds everything when the run command starts it, in this order: parse the configuration, connect the database and run migrations, build the secrets box and the metrics registry, discover models on every upstream, set up the readiness snapshot, start connecting the enabled MCP servers in the background, and load the embedded web app. A configuration error at any step stops startup; an unreachable upstream or MCP server does not.
| Package | Role |
|---|---|
internal/pkg/http/server | echo router, session middleware, handlers over the generated strict interface, the error envelope and the web app fallback. |
internal/pkg/core/auth | Accounts, bcrypt passwords, session tokens, setup and passwordless login. |
internal/pkg/core/chats | Durable turns: model or demo selection, per-chat locks, history selection, SSE streaming, checkpoints and persistence. |
internal/pkg/core/chat | Adapts elelem’s tool-calling loop to the SSE stream. |
internal/pkg/upstreams | Model discovery, routing a model ID to its upstream, concurrency slots, timeouts and health. |
internal/pkg/mcp | MCP connections, retries, tool routing and .mcp.json import. |
internal/pkg/db | Postgres and SQLite connections, embedded migrations, models and generated repositories. |
internal/pkg/secrets | AES-256-GCM sealing of stored MCP credentials. |
internal/pkg/operations, metrics, usage, logging | Readiness, Prometheus metrics, per-call usage rows, the log file sink and redaction. |
web/ | The SvelteKit web app, embedded into the binary through internal/pkg/webassets. |
A chat message travels: browser, session cookie, handler, core/chats (history and settings), elelem driver with the chat’s MCP tools, upstream. The reply streams back as SSE frames while checkpoints are saved, and the finished turn is written in one step. The turn rules are in the chat orchestration README.
Runtime shape
cmd/main.go
├─ set global log scope: binary, commit
├─ services.Init() generated factory registration
└─ Cobra "run"
└─ pkg/runner
├─ signal / parent-context handling
└─ internal/app.App
├─ pre-run hooks
├─ internal/pkg/service-manager
│ └─ service factories → services running concurrently
└─ post-stop hooks
cmd/main.go is intentionally small: establish process identity, register generated service factories, expose run and command namespaces, then pass control to the runner. The runner README explains the signal and deadline rules.
internal/app owns application-level hooks and delegates service execution to the service manager. App is the place for whole-process behavior; individual services should not reach across into siblings to create their own lifecycle graph.
Deployment shape
One Chatz container runs one binary. The binary serves the versioned JSON/SSE API and the static SPA from the same origin. Postgres is the default store; SQLite is available for a single process using one local Docker volume. See deployment for running it.
The HTTP service constructs the database, generated repositories, auth service, MCP manager, upstream registry, and web-asset server. They run in the same process and share its cancellation path. Adding separately deployed workers or other processes would require explicit network or queue contracts. A Servicepack dependency declaration only orders in-process services.
Source ownership
| Path | Role | Ownership after make own |
|---|---|---|
cmd/main.go | Process entry point and root CLI. | Framework |
cmd/init.go | Extra handlers and application hooks. | Project |
cmd/commands.go | App-level CLI commands. | Project |
internal/app/ | App lifecycle wrapper. | Framework |
internal/pkg/service-manager/ | Concurrency, dependency, retry, and stop semantics. | Framework |
internal/pkg/services/ | Business services and services.gen.go. | Project; generated registration is not hand-edited. |
pkg/runner/ | Signal-aware runner. | Framework |
scripts/make/servicepack/ | Updateable Make implementations. | Framework |
scripts/make/ | Project-specific target overrides. | Project |
Makefile.servicepack | Framework Make target definitions. | Framework |
Makefile | Project targets and overrides. | Project |
Framework-owned means an update may replace it. Project-owned means the update’s normal exclusion policy preserves it. See framework updates before changing that boundary.
Registration and lazy construction
make service-registration runs the generator that discovers Service implementations and writes internal/pkg/services/services.gen.go. Chatz uses that generated registration for its HTTP service. The generated code registers factories rather than fully constructed services.
That distinction matters:
runinstantiates enabled factories;- a per-service Cobra command instantiates only that one service;
- connections and config parsing happen in a service’s
New, not at package import time.
This keeps command execution from accidentally opening every database/client in the project. Details are in the service-manager README.
Observability and configuration
Logging starts with the slogging handler setup and flows through ctxscope. The binary and build commit are global scope; the service manager adds a service field while it runs or stops a service. Preserve and extend the context passed into Run; it carries cancellation and those fields together.
Configuration is typed and parsed at each service boundary with gonfiguration. Every chatz setting is on the configuration page, and .env.example has the usual ones.
Build and test topology
The Makefile runs tooling in Docker. Test targets additionally receive Docker access for Testcontainers. The development model, coverage boundary, and override mechanism are documented in development, with the exact script behavior in the Make-script README.