All notable changes to Otari will be documented in this file.
The format follows Conventional Commits.
0.5.1 - 2026-09-08
- a11y: Stop sidebar nav disclosures from emitting document headings in #951 by @AloysJehwin (
8722739) - lint: Strip block comments before matching arbitrary font-size rule in #941 by @AloysJehwin (
3146bea) - settings: Prevent database_url value from overflowing Settings card in #943 by @AloysJehwin (
83d4c24) - gateway: Let the platform health probe reach a route outside base_url's own path in #964 by @macaab26 (
2e0f885)
Full Changelog: https://github.com/mozilla-ai/otari/compare/v0.5.0...v0.5.1
0.5.0 - 2026-09-08
- providers: Honor the optional API key for keyless custom endpoints in #423 by @njbrake (
5fa80d0) - dashboard: Accessibility and UX polish pass in #435 by @njbrake (
b153949) - dashboard: Address timeline review follow-ups from #445 in #453 by @njbrake (
75556a9) - dashboard: Distinguish a missing /models endpoint from an unreachable provider in #454 by @njbrake (
ab4d3ca) - providers: Gate provider api_base write path behind SSRF check in #444 by @njbrake (
cb5d6ec) - otlp: Price imported cache creation at the 5m rate, not 1h in #468 by @njbrake (
ad4b815) - models: Let a keyless local provider opt into discovery in #471 by @njbrake (
6872df3) - pricing: Follow genai-prices to the live v2 price feed in #466 by @HareeshBahuleyan (
3773675) - dashboard: Clear a provider's connection-test result when it changes in #467 by @njbrake (
2286e0a) - lint: Apply every enclosing layer's rules in the architecture check in #476 by @njbrake (
e4c79d1) - errors: Classify provider billing exhaustion instead of calling it a bad request in #483 by @njbrake (
fc41d02) - hybrid: Prevent pending cost calculations when fallback attempts stop in #498 by @HareeshBahuleyan (
60864af) - dashboard: Correct the routing copy for mid-plan stops and unloaded plans in #507 by @njbrake (
e7edbdf) - dashboard: Open the onboarding quickstart link in a new tab in #508 by @njbrake (
ef9312e) - dashboard: Report a failed provider query on the Overview page in #509 by @njbrake (
7b56ce7) - hybrid: Re-raise CancelledError in platform attempt walker in #511 by @njbrake (
e7d9370) - sdk-codegen: Allow non_snake_case in the generated Rust core in #515 by @njbrake (
65ada5f) - sdk-codegen: Make the gateway the source of truth for endpoint coverage in #516 by @njbrake (
af2137c) - ci: Flag regen PRs on failing checks, not just age in #518 by @njbrake (
f9c59f7) - errors: Return the provider's own message on a caller-fault rejection in #537 by @njbrake (
8b4f2b4) - billing: Record charge lines for embeddings, images, moderations, and rerank in #542 by @AmirF194 (
98cf54f) - router: Match routing-memory scores on canonical model identity in #547 by @njbrake (
f75e5b0) - routing: Fall back on every provider failure in #546 by @njbrake (
0024769) - billing: Audio charge lines, no token defaults on non-token routes in #550 by @njbrake (
1ab8a4b) - dashboard: Stop the expanded table row remounting on every poll in #558 by @njbrake (
ee2aadd) - pricing: Match genai-prices defaults for renamed instances and vendor-prefixed models in #559 by @pss-julien (
c0cff4a) - chat: Forward service_tier to the provider instead of dropping it in #561 by @pss-julien (
17c8a8d) - models: Tolerate a provider that reports no model
createdin #562 by @pss-julien (659fa79) - dashboard: Fit the share card's hero to its value and rebuild the wide layout in #566 by @njbrake (
4c7be2f) - usage: Pin offset-less usage window bounds to UTC in #570 by @njbrake (
cd58170) - web-search: Serialize extraction and return its retained heap in #571 by @njbrake (
1e5e908) - dashboard: Bound and await sign-out so it can't clobber a fresh sign-in in #573 by @champ18ion (
c106d63) - Emit Node-compatible imports in TypeScript SDK codegen in #575 by @Copilot (
9458131) - code-execution: Three wrong verdicts in the conformance check in #602 by @khaledosman (
ead01fc) - e2e: Wait for the provider form to close, not for a button to be relabeled in #612 by @njbrake (
5522263) - auth: Move the API key format check off the verify path in #665 by @njbrake (
49aeeeb) - migrations: Rejoin the two Alembic heads so upgrades run again in #673 by @khaledosman (
e798d3e) - batches: Price a batch one request at a time by @njbrake (
0b40722) - pricing: Round a batch once, and coerce every rate reader by @njbrake (
e44c432) - pricing: Chain the money migration onto the current head by @njbrake (
5c2f348) - pricing: Keep cached tokens in batch pricing, reserve the dearest rate in #683 by @njbrake (
6c4f83a) - mail: Correct three comments that describe code this PR changed by @njbrake (
dbe5cf2) - mail: Make mail readiness independent of startup validation by @njbrake (
8db9ef3) - mail: Use the right article for the role in an invitation by @njbrake (
3472d64) - mail: Sanitize the envelope addresses, and work the review findings by @njbrake (
cdea86c) - pricing: Map the override write race to 409, and agree on the canonical key in #677 by @khaledosman (
f84f3fa) - auth: Match a held address by casing, and revoke a deactivated identity's sessions in #701 by @khaledosman (
97ad75f) - dashboard: Wire the password hints to their inputs, and keep the button focusable by @khaledosman (
3d760e5) - dashboard: Clear the saved line when the password fields are retyped by @khaledosman (
b03016b) - dashboard: Let a claim correct the bootstrap the whole tab reads by @khaledosman (
f1f809e) - dashboard: Open the organization rail inside the mobile drawer by @jigjigjig (
24f970d) - dashboard: Preserve focus across navigation breakpoints in #712 by @jigjigjig (
3464a78) - deps: Cap mcp below 2.0.0, which drops streamablehttp_client in #699 by @champ18ion (
ca12db9) - dashboard: Login design improvements in #733 by @jigjigjig (
d3cd90d) - dashboard: Stop the bulk action bar sliding in from the left in #735 by @jigjigjig (
7d7eb59) - auth: Gate master-key sign-in on the operator's password, not on any in #746 by @njbrake (
2c06b78) - auth: Lock the identity row before redeeming a verify or reset token in #729 by @AmirF194 (
0dfb88d) - usage: Reserve the otari-ai: source prefix in external-usage ingest in #772 by @khaledosman (
38efb1f) - usage: Read a backfilled hosted row as usage this deployment served in #773 by @khaledosman (
cb0c62e) - guardrails: Let a mandated endpoint's failure follow its own on_unavailable setting in #764 by @njbrake (
5a9c152) - gateway: Stop purpose-hint injection from corrupting list-shaped system content in #730 by @shoemoney (
899a8d1) - tools: Address review on the sandbox image and tool-set policy by @njbrake (
6857615) - tools: Make the storable tool rule and the admission rule one rule by @njbrake (
3b705d5) - dashboard: Keep a stale tool policy the deployment is refusing by @njbrake (
1e603eb) - migrations: Re-point onto main's head after the usage-provenance migration in #766 by @njbrake (
111f62c) - dashboard: Stop the pricing alarm from pushing the shell down in #778 by @jigjigjig (
e2f8ed4) - dashboard: End the create-workspace flow inside the new workspace in #780 by @jigjigjig (
e3a5e8c) - api: Expose prompt cache keys in #777 by @HareeshBahuleyan (
1e29bbb) - mcp: Reject duplicate MCP server names before they collapse silently in #792 by @AmirF194 (
6eade83) - gateway: Address Gateway 502s on max_completion_tokens (OpenAI's current field name) in #769 by @aittalam (
e676ced) - dashboard: Repair the text-subtle contrast failure, and retune the type scale in #807 by @jigjigjig (
b37d7b7) - dashboard: Anchor the filter dropdowns under their trigger in #800 by @jigjigjig (
c070c6d) - api: Gate the deployment-wide management plane on operator authority in #821 by @njbrake (
eb900b8) - dashboard: Point hosted request snippets at the data-plane gateway in #825 by @njbrake (
8d225d0) - api: Give hosted mode a management-only posture in #824 by @njbrake (
b4ed34f) - config: Refuse a data_plane_url that already names /v1 in #828 by @njbrake (
1d7bd27) - api: Answer a HEAD probe from the mode stub routers in #831 by @khaledosman (
6ddc8f8) - dashboard: Hand an accepted invitation off to the claim form in #841 by @njbrake (
fb391e8) - dashboard: Take the sidebar's operator answer off the membership context in #852 by @khaledosman (
ae2910e) - dashboard: Report provider-key encryption on a tenant-readable contract in #851 by @khaledosman (
140292b) - dashboard: Serve the Tools pages to non-operators in #864 by @khaledosman (
b00696b) - dashboard: Withhold operator-only budget reads from the workspaces page in #862 by @khaledosman (
01b0d28) - dashboard: Land every signed-in caller on a usage overview in #863 by @khaledosman (
fec213c) - dashboard: Put the last six bare headings on the type scale in #865 by @khaledosman (
4f96eff) - dashboard: Name the person at the foot of the sidebar, not their role in #869 by @njbrake (
6f7cf23) - dashboard: Generate the PWA manifest from the build's base path in #858 by @khaledosman (
e6dd017) - tenancy: Gate the org provider-keys list on organization management in #873 by @khaledosman (
7aa952f) - dashboard: Open Model pricing to organization admins in #872 by @njbrake (
9b0760c) - dashboard: Clear the accessibility debt in the key reveal, confirms, type scale and touch targets in #890 by @khaledosman (
b40c33e) - hybrid: Map PLATFORM_HEALTH_PATH and trust only a 2xx from the platform probe in #888 by @njbrake (
61a670e) - dashboard: Decide the Overview's variant from the organization context in #894 by @khaledosman (
6c6f204) - api: Gate the deployment-wide routers on the router, not per route in #895 by @khaledosman (
d38a633) - api: Refuse to cap a gateway user at an organization's budget in #898 by @khaledosman (
60a5d8c) - db: Hand the request's connection back before the provider call in #911 by @njbrake (
925e115) - migrations: Run otari's chain only against otari's own database in #919 by @khaledosman (
5018b64) - api: Refuse a blank provider narrowing on POST /v1/scoped-budgets in #912 by @champ18ion (
4d767d1) - messages: Preserve Anthropic container field in #916 by @HareeshBahuleyan (
1ca6ac4) - usage: Scope /v1/usage/count to imported rows for the select-all affordance in #840 by @AloysJehwin (
3a8500b) - messages: Adopt any-llm container support in #938 by @HareeshBahuleyan (
70e3544) - streaming: Relax final forwarded-tool timeout in #827 by @HareeshBahuleyan (
0edd6ab) - mcp: Stop managed transport redirects in #948 by @HareeshBahuleyan (
d894843) - dashboard: Correct two sidebar defects and inline the record copy control in #956 by @jigjigjig (
4bf2a88) - files: Document binary download responses in #949 by @Ovvomii (
d5d355b)
- files: Add S3-compatible object storage backend in #411 by @champ18ion (
3f4f8dc) - dashboard: Bundle the operator user guide and serve it at /#/docs in #434 by @njbrake (
8d1c1e9) - auth: Rate-limit dashboard sign-in against brute-force attempts in #437 by @champ18ion (
2657bc9) - providers: Add opt-in SSRF gate for provider api_base in #441 by @njbrake (
a660931) - web-search: Support recency filtering and publish dates in Brave adapter in #442 by @champ18ion (
09fe531) - dashboard: Make the activity histogram the time-range selector in #445 by @njbrake (
ffad6b5) - dashboard: Install the dashboard to a home screen with the Otari icon in #450 by @njbrake (
e646444) - aliases: Scope a model alias to a single user in #455 by @njbrake (
dbbfebc) - dashboard: Surface failing requests so admins see dropped traffic in #449 by @njbrake (
0b37186) - lint: Enforce gateway layer rules via check_architecture.py in #475 by @peteski22 (
f348d60) - observability: Log the remaining gateway-side rejections in #472 by @njbrake (
1a55d9f) - usage: Break spend down by session, endpoint, and provider in #469 by @njbrake (
9d1b3e6) - search: Add a billed POST /v1/search pass-through in #473 by @njbrake (
0e508d9) - usage: Record a status code on usage logs so failures can be classified in #470 by @njbrake (
86e5353) - dashboard: Browsable activity log and rebuilt usage analytics in #474 by @njbrake (
86bf391) - dashboard: Show the failure diagnostic and status code in the activity detail in #482 by @njbrake (
8c35a87) - dashboard: Make ids selectable and copyable in the tables in #485 by @njbrake (
9f436e2) - dashboard: Price a model the catalogue does not list in #490 by @njbrake (
4795754) - routing: Named routing policies with failover, budget tier-down, and enforced guardrails in #492 by @njbrake (
e4ed919) - hybrid: Forward provider-specific extra_params through resolved attempts in #496 by @tbille (
0d78ff6) - tools: Meter and bill gateway-run tool calls in #504 by @njbrake (
d71a9ce) - dashboard: Show what served a routed request in the activity log by @njbrake (
dbf6a51) - messages: Support Anthropic context compaction in #512 by @HareeshBahuleyan (
726eb63) - keys: Add per-key ignore_user_mismatch opt-out in #510 by @njbrake (
19266bd) - dashboard: Edit provider client_args from the Providers page in #520 by @njbrake (
d036f1b) - dashboard: Filter usage views on several models, users, or keys in #521 by @njbrake (
9ef21b1) - tools: Accept natural web_search tool declarations and emit native result blocks in #523 by @njbrake (
611fd62) - sdk-codegen: Queue auto-merge on the regeneration PR in #519 by @njbrake (
197fb33) - streaming: Send SSE keepalives while a provider stream is idle in #528 by @njbrake (
d6d686b) - router: Learned kNN router as a routing-policy backend in #188 by @njbrake (
8b20687) - responses: Preserve context compaction through tool loops in #513 by @HareeshBahuleyan (
1d8af00) - code-execution: Publish the wire contract and type the sandbox client in #544 by @khaledosman (
8b7e1d7) - dashboard: Show requests in flight on the activity page in #549 by @njbrake (
4600ae4) - routing: Weighted policy for load balancing across providers in #553 by @njbrake (
709c794) - dashboard: Share the usage view as an image in #555 by @njbrake (
d864e05) - otlp: Capture coding-agent behavioral events into agent_telemetry in #548 by @arthuursantos (
7bd6546) - routing: Rename policies in place, and stop them hiding candidate prices in #560 by @pss-julien (
e0db77d) - telemetry: Receive OTLP outcome metrics and read them per unit of spend in #567 by @arthuursantos (
4a95bb5) - dashboard: Stop the activity log refreshing itself in #569 by @njbrake (
f32436c) - search: Serve POST /v1/search from a SearXNG backend in #598 by @njbrake (
8eca8f5) - code-execution: Publish the OpenAPI IDL and a backend conformance check in #578 by @khaledosman (
8b9a326) - search: Manage search tools at runtime from the dashboard in #604 by @njbrake (
af26c38) - scripts: Enforce OSS/enterprise boundary in check_architecture.py in #609 by @tbille (
dfffeb3) - dashboard: Select the runtime context from a deployment bootstrap in #618 by @njbrake (
88c24ac) - dashboard: Render the sidebar from a nav registry with an entitlement gate in #619 by @njbrake (
492d156) - dashboard: Rehome the shared design foundation from the platform UI in #620 by @njbrake (
0fb1b24) - dashboard: Rebuild every page on the shared design foundation in #622 by @njbrake (
df9d01a) - dashboard: Boot a hybrid gateway into a data-plane landing page in #623 by @njbrake (
7797e83) - hybrid: Read the platform's optional caller-identity field in #615 by @tbille (
ad3efac) - reprise: Retain the resolved workspace and organization ids in #628 by @dpoulopoulos (
bfaccef) - tenancy: Land the reconciled tenancy schema on an async repository base in #624 by @njbrake (
3ef8ac8) - tenancy: Serve organizations, workspaces, and memberships in #625 by @njbrake (
9a32cc0) - tenancy: Give the tenancy model a request plane in #633 by @njbrake (
5d5ebff) - dashboard: Port the tenancy pages onto the rehomed control plane in #626 by @njbrake (
4812bcf) - tenancy: Add the nullable credential columns to
userin #667 by @njbrake (c9891ef) - dashboard: Port the nav label-override seam in #664 by @njbrake (
b4c5861) - budgets: Let a scoped budget reset on a UTC calendar boundary in #640 by @njbrake (
d37fe93) - dashboard: Bring the navigation shell onto the design in #638 by @jigjigjig (
94ed29c) - platform: Return settled cost inline in #675 by @HareeshBahuleyan (
169a1b4) - pricing: Per-organization model rate overrides in #674 by @khaledosman (
244bec5) - tenancy: Invitations for organization and workspace membership in #668 by @tbille (
dbc3c69) - dashboard: Give dashboard sessions a user identity in #681 by @njbrake (
80f4074) - tenancy: Workspace per-member budget defaults in #671 by @tbille (
4407789) - provider-keys: Add organization-scoped BYO provider keys in #670 by @tbille (
a82cd29) - mail: Make outgoing mail a transport with an honest no-transport mode by @njbrake (
51a6787) - auth: Password sign-in, and the master key retires as the dashboard login in #684 by @njbrake (
015a8ee) - dashboard: Give the dashboard password a page, and open the account menu's row by @khaledosman (
4b9fef7) - activation: Guide a new workspace to its first successful request in #705 by @khaledosman (
c2dfa30) - auth: Signup, email verification, and password reset in #708 by @tbille (
754f830) - dashboard: One budgets page, and a person is a member in #711 by @njbrake (
40e9c90) - dashboard: Give signup, verification, and password reset their pages in #726 by @njbrake (
34741ae) - budgets: Make the spend ledger exact, like the settled rows feeding it in #722 by @njbrake (
d2ba90e) - usage: Record the cached-token convention on usage_logs in #723 by @njbrake (
d80beab) - mcp: Let a workspace configure its own MCP servers in #724 by @njbrake (
a54acfe) - gateway: Give a workspace a code-execution policy over the deployment sandbox in #725 by @njbrake (
0e2634c) - budgets: Close the last two gaps in workspace budget defaults in #745 by @njbrake (
86fff80) - dashboard: Add the intra-page slot seam for the top bar's balance in #748 by @njbrake (
27c0a84) - gateway: Freeze dashboard sign-ins with a maintenance-mode switch in #749 by @njbrake (
4cb3a34) - gateway: Let a workspace configure its own web search in #744 by @njbrake (
62a25c7) - dashboard: Add a no-op telemetry seam an overlay build can replace in #754 by @njbrake (
c3a0343) - architecture: Land the ports package, composition container, and bootstrap hook in #756 by @njbrake (
150c492) - Register and sign in with passkeys in #751 by @khaledosman (
cf3de46) - guardrails: Let an organization mandate guardrails over its workspaces in #752 by @njbrake (
698be2b) - architecture: Resolve captured telemetry through a storage port in #770 by @khaledosman (
c68f3cd) - dashboard: Let a deployment point the Documentation links at its own docs site in #774 by @khaledosman (
ab25543) - usage: Add pricing-provenance columns to usage_logs in #775 by @khaledosman (
c0f1fc6) - tenancy: Create an organization, and switch the active one in #771 by @khaledosman (
c33d68c) - dashboard: Add a seam an overlay resolves entitlements through in #759 by @njbrake (
7f00911) - gateway: Price imported usage at organization rates in #761 by @njbrake (
e42afdf) - dashboard: Manage a workspace's MCP servers from Tools in #760 by @njbrake (
281353e) - gateway: Consult ModelProviderPort when no stored credential serves a candidate in #762 by @njbrake (
26dda60) - tools: Let a workspace pin its sandbox image and exposed tool set by @njbrake (
c3a2335) - Sign in with Google and GitHub, behind a real IdentityProviderPort in #765 by @njbrake (
cc693e0) - budgets: Give reservations a ledger in #767 by @njbrake (
4a2ee91) - tenancy: Scope the gateway survivals to a workspace in #784 by @khaledosman (
a10d46f) - dashboard: Give the shell a seam an overlay mounts a post-sign-in step in in #793 by @njbrake (
124565e) - tenancy: Give an operator a surface over the deployment's accounts in #798 by @njbrake (
065fe51) - dashboard: Add a trend chip for period-over-period change in #801 by @jigjigjig (
66b6537) - dashboard: Render usage deltas as trend chips in #815 by @jigjigjig (
4cd3f5c) - dashboard: Land the organization provider-keys page and a hosted surface set in #820 by @njbrake (
0dd3d6c) - dashboard: Finish the trend-chip migration on the overview page in #830 by @jigjigjig (
240b732) - api: Give a tenant an organization-scoped read of their own usage in #842 by @njbrake (
ec0d1c7) - api: Remove GET /v1/usage/summary.csv in #854 by @njbrake (
884221d) - dashboard: Give members a read-only view of the Build pages in #867 by @khaledosman (
b25554a) - dashboard: Publish the deployment's legal page addresses in #870 by @njbrake (
a40a1ca) - keys: Let members create and manage their own API keys in #866 by @khaledosman (
75bbf68) - budgets: Give an organization its own budgets and spend ceilings in #875 by @khaledosman (
aee4cca) - growth: Resolve GrowthSignalPort at signup and a member's first key in #884 by @njbrake (
aa58b5a) - web-search: Call Tavily and Brave in-process, and serve them to a data plane in #882 by @njbrake (
466284c) - dashboard: Give a member their organization's roster in #893 by @khaledosman (
feae11f) - dashboard: Add an organization-wide usage page for admins in #897 by @khaledosman (
196519b) - dashboard: Give organization admins Edit on the Build pages in #889 by @khaledosman (
ea8c750) - dashboard: Name the setup-guide step in the telemetry vocabulary in #908 by @khaledosman (
bddcc53) - budgets: Cap tokens and requests, not dollars alone in #906 by @khaledosman (
01bdd8b) - tenancy: Give the invitee a membership inbox to list, accept and decline in #914 by @khaledosman (
f2d8363) - tenancy: Email-domain auto-join, claimed and proven by DNS in #915 by @khaledosman (
10c4e6c) - api: Expose managed MCP activity in Messages streams in #795 by @HareeshBahuleyan (
9930c86) - dashboard: Redesign in #934 by @jigjigjig (
12e6770)
- @Ovvomii made their first contribution in #949
- @jigjigjig made their first contribution in #956
- @HareeshBahuleyan made their first contribution in #948
- @AmirF194 made their first contribution in #792
- @shoemoney made their first contribution in #730
- @dpoulopoulos made their first contribution in #628
- @Copilot made their first contribution in #575
- @arthuursantos made their first contribution in #567
- @pss-julien made their first contribution in #562
Full Changelog: https://github.com/mozilla-ai/otari/compare/v0.4.0...v0.5.0
0.4.0 - 2026-07-24
- dashboard: Align input boxes on the tools & guardrails page in #383 by @njbrake (
fa68234) - models: List env-credentialed providers in /v1/models in #388 by @njbrake (
340bd4b) - Surface actionable tools + reasoning_effort provider error in #385 by @njbrake (
47c142c) - dashboard: Make the provider picker open instantly (lazy split) in #382 by @njbrake (
e16e49f) - dashboard: Persist sign-in with an HttpOnly session cookie in #384 by @njbrake (
f462b03) - dashboard: Make the dashboard mobile friendly in #406 by @njbrake (
819681d) - hybrid: Recognize SDK-wrapped timeout/connection errors for fallback in #381 by @tbille (
c463603) - dashboard: CodeRabbit follow-ups on budgets, usage, and tool tests in #404 by @njbrake (
6a8a165) - compose: Bind quickstart postgres to localhost in #413 by @pcornelissen (
a5e8058) - dashboard: Stop table selection clicks from re-rendering every row in #416 by @njbrake (
39428aa) - dashboard: Reopen the request detail inline under the clicked activity row in #417 by @njbrake (
4685d03) - dashboard: Wrap usage stat tiles into a responsive grid on mobile in #418 by @njbrake (
50b0cdd)
- dashboard: Code-split route bundles in #373 by @njbrake (
b04d3cd) - pricing: Add reviewable price refresh in #367 by @njbrake (
e5bdbb8) - pricing: Meter cache writes and long context in #379 by @njbrake (
7559257) - files: Stream uploads and downloads instead of buffering in memory in #380 by @champ18ion (
cbee20e) - dashboard: Adopt recharts and retire hand-rolled UsageChart in #386 by @njbrake (
de90d8a) - dashboard: Disable adding providers when OTARI_SECRET_KEY is unset in #391 by @njbrake (
661aafc) - usage: Ingest external usage events for API-equivalent cost tracking in #392 by @njbrake (
bf90e94) - dashboard: Shared table framework with filtering, pagination, selection, and bulk actions in #415 by @njbrake (
c8e09b3)
- @pcornelissen made their first contribution in #413
- @champ18ion made their first contribution in #380
Full Changelog: https://github.com/mozilla-ai/otari/compare/v0.3.0...v0.4.0
0.3.0 - 2026-07-22
- openapi: Add ApiKeyAuth security scheme to schema definition in #248 by @Sourav-Tripathy (
ca5608c) - Fixing the render deploy url in #257 by @ojusave (
d7fd902) - sandbox: Give exec POST its own timeout above the execution budget in #279 by @njbrake (
d7ed152) - cli: Honor --log-level names and reject unsupported --workers in #284 by @njbrake (
36893fc) - metrics: Label requests by route template, not raw path in #274 by @njbrake (
a147e42) - config: Honor explicit OTARI_MODE instead of deriving from token alone in #276 by @njbrake (
2314b23) - batches: Enforce user resolution, budget, rate limiting, and batch ownership in #273 by @njbrake (
3f2c23e) - usage: Bill vision side-call before top-up and track usage report tasks in #277 by @njbrake (
81b0677) - api: Unify error handling and hybrid streaming fallback across completion routes in #281 by @njbrake (
ff6acb1) - deps: Bump any-llm to 1.21.0 to meter streaming /v1/messages usage in #292 by @njbrake (
01335b2) - Return 400 for unresolvable model selectors in #254 by @AloysJehwin (
b07a37c) - config: Reject invalid OTARI_SECRET_KEY at startup in #325 by @njbrake (
e2a8cbb) - observability: Don't count locked-in tool-loop failures as abandoned in #326 by @njbrake (
48e3e1d) - auth: Accept a raw token on the Otari-Key header, not just Bearer in #323 by @njbrake (
794eeef) - models: Bound provider discovery, cache failures, single-flight in #342 by @njbrake (
7c5c79c) - providers: Make API key optional when the provider env var is set in #344 by @njbrake (
f7b96c5) - Preserve Codex Responses metadata for OpenAI in #364 by @njbrake (
7915544) - dashboard: Limit automatic provider health checks in #363 by @njbrake (
e114665) - Require any-llm 1.22.1 in #369 by @njbrake (
3711f4c) - dashboard: Streamline first-run navigation in #368 by @njbrake (
f421a93)
- Enforce "PERF" ruff rules by @MooseTheRebel (
6547fe8) - Enforce ruff PERF rules across codebase in #245 by @MooseTheRebel (
dd8b71d) - models: Config-level model aliases that map to real selectors in #229 by @njbrake (
ed1a531) - Add API key rotation endpoint in #285 by @njbrake (
2756f08) - config: Surface hidden otari_env settings on GatewayConfig and fix config docs in #280 by @njbrake (
34f7a20) - Otel tool spans telemetry in #289 by @Sourav-Tripathy (
74b0d58) - ui: Add a standalone admin dashboard for models, pricing, and settings in #227 by @njbrake (
fc02390) - Add agent skills and scoped review instructions in #296 by @njbrake (
0c3542b) - ui: Manage providers, pricing, and aliases at runtime from the dashboard in #297 by @njbrake (
7d09276) - auth: Accept x-api-key header so Anthropic-native clients work (fixes #253) in #315 by @AloysJehwin (
4b4bd96) - dashboard: API key management and per-key model access control in #318 by @njbrake (
f49d70f) - observability: Count upstream attempts abandoned before first chunk in #324 by @njbrake (
b65e669) - dashboard: Budget and user management with two-layer model access in #322 by @njbrake (
eac679e) - Remove gateway/GATEWAY_ and pre-rename legacy aliases in #314 by @njbrake (
cb89cd2) - dashboard: Activity / request log viewer with per-request latency in #330 by @njbrake (
631a006) - batches: Add a batches table for idempotent accounting, spend folding, and strict ownership in #340 by @njbrake (
de23b87) - dashboard: Connection toast, resizable columns, and form polish in #343 by @njbrake (
e2ff7c8) - dashboard: Provider health monitor on the Providers page in #347 by @njbrake (
769c713) - dashboard: Usage & analytics page with aggregation endpoints (#303) in #345 by @njbrake (
2c95f65) - dashboard: Expand Settings with a full config viewer, wider settable set, and search in #346 by @njbrake (
d6d8ccf) - Add ability to edit alias target in dashboard in #348 by @njbrake (
58ed119) - dashboard: Built-in tools & guardrails configuration (#306) in #350 by @njbrake (
35991e2) - dashboard: Link provider name to filtered models page in #352 by @njbrake (
632b68a) - dashboard: Overview / home page (#302) in #354 by @njbrake (
150b2b1) - Price cached input tokens in standalone cost calculation in #356 by @njbrake (
1a8196e) - Add dashboard key lifecycle controls in #360 by @njbrake (
3f31c9c)
- @Shaurya2k06 made their first contribution in #334
- @AloysJehwin made their first contribution in #315
- @Sourav-Tripathy made their first contribution in #289
- @ojusave made their first contribution in #257
- @MooseTheRebel made their first contribution in #245
Full Changelog: https://github.com/mozilla-ai/otari/compare/v0.2.0...v0.3.0
0.2.0 - 2026-07-03
- client: Send Postman auth as Authorization Bearer in #220 by @njbrake (
45ba952) - ui: Redesign root tutorial page and fix dead quickstart link in #218 by @njbrake (
e510156) - ui: Drop "(Proxy Server)" label and add a landing-page favicon in #222 by @njbrake (
41162c3) - pricing: Warn and skip instead of crashing for unlisted providers in #215 by @njbrake (
80c36d1)
- client: Generate Postman collection from OpenAPI spec in #216 by @njbrake (
07abe38) - gateway: Classify hybrid streaming failures and envelope hybrid messages errors in #202 by @khaledosman (
52fc430) - providers: Support multiple named provider instances in #219 by @njbrake (
c6576a2) - sandbox: Consume the platform code-execution resolve by @agpituk (
d768976) - hybrid: Forward request session_label to platform usage report in #233 by @khaledosman (
5fdbe75) - Opt-in first-chunk grace for the final streaming attempt in #240 by @peteski22 (
73c8837)
Full Changelog: https://github.com/mozilla-ai/otari/compare/v0.1.0...v0.2.0
0.1.0 - 2026-06-25
- Add root path bootstrap for migrated test suite by @tbille (
742ee21) - Fix preserve existing Vary header values in #10 by @tbille (
c9700dd) - Satisfy lint in platform mode tests and config by @tbille (
49c2fbb) - Default to platform mode when token is set by @tbille (
3aea381) - Resolve mypy type errors and regenerate OpenAPI spec by @tbille (
85469ad) - chat: Map stream-creation errors to 502 + narrow route for mypy by @agpituk (
b7078ec) - streaming: Bake X-Otari-Request-ID into StreamingResponse headers by @agpituk (
100a1d3) - api: Normalise naive datetimes from SQLite before tz-aware comparison by @agpituk (
8705c11) - chat: Duck-type tool_call detection in MCP loop by @agpituk (
bdab428) - Address Copilot review feedback on PR #63 by @agpituk (
6145a7a) - Use requested n as fallback for image count in cost calculation by @tbille (
cf09b00) - Address Copilot review feedback on audio endpoints by @tbille (
ba98380) - Address Copilot review feedback on PR #65 by @agpituk (
8855127) - demo: Auto-start a llamafile via $LLAMAFILE_BIN, no home-dir guessing by @agpituk (
bb6a6a1) - demo: Handle empty model id from /v1/models and fix HF recipe URL by @agpituk (
af8af69) - chat,sandbox: Address tbille review on PR #65 by @agpituk (
1a001c8) - deps: Use git source for any-llm-sdk instead of local path by @tbille (
ba848ba) - Import BatchRequestCounts from canonical module to satisfy mypy attr-defined check by @tbille (
6fc7934) - Inject provider field into all batch endpoint responses by @tbille (
d54e86f) - Revert AGENTS.md to main branch version by @tbille (
ec0c21d) - deps: Use any-llm-sdk from PyPI instead of git branch source by @tbille (
fcfa511) - Address Copilot review feedback on batch endpoints by @tbille (
8e33cc0) - Address review feedback on PR #52 by @tbille (
ce5d7c9) - mypy: Resolve type errors for moderation types and amoderation import by @tbille (
ac4c01a) - Avoid name redefinition in moderation type shim by @tbille (
f291039) - Address Copilot review feedback on rerank endpoint by @tbille (
6102855) - Use direct arerank import now that SDK exports it by @tbille (
99bce51) - chat: Align mcp-servers resolver status semantics with credentials resolver by @agpituk (
e047521) - web-search: Address copilot review on PR #72 by @agpituk (
64cd537) - web-search: Silence init errors for unused searxng engines by @agpituk (
c20f170) - Add missing type annotations for mypy strict mode by @tbille (
040f26a) - Address Copilot review feedback on model discovery by @tbille (
7af6773) - Address remaining Copilot review comments on model discovery by @tbille (
ab2829d) - chat: Align streaming tool-mode dispatch with tool_mode predicate by @agpituk (
934f471) - routes: Address Copilot review on PR 1 by @agpituk (
615b5c3) - messages: Map streaming backend-unreachable errors to 502 by @agpituk (
a61fda3) - messages: Address Copilot review on PR 2 by @agpituk (
c154aaf) - responses: Map streaming backend-unreachable errors to 502 by @agpituk (
34f89a0) - responses: Address Copilot review on PR 3 by @agpituk (
3fc34b6) - platform: Address Copilot review on PR 4 by @agpituk (
c3ab2e0) - messages,responses: Streaming provider errors → HTTP error + regen OpenAPI spec by @agpituk (
1e3273f) - tools: Typecheck + address Copilot review on PR 86 by @agpituk (
f9442e7) - budget: Close cross-user IDOR, overspend race, and unmetered-usage bypasses by @khaledosman (
9b0abbe) - ci: Typecheck async-generator aclose + regenerate openapi spec by @khaledosman (
69fc2f9) - review: Address PR review on budget fix by @khaledosman (
5e17af0) - review: Harden against negative/zero budget inputs + log estimated cost by @khaledosman (
360bbfa) - guardrails: Address Copilot review by @agpituk (
d134663) - demo: Repair guardrails demo_flow /validate formatter by @agpituk (
c2d7dc9) - messages: Address review on count_tokens endpoint by @khaledosman (
691ac52) - bootstrap: Keep bootstrap API key on a single log line in #131 by @njbrake (
dcf2b00) - budget: Refund the reservation on streaming pre-commit provider errors in #129 by @njbrake (
b0ee34e) - deploy: Dispatch to renamed otari-ai platform repo in #141 by @njbrake (
8823fea) - sdk-codegen: Type response schemas in serialization mode so reasoning is a string in #145 by @njbrake (
55d80f2) - files: Bill vision side-calls, harden delete + retention docs by @khaledosman (
d849cfe) - files: Address review (bare-string Responses input, vision token cap, epoch skew) by @khaledosman (
34e642f) - docker: Keep python 3.14, bump onnxruntime instead of downgrading in #159 by @njbrake (
a83442a) - codegen: Name free-form-object union arrays so Go generates valid syntax in #166 by @njbrake (
ea973fe) - codegen: Map bare empty-schema union members to FreeFormObject in #180 by @njbrake (
69af27e) - usage: Treat 402 from the usage-report endpoint as non-retryable by @khaledosman (
3f9c767) - fallback: Treat 404/405/409/410 as retryable so model-unavailable falls through by @agpituk (
670165d) - fallback: Report each failed attempt when the whole chain is exhausted by @agpituk (
039ce8c) - fallback: Log dropped inline usage-report failures; drop em dashes in #174 by @khaledosman (
14610be) - fallback: Report each failed attempt when a streaming chain is exhausted by @agpituk (
98be421) - fallback: Don't block cancellation or leak the backend stack on all-failed stream in #176 by @khaledosman (
ed0978b) - fallback: Bound the inline usage-report flush; dedupe all-failed test by @khaledosman (
419e19b)
- Extract gateway runtime into standalone service repo by @tbille (
522fe09) - Add platform mode startup gating and API surface by @tbille (
68e9538) - Integrate platform resolve and async usage reporting by @tbille (
c491bba) - gateway: Add enable_docs config to disable FastAPI docs endpoints by @tbille (
786dc75) - asyncio: Convert DB layer to async, add budget strategies and log writer by @tbille (
3e593b6) - pricing: Add effective_at to model_pricing for historical price tracking by @tbille (
e1d69b9) - gateway: Add GET /v1/usage bulk endpoint with time range filters by @tbille (
139fef9) - Add POST /v1/responses endpoint for OpenAI Responses API by @tbille (
b9b0e59) - gateway: Rename API_KEY_HEADER from X-AnyLLM-Key to AnyLLM-Key by @peteski22 (
a006df8) - gateway: Accept legacy X-AnyLLM-Key header for back-compat by @tbille (
a6f00e6) - chat: Walk multi-attempt resolve responses with retry on transient errors by @agpituk (
08e4370) - streaming: First-chunk gate for routing-policy fallback (v1.1) by @agpituk (
24e39fe) - chat: MCP tool-use loop with streaming support by @agpituk (
850be4c) - chat: URL safety, content-block rendering, hardened error handling by @agpituk (
5724cba) - Add POST /v1/images/generations endpoint by @tbille (
f99061b) - Add POST /v1/audio/transcriptions and /v1/audio/speech endpoints by @tbille (
6749d47) - chat: Code execution via sandbox container with multi-keyword detection by @agpituk (
0df1f89) - chat: Resolve workspace-scoped MCP server ids in platform mode by @agpituk (
fe8bbf1) - chat: Hoist user_token + narrow type guard for platform resolve by @agpituk (
3cfbf8a) - chat: Web_search tool via SearXNG-compatible backend with content extraction by @agpituk (
07c14c0) - Auto-discover models from configured providers for /v1/models by @tbille (
0c95f3b) - chat: Pre-lock-in fallback for tool-loop requests by @agpituk (
b4866ce) - messages: MCP / sandbox / web_search on /v1/messages by @agpituk (
1fa3e47) - messages: Wire on_first_response into anthropic_tool_loop by @agpituk (
65ce24a) - responses: MCP / sandbox / web_search on /v1/responses by @agpituk (
92b9aa1) - responses: Wire on_first_response into responses_tool_loop by @agpituk (
a8bfe78) - platform: Platform-mode routing for /v1/messages and /v1/responses by @agpituk (
4cc54f8) - tools: Explicit otari_* gateway tool shapes (BREAKING) by @agpituk (
259a267) - demo: --brave flag for the web-search demo by @agpituk (
b67b413) - guardrails: Request-level guardrails across all endpoints by @agpituk (
41bb2a9) - guardrails: Default to PIGuard via encoderfile, monitor mode by @agpituk (
16632cd) - Add CodeRabbit configuration in #110 by @njbrake (
f52cdd1) - messages: Add /v1/messages/count_tokens for Claude Code support by @khaledosman (
2664e11) - Add PR template and enforcement workflow in #123 by @njbrake (
690996c) - sdk-codegen: Emit the rust core as an inlined src/_client module in #136 by @njbrake (
aca95e3) - sdk-codegen: Coordinate SDK releases with stamped spec version and staleness alert in #137 by @njbrake (
3996758) - web-search: Per-workspace web-search policy via platform resolve in #144 by @agpituk (
a2fc5c8) - files: File uploads + document understanding for local models by @khaledosman (
56ba31e) - api: Derive request schemas from any-llm Params to stop param drift in #160 by @njbrake (
d3948a1) - web-search: Forward X-Gateway-Token to the search backend in #164 by @agpituk (
2ec9053) - Generate the changelog and release notes from commits with git-cliff in #162 by @njbrake (
c2d3eb2) - codegen: Type the image-generation response from any-llm ImagesResponse in #178 by @njbrake (
90f4523) - sandbox: Forward caller token to the sandbox backend as Bearer auth in #182 by @agpituk (
a74c8bf) - usage: Forward provider cached-token counts in usage reports in #196 by @tbille (
1b6a80e) - gateway: Classify upstream provider errors into specific safe responses in #197 by @khaledosman (
01df970) - deploy: Add Deploy on Railway template scaffold and button in #210 by @njbrake (
528c9be) - config: Support full provider/pricing config via environment in #211 by @njbrake (
022a8dc) - pricing: Add genai-prices default pricing fallback in #201 by @njbrake (
9880f58)
- Standardize logger placeholders across gateway modules in #12 by @tbille (
8c92ed8) - Add Ruff linting with local and CI enforcement in #14 by @tbille (
e0bd004) - Make Vertex credential setup request-local in #17 by @tbille (
df52553) - Avoid row lock during preflight budget checks in #18 by @tbille (
1deb7e9) - Remove unused domain exception hierarchy in #23 by @tbille (
87e30f5) - Drop user_id labels from rate and budget counters in #21 by @tbille (
a88a432) - Adding release actions by @macaab26 (
eed4b97) - Changing tags naming by @macaab26 (
59550be) - Any-llm → otari across the project by @tbille (
0153cc2) - Docker image mzdotai/gateway → mzdotai/otari by @tbille (
2ffd357) - Rename AWS ECS cluster and service to otari-ai naming convention by @tbille (
d5ab256) - Update to readme to include Otari in title by @tbille (
d537202) - Add batch API endpoints for asynchronous LLM processing by @tbille (
08d77e5) - Fix batch API review issues: add FIXME for results endpoint, usage logging, error handling, and missing tests by @tbille (
4960a8b) - Move Batch import out of TYPE_CHECKING and add SDK batch result imports by @tbille (
a042fd6) - Remove dead db parameter from log_batch_usage and fix model sentinel by @tbille (
a0b6c1d) - Switch get_db to get_db_if_needed and add BackgroundTasks parameter by @tbille (
b8c1159) - Use aretrieve_batch_results and BatchNotCompleteError in results endpoint by @tbille (
28f6ed8) - Mock split_model_provider in invalid model format test by @tbille (
f523fd4) - Update results endpoint tests to use aretrieve_batch_results by @tbille (
e2912a5) - Pin any-llm-sdk to local source with batch result types by @tbille (
d70ca88) - Update AGENTS.md with batch API spec and sync uv.lock for local SDK by @tbille (
cae8709) - Add moderations route mirroring /v1/embeddings by @tbille (
85446bc) - Register moderations router in standalone mode by @tbille (
0557127) - Add integration tests for /v1/moderations endpoint by @tbille (
e80faf3) - Document /v1/moderations in README API surface by @tbille (
69c9198) - Add local ModerationResponse/ModerationResult types by @tbille (
776c477) - Fix moderations route import against older any-llm-sdk by @tbille (
317b3f1) - Import moderation types from gateway-local module in tests by @tbille (
db30a43) - Regenerate OpenAPI spec for /v1/moderations route by @tbille (
7a799d6) - Final uncommitted changes for add-moderation-api-support by @tbille (
7161e1f) - Add POST /v1/rerank route handler and register router by @tbille (
cdc1ea4) - Add integration tests for /v1/rerank endpoint by @tbille (
a77162d) - Regenerate OpenAPI spec with /v1/rerank endpoint by @tbille (
d624ccb) - Default PLATFORM_BASE_URL for otari.ai by @tbille (
d373516) - Rename platform token env var to OTARI_AI_TOKEN by @tbille (
bc93f37) - Support OTARI_* env vars for core settings by @tbille (
fdfcc35) - Deleting deployment actions by @macaab26 (
6519e60) - Adding trigger for the platform by @macaab26 (
bd29d0f) - Accept gw_ API key prefix by @tbille (
5c548ef) - Add CLAUDE.md as a symlink to AGENTS.md in #98 by @njbrake (
156fd4e) - Refresh README: ecosystem framing + SDK links in #104 by @njbrake (
c46de4e) - SDK automatic codegen from OpenAPI spec in #99 by @njbrake (
ac8bb58) - Pin OpenAPI Generator via openapitools.json in SDK codegen workflow in #109 by @njbrake (
3be3d54) - Tech-debt cleanup: delete dead src/ shims, ToolBackend Protocol, document narrowing asserts in #117 by @njbrake (
d87e3fa) - Fix intermittent auth 500s from SQLite lock contention (#106) in #115 by @njbrake (
bde1b1f) - Docs/refactor in #119 by @angpt (
1286093) - Consolidate the chat/messages/responses request pipelines onto a shared core in #130 by @njbrake (
172325d) - Change repository name in gateway-docker.yml by @njbrake (
d4b29f7) - Rename "Otari Gateway" to "Otari" in #142 by @njbrake (
8cd4f7f)