Skip to content

v0.7.0 — security-audit remediation, supply-chain hardening, and committee governance

  • operator
  • evaluator

Status: ready for the v0.7.0 tag. The blocker is cleared — ADR 0025 (release versioning policy and pipeline ordering) was accepted by the committee at the 2026-08-09 weekly call and merged as #487, so the version-consistency gate and the backend-before-frontend pipeline this release is cut under are live on main. This PR marks ADR 0025 Accepted, bumps api and gateway __version__ to 0.7.0, and regenerates the committed OpenAPI export, which carries the api version. This is the first release cut under ADR 0025.

This is a hardening release, not a feature milestone. It is the first substantial remediation of the 22-finding 360° security audit (#288, Claude Fable 5), and it lands supply-chain, CI, and governance work alongside it. Its headline is that all three High-severity audit findings are closed, together with three Mediums. Per ADR 0025 the number is computed from what has accumulated on main: three of these changes (#396, #399, #400) require operator action, so the next release is a minor — v0.7.0, not a patch. The through-line is the audit’s own verdict — LQ.AI is “a codebase worth hardening, not rewriting.”

It is also the first release whose headline is community work. Every audit-remediation PR here was written by an external contributor, and four people landed their first commit on main in this range. See Who built this release.


Headline — security-audit remediation (#288)

Section titled “Headline — security-audit remediation (#288)”

The audit surfaced 22 findings (3 High, 10 Medium, 8 Low, 1 Info). None is exploitable by a raw external attacker in the shipped default (every service binds to 127.0.0.1); all sit on the platform’s core promise — cross-tenant data sovereignty for privileged legal material. This release closes all three Highs plus three Mediums:

High — all three closed

  • GW-01 — the gateway inference/embeddings endpoints now require a key. POST /v1/chat/completions and /v1/embeddings previously accepted requests with no gateway key while every sibling router enforced one, contradicting the gateway’s own OpenAPI contract; on the flat compose network any co-located container could drive the crown-jewel egress path directly. Both now require the key. (#396)
  • API-01 — the cross-tenant IDOR at chat creation is closed. An authenticated user could bind a new chat to another user’s project UUID, pulling the victim’s knowledge-base documents into the response and forwarding them to the external LLM with anonymization disabled. Chat creation now validates project ownership. This was the finding most directly on the data-sovereignty guarantee. (#397)
  • D-01 — stored XSS in the skill source viewer is closed. SkillSourceView.svelte rendered skill markdown through marked with no DOMPurify — the one LQ-authored {@html} sink that skipped the sanitization applied elsewhere by convention (a malicious skill body → JS in a viewer’s authenticated session → account takeover). Skill markdown is now sanitized before render. (#398)

Medium — three closed

  • GW-04 — LLM provider base_url egress is guarded. The provider base_url had no SSRF/allowlist/HTTPS guard, asymmetric with the hardened tool-egress path; it is now validated at adapter-build time, and a violation is fatal at gateway startup rather than skipped (skipping would let the router fall through and send prompts somewhere the operator did not choose). https is always allowed; plaintext http only to a loopback/private host. (#400) Related: provider egress refusals are now a typed ProviderEgressRefused under the LQAIError hierarchy. (#488)
  • AG-01 — autonomous retrieve_chunks is scoped to the session owner. Model-supplied kb_id/file_id in the autonomous layer previously carried no ownership scoping; retrieval is now bound to the session owner. (#401)
  • API-04 — the api refuses to boot on the published dev JWT secret. A deployment left on the documented dev JWT_SECRET will now refuse to start in production (set the secret, or LQ_AI_DEV_MODE). (#399) — This addresses audit finding API-04; the commit was not tagged with the finding ID, so it will not appear in a Refs #288 grep.

Operator note — three behavior changes (the reason this is a minor, per ADR 0025 decision 3). All three carry the breaking-change label, so the version-consistency gate sees them. #396 — a deployment calling the gateway directly without a key gets 401 on every inference call after upgrading (in the default topology the api already sends the key, so no action is expected). #399 — an install still on the dev JWT_SECRET will refuse to start until the operator sets a real secret (or LQ_AI_DEV_MODE). #400 — the gateway refuses to start if any configured provider base_url is plaintext http to a non-local host, or uses a scheme other than http/https. A local Ollama on http://localhost or a private IP is unaffected; a remote http:// endpoint must move to https. Read before upgrading.


  • uv-managed lockfiles for gateway/ and api/ (ADR 0023). Both services build from uv-managed lockfiles — reproducible, fully-pinned resolution across the SBOM. (#312, #441)
  • Stack-boot CI smoke test. CI builds and boots the whole compose stack on every run, catching a break in the 7-service bring-up before merge. (#297)
  • GitHub Actions pinned to commit SHAs. Release-relevant actions no longer float on mutable tags (partial remediation of audit E-02; the signing/packages:write split remains open). (#296)
  • The release pipeline is staged and gated (ADR 0025). release.yml splits its single unordered matrix into build-backend → build-frontend, so a backend failure structurally blocks the frontend from publishing under the same tag, and adds a version-consistency job that fails the tag push if api and gateway disagree with each other or with the tag, or if a patch tag is cut while a breaking-change-labelled PR merged since the previous release. ADR 0025 is Accepted (committee, 2026-08-09), and this is the first release cut under it. (#487)
  • Windows entrypoint line-endings forced to LF, fixing a class of Windows-checkout boot failures. (#259)
  • Gateway test fixture pins OLLAMA_BASE_URL, closing an ambient-environment leak that let a developer’s shell change test behaviour. (#443)
  • Dependency bumps, including starlette 0.48.0 → 1.3.1 (a major bump) and cryptography 44.0.3 → 49.0.0 in the gateway, FastAPI, uvicorn, aioboto3, sqlalchemy, ruff, and several GitHub Actions to current majors — several swept into single lock/CI writes. (#468, #454, #449, #469, #472, #452, #476, #477, #467, #463, #180, #250, #124, #123, #248, #249)

  • Knowledge bases listed by the project_knowledge_bases junction, with a backfill. Replaces the legacy single-column association; ships migration 0066 to backfill existing rows. No operator step (the backfill runs on migrate). (#442)
  • Committee governance framework (ADR 0022). New GOVERNANCE.md plus ADR 0022 formalize the LQAI committee’s decision process and meeting-record convention. (#311); ADR 0022 is now marked Accepted (committee, 2026-07-26). (#507)
  • Expansion-contribution routing (ADR 0024 + direction paper). A direction paper and ADR 0024 give jurisdiction- and practice-area-expansion contributions a documented route, so proposals stop stalling for want of a place to go. Both Accepted (2026-08-09). (#313)
  • Native frontend dev flow (Vite HMR) documented for web/. (#281)
  • caddy-tailscale deployment recipe cleaned up. (#261)
  • DE-385 filed — desktop package-lock.json version stale at 0.5.2, tracked for follow-up. (#276)
  • DE-386 filed — tool providers have no client-credentials auth path; the five missing pieces are enumerated in PRD §9 rather than left implicit. (#508)

  • compose: forward LQ_AI_GATEWAY_MASTER_KEY into the gateway service (Donna #3 follow-up) — fixes encrypted-key startup in the default stack. (#278)
  • desktop: pull images before up so the launcher picks up published releases (shipped as the desktop-v0.6.2 tag). (#277)

The entire security-audit remediation in this release was written by a contributor from outside the maintainer team. All three High findings (#396, #397, #398), all three Mediums (#399, #400, #401), the two follow-ups that tidied up after them (#443, #488), and the SHA-pinning of GitHub Actions (#296) are @SaifAlYounan’s work — nine PRs, the first of which landed on 2026-07-08. The headline of this release is not maintainer work.

Four people landed their first commit on main in this release:

Contributor What they landed

@SaifAlYounan
the whole #288 remediation (9 PRs), plus SHA-pinned Actions
@mkorpela
LF line-endings on the Windows entrypoint, fixing a class of Windows-checkout boot failures (#259)
@sgbooth
the native Vite-HMR frontend dev flow, documented (#281)
@sergiomaldo
knowledge bases listed by the junction table rather than the legacy column (#442)

And one came back: @ThurgyThurg, whose caddy-tailscale recipe first landed in #134, returned to clean it up in #261.

The project’s contributor base is genuinely widening. Seven distinct human identities had landed commits on main by v0.6.1; twelve had by the time this release was cut. Concentration is still real — one person authored roughly 95% of all human-authored commits in the project’s history, and ADR 0025’s Cadence section is honest about what that means for release cadence and for the desktop signing identity. But the security-critical work in this release came from the community, and that is a different shape of project than the one the last release shipped from.

Worth recording alongside the credit: none of these landed on the first pass. The security PRs went through review rounds that sent work back — a missing loop-path test on #401, a corrected diagnosis of which guard was actually failing on #443 — and they are better for it. Review capacity is the other half of a widening contributor base, and it is the half that does not scale by itself.


Migration head 0066 (0066_backfill_project_knowledge_bases, #442) — reversible, applied on make migrate, no operator action. This is the only schema change in the release.


Per ADR 0025 decision 1, api, gateway, web, and proxy share one release version — the image tag. This PR bumps the two project-owned strings, api and gateway __version__ → 0.7.0. The drift ADR 0025 records (api 0.6.1, gateway lagging at 0.5.1) was already closed by #487, which moved gateway to 0.6.1; both now move together, as the policy requires. web/package.json stays 0.9.2 (OpenWebUI fork, per ADR 0001) and proxy carries no version string; neither is retitled.

The api version is also stamped into the committed OpenAPI export (docs/api/backend-openapi.generated.yaml → info.version), so the bump requires a make openapi regeneration; the test_generated_openapi_export_is_current drift-guard fails otherwise. That step is not yet in the release checklist — see Follow-ups below.

Per ADR 0025 decision 3, this is a minor: #396, #399, and #400 each require operator action, so a patch tag would fail the version-consistency breaking-change gate. The number is not chosen from a milestone — it is what has accumulated on main.


Six of the 22 audit findings are closed here (all 3 High; GW-04, AG-01, API-04). The remainder of #288 stays open and should each land with its finding ID in the commit subject (GW-02, Refs #288) so the next narrative assembles the same way:

  • Medium: GW-02 (client-settable anonymization/tier flags), GW-03 (embeddings egress enforces neither anonymization nor tier floor), API-02 (viewer role defined but unenforced), API-03 (no login/MFA rate-limiting), AG-02 (MCP trusts server-attested safety metadata), D-02 (Electron navigation allowlist / sandbox:false), E-05 (EOL Electron 31).
  • Low: GW-05 (Presidio recognizers off by default), AG-03 (autonomous max_allowed_tier=None), AG-04 (emit_artifact re-embeds model content), D-03 (Teams id_token unverified), E-03 (root containers), E-02 remainder (signing + packages:write still in the release job), E-04 (*_BIND_ADDR exposure), E-06 (floating community submodule ref).
  • Info: GW-06 (master key + gateway secret via plaintext env).

Unblocked. ADR 0025 merged as #487, and this branch is rebased onto it — the version-consistency gate and the staged pipeline are live and will run on the tag push.

Gates (maintainer, before tagging): api ruff/mypy + pytest; gateway ruff/mypy --strict + pytest; web check:lq-ai + vitest; the stack-boot smoke; and the version-consistency job (api == gateway == tag; not a patch while a breaking-change-labelled PR merged since v0.6.1).

Pre-flight, already satisfied:

  • api and gateway __version__ are both 0.7.0; the OpenAPI export is regenerated to match.
  • #396, #399, and #400 carry the breaking-change label, so the gate sees them and v0.7.0 (a minor over v0.6.1) satisfies it.

Steps (maintainer):

  1. Merge this PR to legalquants/main.
  2. Tag v0.7.0 (annotated) on the merge commit and push it to legalquants — the Release workflow builds + signs the four images and attaches the SBOM. Do not use [allow-version-drift] or [allow-breaking-in-patch]; neither is needed.
  3. Verify the published image set before going further (ADR 0025 decision 4 gates backend before frontend, but the intra-stage guarantee is partial — confirm all four images carry the tag).
  4. Cut desktop-v0.7.0 after the image set is live and verified, recording the image tag it ships against (ADR 0025 decision 2).
  • The release checklist does not mention regenerating the OpenAPI export. docs/BUILD-AND-RELEASE.md §“Versioning policy” tells the maintainer to bump api and gateway __version__, but the api version is also stamped into docs/api/backend-openapi.generated.yaml, so a bump alone leaves CI red on test_generated_openapi_export_is_current. It cost a red CI run on this release. Worth a one-line addition to the checklist.
  • The 2026-08-09 committee minutes are not published yet. ADR 0025’s Accepted status (and ADR 0024’s, from the same call) rests on the maintainer’s record of the call until they are. The minutes backlog is tracked separately — only two of the recent calls have public notes.
  • ADR 0025’s contributor-concentration figures do not reconcile. Its Cadence section states 655 non-merge commits, ~642 (~98%) by one person across two identities, and “five distinct human identities”; recounting at b060ae2f — the SHA the ADR says it verified against — gives 704 commits, 665 (~94%) and eleven identities. The argument the section makes is unaffected: 94% is still overwhelming concentration, and the desktop signing bottleneck is unchanged. But the numbers should be corrected so this document and that one do not disagree on a countable fact.

Drafted from the v0.6.1..legalquants/main inventory on 2026-08-06 (35 commits) and refreshed 2026-08-10 against 460997b5 (40 commits), which added #313, #443, #487, #507, and #508. Security context from #288 (Claude Fable 5). Remediation coverage verified against finding-ID commit subjects on legalquants/main: GW-01 (#396), API-01 (#397), D-01 (#398), GW-04 (#400), AG-01 (#401) carry Refs #288; API-04 is #399 (untagged). Version numbering per ADR 0025.