Documentation
Docs
Zintus is a local-first, BYOK router that maximizes free-tier quotas across 12 AI providers from one OpenAI-compatible endpoint — with automatic same-model failover, cooldown, and quota-aware routing. Your keys live in your OS keychain (CLI/desktop) or your browser (web). There is no SaaS bill and no hosted control plane.
Quickstart
Build the CLI from source with Bun (npm publish coming soon), run the first-run wizard to add a free provider key, then chat.
$ git clone https://github.com/trustphoneapp/zintus && cd zintus
$ bun install && bun run --filter zintus build
$ zintus setup # add + validate free provider keys
$ zintus chat "Hello from Zintus"1. Install
There is no package-manager install yet — npm publish is coming soon. For now, clone the repo and build the CLI from source with Bun 1.2+ (see the terminal above): bun install && bun run --filter zintus build.
2. Add a key
zintus setup walks you through adding free API keys (Cerebras, Groq, Gemini, and more) and validates each one. Prefer doing it by hand? Use zintus keys set <provider> <key>. Keys are stored in your OS keychain — never sent to any Zintus server.
3. Chat
zintus chat "…" streams a response from the best available provider. When one runs out of quota or rate-limits, the router automatically fails over to the next. Run zintus serve to expose the gateway for the web, desktop, and mobile clients.
BYOK & providers
BYOK (Bring Your Own Key) is zero-knowledge: you sign up for free-tier keys and store them locally. The CLI and desktop app keep keys in your OS keychain (via @napi-rs/keyring); the web app encrypts them in your browser with Web Crypto (AES-256-GCM + PBKDF2) before localStorage; mobile uses SecureStore. Keys never reach a Zintus server. Use the id below with zintus keys set <id> <key>.
cerebrasFree tier · fast inferencegroqFree tier · rolling-window cooldown from x-ratelimit headersgeminiGoogle AI free tieropenrouterFree :free modelscohereFree trial tiermistralFree tierdeepseekBring your own keyfireworksBring your own keyxaiBring your own keyhuggingfaceBring your own keylmstudioLocal · no API key · LM_STUDIO_HOST (default http://localhost:1234/v1)ollamaLocal · no API key · OLLAMA_HOST (default http://localhost:11434)LM Studio and Ollama run on your machine and need no API key. Free-tier limits are set by each provider and change over time; check the provider for current quotas.
Gateway & API
The gateway is the single stateful brain the GUI clients connect to. It exposes an OpenAI-compatible endpoint, so you can point any OpenAI SDK at it by setting base_url to the gateway. Zintus routes to its own provider fleet — OpenAI is not a backend provider.
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8788/v1",
api_key="$GATEWAY_TOKEN", # your gateway token (or any value if unset)
)
stream = client.chat.completions.create(
model="llama-3.3-70b",
messages=[{"role": "user", "content": "Hello!"}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")Responses carry routing-metadata headers: X-Provider-Used, X-Cache-Hit (L1/L2/miss), and X-Failover-Count. Deviations from OpenAI: no created/usage fields; id is the internal routing trace id.
API reference
POST /v1/chat/completionsBearerOpenAI-compatible chat completion (SSE stream by default, or stream: false).
POST /v1/researchBearerStreaming deep research (decompose → search → synthesize). Needs TAVILY_API_KEY or SERPER_API_KEY.
GET /v1/statusBearerProvider inventory, key presence, cooldown, live quota, and savings.
GET /v1/savingsBearerEstimated USD a paid API would have charged for the free tokens served.
GET /v1/tracesBearerRecent routing traces (also /v1/traces/last and /v1/traces/{id}).
GET /healthPublicMinimal liveness probe; 503 while draining. No topology or savings leaked.
Full request/response schemas live in the OpenAPI 3.1 spec: docs/openapi.yaml. When GATEWAY_TOKEN is set, every endpoint except GET /health requires Authorization: Bearer <token>; /metrics is also available (Prometheus text or JSON), auth-gated only when a token is configured.
Self-host (Docker)
One command, no SaaS — the gateway runs locally and your keys stay on the host. Keys and quota.db persist in the zintus-data named volume; the container runs as the non-root bun user (uid 1000).
# 1. Routing policy (no secrets in it):
cp policy.example.json policy.json
# 2. Bring up the gateway on :8788 with a strong token:
GATEWAY_TOKEN=$(openssl rand -hex 24) docker compose up -d
# 3. Verify (minimal, unauthenticated liveness):
curl -s localhost:8788/health | jq # { "ok": true }
# 4. Auth-gated status + savings:
curl -s -H "Authorization: Bearer $GATEWAY_TOKEN" localhost:8788/v1/status | jqPrebuilt images publish to GHCR on each v* tag (ghcr.io/<owner>/zintus-gateway). A bind-mounted state dir must be writable by uid 1000 (chown 1000:1000), or use the named volume. Point any OpenAI-compatible client at http://localhost:8788/v1 with the bearer token.
Network-exposed checklist
The gateway binds 127.0.0.1 by default and refuses to bind a public interface without GATEWAY_TOKEN. Anyone who can reach the gateway and present the token can spend every provider key on the host — scope both accordingly.
GATEWAY_TOKENdefault: —Required for any non-loopback bind. Bearer token for all routes except /health. Generate with openssl rand -hex 24.
GATEWAY_HOST / GATEWAY_PORTdefault: 127.0.0.1 / 8788Set GATEWAY_HOST=0.0.0.0 only when you intend to expose it; prefer a TLS-terminating reverse proxy.
GATEWAY_RATELIMIT_RPMdefault: offPer-client request cap on /v1/chat/completions and /v1/research (429 + Retry-After). Set it on any exposed deployment.
GATEWAY_TRUST_PROXYdefault: unsetOnly when set is X-Forwarded-For trusted for rate-limit/IP keying. Leave unset unless behind a trusted proxy.
GATEWAY_CORS_ORIGINdefault: unsetRestrict to your web origin(s) if browsers call the gateway directly. No wildcard with credentials.
GATEWAY_REQUEST_TIMEOUT_MSdefault: 60000Connect / first-token timeout; aborts upstream and releases the quota reservation (408).
GATEWAY_STREAM_IDLE_TIMEOUT_MSdefault: 60000Mid-stream idle watchdog; aborts a stalled provider. 0 disables.
GATEWAY_DRAIN_TIMEOUT_MSdefault: —Graceful-shutdown bound: on SIGTERM/SIGINT /health flips to 503 draining, in-flight streams finish, then exit.
GATEWAY_MAX_BODY_BYTES / GATEWAY_MAX_MESSAGESdefault: —Bound request size / message count to limit abuse (413).
Always terminate TLS at a reverse proxy (Caddy/nginx/Cloudflare); the gateway speaks plain HTTP. Give your orchestrator a grace period ≥ GATEWAY_DRAIN_TIMEOUT_MS.
Cloud relay (optional)
Zintus Cloud lets you reach your home gateway from the mobile app (or zintus.app/dashboard) without re-scanning a QR code on every restart. It is still BYOK — your keys never leave the home machine. The relay is outbound-only: the home machine initiates the connection, so it works behind NAT and firewalls with no inbound ports. Only status JSON, control commands, and SSE events pass through; never API keys, raw chat messages, or router state.
Domains: zintus.ai is the marketing site, these docs, and the relay (relay.zintus.ai). zintus.appis the cloud account & dashboard you sign in to (sign-in emails come from @zintus.app). Your gateway runs locally on your own machine (localhost:8788 by default) — neither domain hosts it; the relay only brokers the connection.
$ zintus cloud login # sign in to zintus.app, save credentials
$ zintus serve --cloud # start gateway + connect to the relay
# then open zintus.app/dashboard — your gateway appears onlineManaged-key tiers (where Zintus holds the provider keys for you) are coming soon — the key-custody backend is not yet built, so those paid tiers are not purchasable today. The free, BYOK path above is fully functional.
Routing & policy
Provider priority, weights, model groups, fallbacks, and per-provider quota limits live in a single policy.json (repo root, ~/.zintus/policy.json, or $ZINTUS_POLICY). The gateway loads it at startup and hot-reloads on change — no restart needed. Every field is optional; missing fields fall back to built-in defaults. No secrets belong in this file.
{
"providerPriority": ["cerebras", "groq", "gemini", "fireworks", "openrouter"],
"providerWeights": { "groq": 7, "cerebras": 3 },
"modelGroups": { "llama-3.3-70b": ["groq", "openrouter", "fireworks"] },
"fallbacks": { "on_429": "next_provider", "on_5xx": "next_provider" },
"limits": {
"groq": { "requestsPerDay": 1000, "tokensPerDay": 100000, "requestsPerMinute": 30 },
"gemini": { "requestsPerDay": 1500, "requestsPerMinute": 15 }
}
}Start from policy.example.json (no secrets in it): cp policy.example.json ~/.zintus/policy.json, edit, and save — the running gateway picks it up. In Docker, mount it read-only: -v "$PWD/policy.json:/app/policy.json:ro". You can also override routing per request via the strategy, provider_weights, and virtual_key fields on /v1/chat/completions.
CLI reference
zintus setupFirst-run wizard: add API keys with validation.
zintus chat "<prompt>"Stream a chat response from the best available provider.
zintus "<prompt>"Shorthand for chat.
zintus serveRun the gateway HTTP server the GUI clients connect to (127.0.0.1:8788).
zintus statusLive dashboard of providers and quota usage.
zintus keys set <provider> <key>Store an API key in the OS keychain.
zintus keys listList stored API keys (masked).
zintus keys remove <provider>Remove a stored API key.
zintus configConfigure routing strategy via interactive wizard.
zintus doctorCheck health: keychain, quota DB, provider keys, Ollama, relay.
zintus historyList saved conversation threads.
zintus trace [traceId]Show the routing-trace waterfall for the last or given request.
zintus cloud loginSign in to zintus.app and save credentials to ~/.zintus/cloud.json.
zintus cloud statusShow cloud connection status.
zintus cloud logoutSign out and remove ~/.zintus/cloud.json.
License
Zintus is source-available under the Business Source License 1.1 — free for personal and internal business use. Contact YS Ventures LLC for commercial licensing.