π¦ Nika 0.58.0 β Serve V2
π¦ Nika 0.58.0 β Serve V2
Inference as Code Β· March 31, 2026 Β· 26 commits
| π§ͺ Tests | π§ Builtins | π¦ Transforms | π Providers |
|---|---|---|---|
| 9,109 | 35 | 31 | 9 |
β§ infer Β· β exec Β· β fetch Β· β invoke Β· β agent
β‘ Production-Grade Serve
v0.56.0 introduced nika serve β a REST API for running workflows over HTTP. It worked. It also had a UUID collision bug at 4.2M jobs, leaked environment variables to subprocesses, panicked when workers crashed, and let two instances corrupt the same database.
v0.58.0 fixes all of that. This is Serve V2: the version you'd actually deploy behind a load balancer.
ποΈ The Architecture
nika serve now supports two execution modes. Choose based on your deployment model:
| Mode | How it works | Best for |
|---|---|---|
| EmbeddedExecutor | Workflows run in-process, inside the Axum server's tokio runtime | Single-machine deployments. Low latency. Shared memory. |
| SubprocessExecutor | Each job spawns nika run as a child process with process-group isolation |
Multi-tenant. Crash isolation. Memory limits per job. |
# Embedded (default) β in-process, fast
nika serve --bind 0.0.0.0:3000
# Subprocess β isolated, safe for multi-tenant
NIKA_SERVE_EXECUTOR=subprocess nika serve --bind 0.0.0.0:3000The job queue is now atomic. A single AtomicUsize counter replaces two separate database queries for checking concurrency limits. When 20 requests arrive simultaneously, exactly max_concurrent get through β not 20.
π NikaVault v2 β Encrypted Secrets at Scale
Secrets management got a complete rewrite. NikaVault v2 stores versioned, multi-field credentials encrypted with XChaCha20Poly1305 + Argon2i key derivation. But the real feature is the $vault binding source:
tasks:
- id: call_api
with:
key: $vault.ELEVENLABS_API_KEY
fetch:
url: "https://api.elevenlabs.io/v1/text-to-speech"
headers:
xi-api-key: "{{with.key}}"No more $env.SECRET_KEY leaking through process environment. Vault secrets are resolved at binding time, never exposed to subprocesses or traces.
Manage from the CLI:
nika vault set ELEVENLABS_API_KEY sk-xxx-your-key
nika vault list
nika vault get ELEVENLABS_API_KEY
nika vault delete ELEVENLABS_API_KEYFor teams, the Doppler backend connects NikaVault to your existing secrets infrastructure:
export NIKA_VAULT_BACKEND=doppler
export DOPPLER_TOKEN=dp.st.xxx
# Vault reads from Doppler, writes stay localEvery vault operation is logged in an audit trail β who accessed which secret, when.
π‘οΈ Four Security Patches
These aren't theoretical. They were found during a 10-agent adversarial audit of the serve layer.
π CORS Lockdown
Before: CorsLayer::allow_origin(Any) β any website could make authenticated requests to your Nika server via browser JavaScript. Classic CSRF vector.
After: No CORS headers by default. Opt-in with NIKA_SERVE_CORS_ORIGIN=https://your-app.com.
π Auth Token Minimum
Before: NIKA_SERVE_TOKEN=abc was accepted. Three characters. Brute-forceable.
After: Minimum 16 characters. Empty or short tokens rejected at startup with a clear error message.
π Environment Isolation
Before: Worker subprocesses inherited the server's full environment via env_remove denylist. Any new secret added to the server leaked to workflows automatically.
After: env_clear() + explicit allowlist. Only PATH, HOME, API keys, and NIKA_* vars pass through. Everything else is blocked.
ποΈ Database Locking
Before: Two nika serve instances on the same machine could both write to the same SQLite database simultaneously, causing corruption.
After: flock() on <db>.lock enforces single-instance per database file.
β‘ SSE Streaming + Job Lifecycle
Real-time job progress via Server-Sent Events. Connect with Accept: text/event-stream and get structured events as tasks execute:
# Submit a job and stream events
curl -N http://localhost:3000/v1/events/JOB_ID \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: text/event-stream"
# Events:
# data: {"type":"TaskStarted","task_id":"research"}
# data: {"type":"InferChunk","text":"The key finding..."}
# data: {"type":"TaskCompleted","task_id":"research","duration_ms":2340}
# data: {"type":"JobCompleted","output":"..."}Job garbage collection runs automatically. Completed and failed jobs older than the configurable TTL (default: 7 days) are cleaned up. No manual maintenance.
Cancel kills the process. Before v0.58.0, cancelling a job only killed the tokio task β the child process kept running. Now we store the child PID in WorkerHandle and send SIGTERM, then SIGKILL after a grace period. On shutdown, workers get SIGTERM via tokio::select! instead of running until the 30-second drain timeout.
π Performance: mimalloc
Switched from the system allocator to mimalloc. Consistent performance across macOS, Linux, and Windows. Particularly noticeable under high concurrency where the system allocator's per-thread arenas fragment.
π§ Every Fix in Detail
| Bug | Impact | Fix |
|---|---|---|
| UUID collision | 44-bit IDs collide at ~4.2M jobs | Full 128-bit UUID (32 hex chars) |
| WorkerGuard panic | Crashed workers left stale state | Drop guard: mark failed + decrement counter + cleanup map |
| Queue race | 20 simultaneous requests all pass concurrency check | AtomicUsize counter replaces dual DB queries |
| Unbounded output | Verbose workflows cause OOM | Piped reads capped at max_output_bytes |
| Shutdown leaks workers | Workers run until 30s drain | tokio::select! races subprocess vs shutdown signal |
| Cancel leaks subprocess | abort() only killed tokio task |
Store PID, send SIGTERM/SIGKILL to process group |
| Cancel race | Completed job overwritten as cancelled | Check cancelled before writing terminal status |
| Drain timeout | Workers detached after 30s, not aborted | Abort remaining handles after drain |
| Windows child.kill() | Moved child reference | Restructured to piped reads + child.wait() |
| Inputs silently dropped | RunRequest.inputs accepted but ignored |
Forward as --input key=value CLI args |
| Interactive hang | Subprocess prompts block forever | Added -y --no-interactive flags |
| Deprecated set_var | std::env::set_var() deprecated |
Direct ServeConfig construction |
π By the Numbers
π Serve V2 β What Changed
βββ π Security patches: 4 (CORS, auth, env, flock)
βββ π Reliability fixes: 12 (every race condition found)
βββ β¨ New features: 10 (vault v2, SSE, GC, mimalloc, ...)
βββ π§ͺ New tests: +16 (9,109 total)
βββ ποΈ Architecture: EmbeddedExecutor + SubprocessExecutor
π¦ Install
| Method | Command | |
|---|---|---|
| π | Quick | curl -fsSL https://raw.githubusercontent.com/supernovae-st/nika/main/install.sh | sh |
| πΊ | Homebrew | brew install supernovae-st/tap/nika |
| π¦ | npm | npx @supernovae-st/nika |
| π¦ | Cargo | cargo install nika |
| π³ | Docker | docker run --rm ghcr.io/supernovae-st/nika:0.58.0 |
| π» | VS Code | Search "Nika" or ext install supernovae.nika-lang |
| πͺ | Scoop | scoop bucket add nika https://github.com/supernovae-st/scoop-nika && scoop install nika |
| π§ | AUR | yay -S nika-bin |
π¦ Nika Evolution
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
v0.42 ββββββββββββββββββββββ 8,188 tests
v0.48 ββββββββββββββββββββββ 8,200 tests
v0.52 ββββββββββββββββββββββ 8,938 tests
v0.56 ββββββββββββββββββββββ 9,093 tests
v0.58 ββββββββββββββββββββββ 9,109 tests β you are here
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π§ 35 builtins Β· π¦ 31 transforms Β· π 7+1+1 providers Β· π¦ 15 crates
Made with π by SuperNovae Studio β Open Source, AGPL-3.0
Full Changelog: v0.56.2...v0.58.0