Fifteen changes aimed at the first day of real use:
- Import and media jobs persist to <data>/jobs-*.json and reload on start
(running ones settle as errors); the Register button no longer vanishes
because the gateway restarted.
- Docker ships the cortiq binary (built in the builder stage) — local .cmf
chat models and image/video generation now work in containers, where
auto-install never could (no cargo in the runtime image). libvulkan1 added
for optional GPU offload.
- Open admin console on 0.0.0.0 without a token now warns in the log and
shows a dismissible banner — the VPS footgun is at least labeled.
- "Restart gateway" button in Settings: stops managed model servers, spawns
a fresh copy (bind retried while the port frees), Docker relies on the
container restart policy instead.
- Dashboard "System" panel: CPU sparkline + load, RAM (with the gateway's
own RSS), free disk at the models dir — sampled by sysinfo every 2 s.
- Featured model cards warn "not enough disk" / "may not fit in RAM" before
the download, from the same live snapshot.
- Log viewer in the sidebar: a 500-line in-memory ring fed by a second
ANSI-free tracing layer; filter, level, pause, copy.
- Update check: crates.io polled twice a day, the console shows an
"update available" notice.
- Media generations queue up (with position shown) instead of being
rejected; progress gained an honest ETA from measured throughput.
- Generation gallery under the image/video tabs: lazy PPM thumbnails,
click to reopen with seed/steps, delete.
- Playground chat is a real dialogue now: history is resent, bubbles,
Clear, Cmd/Ctrl+Enter.
- "Connect your app" snippets (curl / Python / JS) on the keys page, with
the real key inlined right after creation.
- One-click benchmark on the Models page via `cortiq bench --json`.
- Config export now downloads the actual gateway.toml (comments intact);
import accepts TOML (validated, hot-reloaded) and the legacy JSON.
- Streaming token counts are exact when the model server provides usage
(cortiq-server patch, shipped separately); the gateway fallback now
counts content chunks (1/token on cortiq) instead of chars/4.
Wizard's Finish lands on the Playground. 37 new strings in all 7 languages.
Startup no longer needs arguments: the config is created on first launch, a busy
port is swapped for a free one, the console is open by default, and the browser
lands on a setup wizard that downloads a ready-made .cmf model and wires it into
routing (managed server + routing.default) when you press Finish.
Download progress was reported from curl's own meter, which prints a full
0→100% bar for the -L redirect hop before the transfer starts — so every job
showed 100% and then hung. Progress now comes from bytes on disk against the
size from the HuggingFace tree (or a HEAD request).
The UI and the gateway also disagreed about the output file name, so a finished
download never marked its card as downloaded; the gateway now returns the exact
stem and the UI sends it back verbatim for download, progress and delete.
Featured models are read live from the infosave HF account in both the wizard
and Import, and all 432 UI strings now exist in every one of the 7 languages.
- New Advanced controls on the import page: O(1) attention spec (off / all /
deep N / explicit layer list) plus the --o1-m / --o1-window / --o1-sink knobs
with validated defaults (32 / 128 / 4) and explanatory hints.
- Backend passes --o1* to the native `cortiq convert`; fails fast with clear
messages when combined with Python-only options, used on GGUF imports, or
when the installed cortiq predates 0.2.0 (the hint needs the new runtime).
- The hint is recorded in the .cmf header (weights byte-identical); the runtime
applies it at load. Verified end-to-end: converted a model with --o1 all and
the runtime logged "o1 nystrom attention: 2/2 layer(s)".
- Stats snapshot now computes the KPI totals AND the breakdown from the
in-window per-minute buckets (previously they used all-time accumulators, so
switching 1h/24h/7d changed only the chart, not the numbers). Buckets now
carry failovers + per-group breakdown; Prometheus keeps all-time counters.
- New DELETE /admin/api/stats clears all in-memory stats and truncates the
JSONL log; the dashboard gets a "Clear logs" button (with confirm).
- The model id is now auto-filled from the model name as you type, and if left
blank it's derived on save (slug of the last path segment, e.g.
"qwen/qwen3.7-max" → "qwen3-7-max") — saving no longer errors on a blank id.
- probe/test auth failures (401 / "User not found" / "No auth") now append a
hint to check the API key (for OpenRouter: use an inference key, not a
provisioning/management key).
- ui.js: mount() now wraps non-node children in text nodes (like h()), so
mount(el, "") no longer throws in appendChild — this is what broke the
"Add model" form (applyDefaults cleared a status via mount(x,"")).
- probe_model: resolve managed local models too (they are synthesized into the
registry, not [[models]]) — probing them no longer returns "model not found".
- models.js: await reload() after delete so the row is removed immediately.
- Providers: add openrouter (https://openrouter.ai/api/v1) and lmstudio
(http://localhost:1234/v1), both OpenAI-compatible. Selecting a provider now
auto-fills its verified base URL, cost tier and default capabilities.
- API key is typed directly in the form and stored securely as a secret under the
provider's env name — no manual env var to manage.
- New POST /admin/api/provider/models validates the key and lists the provider's
models; a "Test & load models" button fills a searchable dropdown. The model
field now sits below the key (some providers list models from the key).
- Fuller capability set: tools, vision, audio, reasoning, json, caching.
- New GET /admin/api/cmf/files lists the .cmf files (and tensor-dir .cmf) under
models_dir, with sizes.
- Settings → Local models: the per-row model field is now a dropdown of those
files instead of a hand-typed path, and auto-fills the id from the filename.
- The gateway can now run several local .cmf models at once: a [[cmf.servers]]
list, one supervised `cortiq serve` per entry on its own port, each registered
in the pool and independently routable/pinnable. The single legacy
local_model/local_port still works (back-compat via effective_servers()).
- Settings → Local models is now a rows editor (id · path · port + check · threads
· GPU) with add/remove and per-server serving status.
- Deleting a managed model stops just its server and removes its file — unless
another model still points at the same file (shared-file guard).
- A pinned playground model that is unavailable now errors clearly instead of
silently answering from the managed local model (route_source no longer lies).
- The managed CMF model is surfaced in GET /admin/api/models (marked "managed",
shown read-only in the UI) so the real running model is visible and pinnable
without a hand-maintained [[models]] entry; a fresh install still lists nothing.
- Deleting a model now removes its local .cmf physically (file or tensor-dir);
deleting the managed model also stops the server and clears the [cmf] config.
Routing is stripped of the deleted id so the config still validates.
- Settings: "?" help tooltips on every option; router key auto-probes the
connection right after saving (no more hanging "unavailable")
- Local models card reworked into 3 clear steps (install cortiq → add model →
run locally); removed the hand-entered .cmf path / pool-id fields — models
are added via the Import tab and the managed server registers them
- Fresh install ships an EMPTY model pool; the local .cmf is auto-registered by
the managed CMF server, so it needs no static [[models]] entry
- New: port-availability check — GET /admin/api/cmf/port + "Check port" button
with next-free-port suggestion. bind()+connect() probe so Docker-proxied /
dual-stack ports (e.g. 8081) are correctly detected as occupied
- /admin/api/cmf now reports the serving model + port; card shows "▶ serving …"
- On-demand runtime install: POST /admin/api/cmf/install
The crates.io/release build shipped a STALE frontend (committed web/js was behind
the working tree), so the admin console hung on load. Commit the current working
web/js (app, api, dashboard, login, import view, i18n de/es/fr/tr/zh, css) and the
backend WIP (import.rs, admin/assets.rs) so the released console matches the dev
build. Verified: clean `cargo build --release`, empty-pool start, working
dashboard served.
- GET /v1/models: pool + virtual cortiq-auto
- POST /v1/completions (legacy): non-stream + streaming text_completion
- POST /route: native passthrough returning the routing decision
- all inbound protocol adapters now implemented; admin meta + README/CHANGELOG updated
- bump to 0.2.1
- release.yml: add x86_64-pc-windows-msvc target, OS-aware packaging (.exe + .zip)
- ci.yml: split into lint (fmt/clippy) + test matrix (ubuntu/windows/macos)
- use getrandom for the admin/key token CSPRNG (secure on Windows too)
- bump version to 0.1.0; add crates.io metadata (include list, homepage, rust-version)
- add .github/workflows/release.yml: on tag v* → publish to crates.io + GitHub Release with binaries
- README: absolute links/images and badges so it renders on crates.io; add install section
- CHANGELOG: 0.1.0