Skip to content

Latest commit

 

History

History
1988 lines (1520 loc) · 153 KB

File metadata and controls

1988 lines (1520 loc) · 153 KB

Changelog

All notable changes to Fortémi are documented here.

The format follows Keep a Changelog, and this project uses CalVer versioning: YYYY.M.PATCH.

[2026.7.19] - 2026-07-29

Corrective publication for 2026.7.18. Knowledge Shard formats, exact 2.0.0/full-v1 evidence, supported platform cells, and consumer revisions are unchanged.

Fixed

  • Persist the Vault-managed mutsu CI public key in a root-owned, boot-available OpenSSH authorization path. Sidecar publication validates the pinned key fingerprint and effective sshd configuration so Linux arm64 and macOS arm64 jobs do not depend on an interactive console login after reboot.
  • Apply the required free-space and inode guard to both matric-builder jobs in the supported-platform workflow.
  • Run the Linux arm64 contract builder and its isolated PostgreSQL service on a private Docker network instead of mounting the mutsu Colima daemon socket into the contract container.

[2026.7.18] - 2026-07-29

Corrective publication for 2026.7.17. Knowledge Shard formats, exact 2.0.0/full-v1 evidence, supported platform cells, and consumer revisions are unchanged.

Fixed

  • Require noninteractive public-key authentication for both mutsu coordinator jobs. Password and keyboard-interactive fallback are disabled, and failed authentication now exits within the bounded connection timeout instead of holding the runner's controlling terminal.

[2026.7.17] - 2026-07-29

Corrective publication for 2026.7.16. Knowledge Shard formats, exact 2.0.0/full-v1 evidence, supported platform cells, and consumer revisions are unchanged.

Fixed

  • Give each Linux arm64 sidecar build a run-scoped Colima profile on /Volumes/build and remove the unbounded cleanup of the obsolete home-volume profile.
  • Bound post-build Colima deletion to 60 seconds so stale VM teardown cannot hold the single mutsu coordinator slot indefinitely.

[2026.7.16] - 2026-07-29

Corrective publication for 2026.7.15. Knowledge Shard formats, exact 2.0.0/full-v1 evidence, supported platform cells, and consumer revisions are unchanged.

Fixed

  • Allow three bounded 15-second attempts when acquiring mutsu's pinned SSH host key. This preserves exact fingerprint verification while tolerating the measured seven-second banner delay under concurrent release load.

[2026.7.15] - 2026-07-29

Corrective publication for 2026.7.14. Knowledge Shard formats, exact 2.0.0/full-v1 evidence, supported platform cells, and consumer revisions are unchanged.

Fixed

  • Pin the Linux arm64 and macOS sidecar coordinator jobs to the titan host runner, which has the required route to mutsu, instead of allowing generic Ubuntu runners without mutsu reachability to claim those jobs.
  • Isolate mutsu SSH keys, host records, configuration, and downloaded binaries under each Actions job's temporary directory so host-runner cleanup cannot modify shared operator SSH state.

[2026.7.14] - 2026-07-29

Corrective publication for 2026.7.13. Knowledge Shard formats, exact 2.0.0/full-v1 evidence, supported platform cells, and consumer revisions are unchanged.

Fixed

  • Relocate native Linux arm64 sidecar Colima state, cache, and temporary files from mutsu's constrained home volume to /Volumes/build, matching the passing supported-platform workflow and preventing release publication from failing while expanding the VM disk.

[2026.7.13] - 2026-07-29

Added

  • Add exact Knowledge Shard 2.0.0 readers for the three registered profiles and receipt-bound opt-in export for exact 2.0.0/full-v1, with authority-schema validation, durable absent/null component presence, and clean repeated import/re-export coverage against pinned React and AIWG producer artifacts. 1.2.0/core-v1 remains the default export; 2.0.0/core-v1 and 2.0.0/record-v1 remain unadvertised.
  • Add the Fortemi-owned supported-platform aggregate, binding the same server authority, @fortemi/core consumer, and HotM application-consumer contract surface on Linux x86_64, Linux arm64, and macOS arm64 on mutsu. Gitea run 6393 passed all three cells and the aggregate at the pinned revisions. Windows remains deferred under #1096.
  • Add native Linux arm64 sidecar publication and checksum binding for the supported-platform consumer journey.

Fixed

  • Reconcile legacy Knowledge Shard guidance with the schema 1.2.0 named profiles and exact 2.0.0 opt-in tuples, explicitly supersede obsolete ADR-028/029 statements, and block stale or unqualified parity claims in the documentation contract scan.
  • Harden attachment-sidecar crash recovery with durable journals, bounded staging, clean retry convergence, and receipt-backed process-abort tests. These runtime receipts do not establish power-loss durability, complete backup, GUI/native-dialog coverage, or suite-wide portability.

2026.7.12 - 2026-07-22

Corrective release for the 2026.7.11 publication. Knowledge Shard formats, profiles, compatibility windows, and conformance receipts are unchanged.

Fixed

  • Rebuild the tracked documentation shard and receipt against the matching server release so bundle images cannot embed a stale v2026.7.10 artifact.
  • Exclude generated Rust and npm dependency trees from the reviewed third-party surface scan, making lint results independent of runner caches.

2026.7.11 - 2026-07-21

Stable publication recovery for the 2026.7.10 data-portability release. The Knowledge Shard formats, profiles, compatibility window, and conformance receipts are unchanged.

Fixed

  • Export the resolved release version to the GHCR promotion script so the versioned API and multi-architecture bundle images can be published.
  • Attach checksum-covered in-toto provenance to versioned native sidecars and mirror the complete five-asset release set from Gitea to GitHub, rejecting checksum, provenance-subject, or existing-asset mismatches.

2026.7.10 - 2026-07-21

Data-portability and contract-hardening release. The registered Knowledge Shard topology now has immutable passing evidence for all nine required producer/consumer cells across Fortemi, Fortemi React, PGlite, RecordStore, and AIWG. Claims remain profile-scoped: the complete full-v1 server self-route is supported, while external full-v1 consumer evidence remains pending.

Security

  • Updated the MCP server's transitive dependency resolution to remove the high-severity fast-uri host-confusion advisory and current Hono and body-parser advisories covered by non-breaking dependency updates.

Added

  • Cross-layer shard conformance matrix: release claims now require complete, digest-pinned producer and consumer evidence across Fortemi, Fortemi React, PGlite, RecordStore, AIWG, and server round trips. Published-package fixtures cover deterministic bytes, semantic re-export, repeated import convergence, compatibility-window boundaries, malformed input, hierarchy cycles, resource limits, and zero mutation on every rejection path.
  • Knowledge Shard record-v1 profile (#1057): contract revision 4 enables the reduced RecordStore profile after an exact Fortemi React producer archive passed schema/checksum/relationship preflight, zero-mutation dry-run and reserved-profile rejection, two convergent replace imports, server re-export, and React return import. The profile covers notes, collections, tags, note-to-note links, and attachment projections with mandatory loss reporting; full-v1 remains reserved.
  • OpenAPI response coverage (#1060): every documented operation now publishes the global rate-limit middleware's schema-bearing RFC 9457 429 response, and contract validation rejects missing statuses, media types, or ProblemDetails schema references.
  • Attachment sidecar staging prerequisite (#1058): the filesystem backend can stream a declared attachment blob into an isolated shard-import staging namespace, verify its canonical BLAKE3 digest and length, atomically promote it, compensate idempotently, and sweep stale stages without traversing final blobs. core-v1 REST exports can now opt into verified, digest-deduplicated blobs/<digest> entries with include_blobs=true; import validates referenced sidecars before writes, stages them outside the final namespace, restores filesystem-backed blobs inside the shard transaction, and cleans up on repeated imports and late component failures. Missing sidecars remain valid reference-only attachments. This bounded-buffered route slice does not claim streaming archive processing or full-v1 conformance.

Fixed

  • Browser CORS compatibility (#1062): configured HotM origins may send the standard Cache-Control request header used by archive health and job polling while unrelated custom request headers remain rejected.
  • Knowledge Shard structured round trips (#1056): default exports now include complete collection hierarchies, imports reject incoherent collection/note/template/link references before writes, and collection, template, and note timestamp identities survive clean and repeated imports.
  • Knowledge Shard attachment projections (#1057): core-v1 imports now restore stable attachment identities, filenames, extraction state and text, canonical digest metadata, and digest-deduplicated reference blobs inside the shard transaction. Attachment bytes remain outside the core-v1 archive.
  • Knowledge Shard empty revisions (#1057): named-profile imports now preserve an explicit empty revised body instead of treating it as a missing revision; unprofiled legacy imports retain their previous behavior.

Security

  • Vault-backed release authority: ordinary commits and stable release tags use distinct OpenBao-custodied GPG keys through TPM-sealed AppRoles. Signing material exists only in tmpfs, exact fingerprints are checked before use, and the release wrapper rejects prerelease versions, stale main, dirty trees, missing release artifacts, or unpublished public authority.
  • Bounded Knowledge Shard import (#923): shard upload, base64 import, and on-disk swap now reject oversized compressed or expanded archives, unsafe or duplicate paths, non-regular tar entries, excessive entries, and oversized component records before checksum/schema validation or database mutation. Ordinary imports apply every selected database component in one schema-scoped transaction, fail closed on any database error, and enqueue NLP work only after commit. Destructive on-disk wipe swaps validate first, then wipe and apply in that same transaction so a late failure restores the previous core-v1 state; undocumented swap strategies are rejected.

2026.7.1 - 2026-07-13

Upgrade-reliability and Intel inference deployment release. This release fixes the remaining February-baseline bundle upgrade blockers, adds a validated Intel Arc/XPU host-vLLM deployment path, migrates release automation to OpenBao-backed credentials, and ratifies the portable binary-attachment shard contract.

Added

  • Intel Arc/XPU host-vLLM deployment: added a Compose overlay that clears NVIDIA reservations and routes generation to a host OpenAI-compatible vLLM endpoint while retaining a separate embedding provider. The profile includes an example systemd service, environment guidance, a render validation gate, and a hardware-independent smoke test.

Fixed

  • Legacy bundle authentication (#1048): existing data directories created with an older password default now align the application role with the configured POSTGRES_PASSWORD before TCP clients start.
  • Pre-migration backup reliability (#1049, #1050): bundle upgrades use compressed pg_dump output, preflight temporary storage, fall back to explicitly warned disk staging when shared memory is too small, and surface dump failures instead of failing silently.
  • Bulk reprocessing completeness (#1052): archive-wide reprocessing now paginates past the repository's 100-note page cap up to the requested limit.
  • Intel inference routing: MATRIC_INFERENCE_DEFAULT now selects a configured generation provider across synchronous and streaming chat, the runtime registry, health probes, and model reporting. The effective configuration endpoint reports env-only OpenAI settings and the real default instead of hard-coding Ollama, while OpenAI-compatible chat retains native token streaming. Production API builds now include the OpenAI-compatible backend required by the Intel host-vLLM overlay.
  • Bundle configuration (#1051): FORTEMI_ALLOW_LOCAL_ISSUER is available to bundled deployments and remains disabled by default.
  • Release automation: Intel overlay validation installs a pinned Compose plugin, and documentation deployment steps run explicitly under Bash.
  • Bundle image defaults: optional component build arguments retain their declared defaults across Docker builders, preventing PostGIS, OCR, and FFmpeg from being silently omitted in builds without explicit overrides.
  • Bundle build context: added Docker exclusions for Rust, Node, coverage, distribution, VCS, and local-secret artifacts so local and CI image builds send only source inputs to the daemon.

Security

  • OpenBao-backed CI credentials: Gitea build, image publication, documentation deployment, and release workflows now fetch scoped runtime secrets through the repository's OpenBao integration.

Documentation

  • Portable shard sidecar contract (#1046): a self-contained shard may store each distinct attachment blob once at blobs/<64-char-lowercase-hex>, with JSON records retaining only the matching blake3:<hex> checksum reference.
  • Reference-only compatibility: shards without sidecar entries remain valid, unknown or unreferenced sidecar entries are ignored, and JSON projection records never inline raw bytes.
  • 2026.7.1 implementation boundary: that release's server shard export remained reference-only and did not restore attachment records or bytes. Self-contained server export/import was not a feature of that release.
  • Backup guidance and contract evidence: reconciled the backup guide, architecture record, test plan, sample payloads, and documentation manifest with the current server behavior.
  • Documentation contract hygiene: replaced two credential-shaped test DSNs that blocked the hosted-strict documentation contract while preserving the migration fixture's runtime behavior.

2026.7.0 - 2026-07-12

Migration-safety and release-publishing recovery release. This release restores a safe upgrade path from the February v2026.2.0 database state to current, requires a successful pre-migration backup for bundled deployments, and verifies that GHCR publishing is active again before cutting versioned public images.

Fixed

  • February-to-current upgrade path: exact v2026.2.0 migration history is normalized before current migrations run, including legacy duplicate-version and checksum drift cases from the February tag.
  • SKOS embedding trigger compatibility: legacy restore states with pgvector IS DISTINCT FROM trigger comparisons are repaired before backup/migration, preventing restore-time type errors on current PostgreSQL/pgvector.
  • Bundled upgrade backup gate: the bundle entrypoint now fails closed if the pre-migration backup cannot be created before applying migrations.
  • GHCR release confidence: release creation remains dependent on successful image publishing, and the post-merge dev publish path was verified before this tagged release.

Documentation

  • Added a February-to-current upgrade runbook, audit report, and fixture evidence covering exact v2026.2.0 restore, 100k-note migration, backup restore drill, and post-February migration inventory.

2026.6.1 - 2026-06-30

Hardening and contract-cleanup release. The API error surface migrates to the RFC 9457 problem+json standard (a clean pre-GA break), a broad redaction pass removes secret/path/diagnostic leakage from errors and logs, HTTP responses gain security headers, and the incoming-webhook receiver moves to HMAC-only authentication. Documentation is reconciled to the shipped code.

Changed

  • BREAKING: API errors now return RFC 9457 application/problem+json (type, title, status, detail, request id) instead of ad-hoc {"error", "message", "details"} bodies; the problem-type contract is published in the OpenAPI spec. (#967)
  • Auth scopes: the MCP scope is now separated from REST read/write scopes; realtime transport (SSE/WebSocket) requires the MCP scope.
  • The API fails closed on invalid startup configuration.

Added

  • Security headers on all HTTP responses.

Fixed

  • The incoming-webhook receive endpoint authenticates via HMAC signature and no longer requires a bearer token.

Security

  • Error responses and logs no longer leak filenames, paths, secrets, or internal diagnostics across the API, core, jobs, and crypto surfaces (telemetry/secret redaction pass). (#968, #974)
  • Bump anyhow 1.0.102 → 1.0.103 (RUSTSEC-2026-0190).

Documentation

  • Reconciled docs to the shipped contract: RFC 9457 error responses, the real inference endpoints (/complete, /stream, /config, /providers, /test-connection), MCP tool counts (43 core / 205 full) and annotations, and corrected environment variables (MATRIC_INFERENCE_DEFAULT, default generation model qwen3.5:9b).

2026.6.0 - 2026-06-15

Incoming streams milestone. This release builds out Fortémi's incoming/streaming surface end to end across four phases: token-by-token streaming chat over Server-Sent Events (Phase A); the incoming-webhook receiver surface with HMAC verification, per-receiver JSON-Schema validation, and idempotent delivery (Phase B); NDJSON streaming bulk ingest with backpressure, cursor resumption, per-stream auth, and finished TUS resumable uploads (Phase C); and a pluggable inbound external-event-source framework with Redis Stream, SSE, and (feature-gated) Kafka connectors (Phase D). Every accepted inbound event lands in the shared event_outbox and flows through the existing fan-out pipeline. A CI publish-pipeline defect that had silently stopped GHCR image publishing since ~February is also fixed.

Highlights

What Changed Why You Care
POST /api/v1/chat/stream (SSE) Assistant responses arrive token-by-token over SSE instead of one blocking JSON body — the basis for a live-typing chat UI.
Incoming webhook receivers Register HMAC-verified receivers, validate payloads against per-receiver JSON Schema, and dedupe with Idempotency-Key — every accepted webhook captured to the outbox.
POST /api/v1/ingest/stream (NDJSON) Long-running agents push notes/events as a resumable stream with per-line acks, backpressure (429), and X-Ingest-Cursor resumption.
Finished TUS resumable uploads Multi-GB media uploads resume after interruption (TUS 1.0.0).
Inbound event-source connectors Pull from external Redis Streams, upstream SSE, and (opt-in) Kafka into the shared outbox — at-least-once, with restart resumption and a DLQ.
Truthful GHCR publishing The Docker image publishes to ghcr.io again; publish jobs now fail loudly instead of masking failed pushes.

Added — Streaming chat (Phase A · #811)

  • Streaming chat endpoint — POST /api/v1/chat/stream (#812). Same request contract as POST /api/v1/chat (input, optional model, optional context.conversation_history), but the response is an SSE stream rather than a single JSON body. Events:

    • delta{"content": "<chunk>"}, one per generated content chunk.
    • done{"finish_reason": "stop", "model": "<slug>"}, terminal success event.
    • error{"error": "<message>", "code": "GENERATION_FAILED"}, terminal failure event.

    The endpoint acquires an owned GPU semaphore permit held for the full stream lifetime (released on completion, error, or client disconnect) and returns 503 immediately when no permit is available — streaming chat never starves background jobs. Multi-turn fidelity is preserved (system prompt + conversation history + current turn). Auth requirements match /api/v1/chat.

  • Streaming-chat observability on GET /api/v1/health/streaming (#814). A new "chat" block sits alongside sse and rtp with process-lifetime counters: chat_stream_started_total, chat_stream_completed_total, chat_stream_errored_total, chat_stream_client_disconnect_total, chat_stream_tokens_total, and chat_stream_dropped_tokens_total.

  • Multi-turn streaming backend — OllamaBackend::chat_multi_turn_stream. Streams /api/chat with stream: true, sharing NDJSON line parsing with the existing single-turn streamer via a common ollama_chat_ndjson_stream helper.

  • Last-Event-ID resumption for /api/v1/chat/stream (#815). Redis-backed per-stream cursor (60s TTL) lets a reconnecting client resume from the last delivered event id.

Added — Incoming webhook receivers (Phase B · #817)

  • Receiver registration + receive + delete. POST /api/v1/webhooks/incoming registers a receiver (HMAC secret + schema ref); POST /api/v1/webhooks/incoming/{slug} receives; DELETE /api/v1/webhooks/incoming/{slug} removes one (204 / 404, idempotent delete_by_slug).
  • HMAC signature verification (#820). Incoming requests are verified against sha256=<hex> over the raw body; bad or missing signatures return 401.
  • Schema-shape registry + server-side validation (#821). A JSONB schema_doc column stores per-receiver JSON Schema; the built-in Twilio schemas were converted to embedded JSON Schema so every payload validates through one jsonschema-backed path, with field-level JSON-pointer 400s. PATCH /api/v1/webhooks/incoming/{slug} updates a receiver's schema in place (slug/secret preserved).
  • Idempotency-Key dedupe via Redis, 24h TTL (#822). An opt-in Idempotency-Key header maps to idem:{slug}:{key} storing {body_hash, status, body}: repeat key + matching body → cached 200 (no duplicate outbox row); repeat key + different body → 409; no header → normal processing. Checked after HMAC verification; degrades to a no-op without Redis.
  • Generic incoming-webhook outbox capture (#818). Every accepted receiver writes a durable incoming_webhook.received row to the shared event_outbox (entity_type = "incoming_webhook", entity_id = <receiver id>, payload carries slug, provider, schema_ref, parsed payload, and side_effect) — in addition to any provider-specific side effect such as Twilio call-session events. An outbox-write failure is logged and does not fail the accepted webhook for the caller.

Added — Streaming bulk ingest + TUS (Phase C · #824)

  • NDJSON bulk ingest — POST /api/v1/ingest/stream (#825). application/x-ndjson request body parsed line-by-line with a per-line insert_tx; SSE ack/done response contract.
  • Per-line validation + progress frames (#826). DB-free schema validation per line; progress {processed:N} every FORTEMI_INGEST_PROGRESS_INTERVAL (default 100); a malformed line errors only that line.
  • Backpressure — bounded buffer + 429 (#827). Configurable FORTEMI_INGEST_STREAM_BUFFER channel with escalating thresholds: warning at 80%, 429 {retry_after_ms, INGEST_BACKPRESSURE} at 95%, TCP block-sender backpressure at 100%; ingest_stream_buffer_pressure gauge + peak/warning/429 counters on /health/streaming.
  • Resumption — X-Ingest-Cursor, 60s TTL (#828). Redis-backed per-ack cursor with server-authoritative skip-ahead on reconnect; 410 Gone beyond TTL.
  • Per-stream bearer token auth + rate limit (#829). POST /api/v1/ingest/tokens mint + DELETE /api/v1/ingest/tokens/{token_id} revoke (1h TTL, archive-bound); per-token lines/sec token-bucket pacing → error {status:429, INGEST_RATE_LIMITED}; fail-closed 401 when INGEST_REQUIRE_TOKEN=true (default). ingest_stream_rate_limited_total on /health/streaming.
  • Outbox integration (#830). Each ingested note appends a note.created outbox row in the same transaction as the insert — outbox-count == ingested-count invariant, atomic per-line rollback.
  • Finished TUS 1.0.0 resumable uploads (#831, closes #544). Note-scoped attachment uploads with Creation/Termination/Checksum extensions, on-disk staging for resume, the tus_upload table (offset + expires_at), and TTL cleanup — multi-GB uploads resume after interruption.

Added — Inbound external event sources (Phase D · #832)

  • Pluggable InboundEventSource framework (#833). Connector trait + registry; a lifecycle supervisor that validates each event, writes it to the shared event_outbox, then commits the upstream offset (at-least-once), with exponential-backoff retry and a dead-letter table (inbound_dlq); per-connector metrics (events/errors/lag) on /health/streaming; and POST/GET/DELETE /api/v1/inbound-sources. All connectors are opt-in via INBOUND_EXTERNAL_SOURCES_ENABLED=false (default — standby cost gate).
  • Redis Stream connector (#834). Consumer-group XREADGROUP with XACK only after the durable outbox write; on restart it drains the pending-entries list then switches to new entries — no event loss.
  • SSE connector (#835). Long-lived text/event-stream consumer with Last-Event-ID resumption seeded from the last committed id, supervisor-driven exponential-backoff reconnect, and an optional event-type filter.
  • Kafka connector (#836). Consumer-group consume with manual offset commit (resume from last committed offset on restart), optional dead-letter topic, and SASL/SSL config. Double-gated per the cost-gate: a compile-time kafka Cargo feature (off by default, so default/edge builds never compile librdkafka — built self-contained via vendored OpenSSL) plus runtime INBOUND_KAFKA_ENABLED=false.

Fixed

  • CI: GHCR/registry publish jobs no longer mask failed pushes (#882). push_with_retry now exit 1 on final failure (was an ignored return 1) and all four publish blocks (publish-dev, publish-release, publish-github-dev, publish-github) run under set -euo pipefail, so a failed docker login/build/push fails the job instead of reporting a false green. This restored GHCR publishing (ghcr.io/fortemi/fortemi:main verified republished). workflow_dispatch was also added to the CI workflow for manual re-runs.

Behavior Notes

  • Backpressure / dropped tokens. Each chat delta send has a window controlled by CHAT_STREAM_SEND_TIMEOUT_SECS (default 30). If a client stops draining the bounded SSE buffer (capacity 256 events), the stalled token is shed and counted in chat_stream_dropped_tokens_total rather than holding the GPU permit indefinitely. A mid-stream client disconnect is likewise counted as dropped and recorded in chat_stream_client_disconnect_total. Under normal client pacing, no tokens are dropped.
  • Event shape consistency. /api/v1/chat/stream uses the same delta/done/error SSE event vocabulary as the existing POST /api/v1/inference/stream, so a client can share one SSE parser across both.
  • POST-based SSE. Because the streaming chat and ingest endpoints are initiated with POST (to carry the request body), browser EventSource cannot be used directly; consume them with a fetch() + ReadableStream reader or a POST-capable SSE client.

Not Yet Included

  • The HotM client-side consumer of /api/v1/chat/stream is tracked in the desktop-app repository (#813).
  • Real-time provider integrations (Twilio Programmable Voice, WebRTC/SIP, recording providers, live video) are a separate epic — out of scope for the incoming-streams work in this release.

Verification

  • cargo fmt --all -- --check
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test -p matric-api -p matric-jobs (streaming-chat, ingest, webhook, and inbound-connector contract + unit suites)
  • cargo test -p matric-jobs --features kafka (Kafka connector unit tests)
  • CI integration suite (real Postgres): generic-outbox capture, ingest count invariant + atomic rollback, webhook signature/schema/idempotency round-trips
  • docker manifest inspect ghcr.io/fortemi/fortemi:main (GHCR publish restored)

2026.5.13 - 2026-05-25

Security maintenance release for the dependency advisory sweep after 2026.5.12. This release updates vulnerable Rust and npm transitive artifacts, removes obsolete advisory allowlists, and includes the post-2026.5.12 documentation/issue-tracking cleanup commits.

Security

  • Rust advisory updates - Updated openssl from 0.10.79 to 0.10.80, rand 0.8.5 to 0.8.6, and rand 0.9.2 to 0.9.3 in Cargo.lock.
  • npm advisory update - Pinned the MCP server dependency graph to qs 6.15.2 via mcp-server/package.json overrides and lockfile update.
  • Advisory allowlist cleanup - Removed the temporary RUSTSEC-2026-0097 ignores from cargo audit and cargo deny configuration now that the patched rand lines are present.
  • Supply-chain review completed - Verified crates.io/npm provenance and artifact integrity for the patched packages, diffed prior and patched artifacts, and closed Fortemi issue #857 with the evidence summary.

Changed

  • Issue tracking preference - Updated project guidance to prefer the canonical Gitea tracker.
  • Planning documentation - Captured licensing and storage planning updates for the upcoming distribution work.

Verification

  • cargo audit
  • cargo deny check advisories
  • npm ls qs
  • npm audit --omit=dev

2026.5.12 - 2026-05-25

Realtime provider integration milestone. This release adds the standards-shaped call transport foundation, the first Twilio Voice adapter, Deepgram live ASR, provider-neutral call event outbox contracts, and the batch transcription bridge for completed Twilio recordings. It also includes fail-closed authentication defaults and the incoming webhook receiver foundation used by provider control-plane callbacks.

Highlights

What Changed Why You Care
Twilio Voice realtime adapter Fortemi can accept signed Twilio Voice webhooks and Twilio Media Streams WebSocket audio for live call transcription.
Standards-shaped realtime contracts Provider-specific wire formats stay inside adapters while call lifecycle, media frames, ASR events, and outbox rows stay provider-neutral.
Deepgram streaming ASR Live call audio can produce partial/final transcript events with reconnect, failover accounting, and health metrics.
Recording-completed batch bridge Twilio recordings are imported as audio attachments and queued through the existing AudioTranscriptionHandler for higher-quality post-call transcripts.
Fail-closed auth default /api/v1/* endpoints now require auth by default unless an operator explicitly opts into anonymous local mode.

Added

  • Incoming webhook receiversPOST /api/v1/webhooks/incoming, receiver lookup, payload validation, HMAC verification, and Twilio Voice schema support for provider control-plane callbacks.
  • Realtime call sessions — persisted call_sessions metadata, call detail lookup at GET /api/v1/calls/{call_id}, realtime session metrics, and database coverage for active/completed call aggregation.
  • Realtime transport foundation — provider-neutral MediaFrame, codec normalization, mock adapter fixtures, mock ASR backend, and Twilio adapter mapping helpers.
  • Twilio Voice + Media Streams support — signed Voice webhooks create/update call sessions, /api/v1/realtime/twilio/{CallSid} accepts Twilio media streams, and recent-session gating rejects stale or unknown WebSocket attempts.
  • Deepgram streaming backend — WebSocket ASR client with secure API-key handling, event parsing, reconnect/backoff behavior, failover to a configured fallback backend, and health metrics.
  • Transcript outbox foundationevent_outbox table and helpers for realtime transcript/call events, including high-volume transcript emission coverage.
  • Twilio call-event outbox contract — adapter-owned mapping for call_started, state_change, recording_available, and ended events so downstream consumers are insulated from Twilio status strings.
  • Twilio recording transcription bridge — completed recording callbacks download the recording into file storage, create an audio attachment note, queue AudioTranscription, and link batch transcript policy metadata back to the call session.
  • Realtime provider setup documentationdocs/deployment/realtime-providers.md now covers Twilio + Deepgram setup, consent/disclosure, troubleshooting, and contract completeness for local, single-tenant, multi-tenant, regulated, and future provider deployments.

Changed

  • Authentication defaults are fail-closedREQUIRE_AUTH now defaults to true. Anonymous mode requires both REQUIRE_AUTH=false and I_UNDERSTAND_NO_AUTH=true; multi-tenant deployments reject anonymous mode regardless of acknowledgment.
  • CI and pre-commit provider-boundary checks — realtime provider-specific imports are rejected outside adapter modules.
  • Docs site workflows — docsite clone steps use the configured build token for authenticated source access.
  • Call-session schema stability — call sessions no longer keep an archive foreign key that could deadlock archive registry operations.

Fixed

  • Call API response shape — call detail responses now align with the persisted session/transcript shape used by the realtime pipeline.
  • Prepared test database use — call API tests now use the prepared DB path consistently.
  • Syntactic chunker performance guard — relaxed an over-strict guard that could fail under noisy CI timing.

Security

  • Fail-closed API authentication (ADR-094, fixes Gitea fortemi/fortemi#709). Existing single-user desktop and local-dev deployments that intentionally run anonymous must add I_UNDERSTAND_NO_AUTH=true when setting REQUIRE_AUTH=false. Stock bundled compose files include that acknowledgment for local profiles.
  • Twilio webhook verification — Twilio signatures are validated against the externally visible URL using the receiver secret; proxy deployments must preserve X-Forwarded-Host and X-Forwarded-Proto for validation to succeed.

Migration Notes

  • Database migrations included — this release adds incoming webhook receiver, realtime call-session, and event-outbox tables. Migrations run through the normal startup/migration path.
  • Auth opt-out must be explicit — deployments that depended on the old implicit anonymous default will now start authenticated unless they set the two-variable local-mode acknowledgment.
  • Realtime recording batch transcripts require file storage — Twilio recording.completed callbacks can only queue the batch transcription bridge when the API process has file storage configured and can reach the recording URL.

2026.5.11 - 2026-05-18

Maintenance tag. No functional change since 2026.5.10. Cuts a clean release tag that lands correctly on both the Gitea (origin) and GitHub (github) remotes — the 2026.5.7, 2026.5.8, and 2026.5.10 tags exist on GitHub but point at the pre-LLM-wizard commit (a stale mirror artifact). This release is the first post-workstation tag that is byte-identical across both remotes.

What the 2026.5.7 → 2026.5.11 sequence delivered (roll-up)

For consumers landing on this changelog and wanting the short version of what shipped under the "workstation" theme:

  • 2026.5.7./workstation wrapper, docker-compose.workstation.yml unified stack (Fortemi + HotM + Ollama), QUICKSTART.md, WORKSTATION-SETUP.md, three profiles (--backend-only, --no-ui, default-ui).
  • 2026.5.8 — Pluggable LLM backend selector: .env.workstation.example template + ./workstation configure-llm wizard covering ollama, vllm, openai, openrouter, llamacpp. extra_hosts: host.docker.internal:host-gateway wired into compose so the same URL works on Linux/macOS/Windows. Doctor probes the configured backend.
  • 2026.5.9 — Correctness patch after validating against the qwen36_vllm_autodeploy_basic.sh reference: wizard now prompts for the served-model-name (not the HF path), defaults to qwen3.5:9b so model strings stay stable across backends.
  • 2026.5.10 — Docs surfacing: WORKSTATION-SETUP.md LLM backend section, README workstation-block callout, docs/content/quickstart.md "Building features on a dev box?" sibling callout, docs/content/inference-backends.md top callout.

No code changes

Bumped Cargo workspace + mcp-server to 2026.5.11. No schema, no behavior, no docs differ from 2026.5.10.

2026.5.10 - 2026-05-18

Docs-only release. The configure-llm wizard and the .env.workstation override layer landed in 2026.5.8–2026.5.9, but they were only discoverable via the wrapper's help output. This release threads them through the canonical docs so new users actually find them.

Documentation

  • WORKSTATION-SETUP.md → new "LLM backend selection" section — Canonical ops reference for the backend layer. Covers the env_file: mechanism, the wizard flow, the five backends with per-backend gotcha table, the served-name-vs-HF-path distinction for vLLM (with both the light Qwen2.5-7B and heavy 35B-on-3×A100 patterns), the doctor's backend-probe check, and the switch-backends procedure (file edit + restart, or runtime hot-swap via /api/v1/inference/config).
  • README.md → Local Workstation block — Added a "Want a different LLM than Ollama?" callout pointing at configure-llm. Surfaces the backend flexibility at the discovery layer so users picking the workstation path see it from the start, not after up.
  • docs/content/quickstart.md → new "Building features on a dev box?" callout — Sibling block to the existing "Looking for a desktop app?" callout. Routes developers at the workstation flow (with the configure-llm invocation) instead of the Docker-bundle server path that the rest of this guide covers.
  • docs/content/inference-backends.md → top callout — One-line redirect for workstation users so they reach the wizard rather than reading the by-hand env-var guide. The guide itself remains the authoritative reference for Docker-bundle and from-source paths.

No code changes

No version-affecting code, schema, or behavior changes. The version bump is for tag-gated CI release jobs (release notes are generated from the CHANGELOG entry on tag push).

2026.5.9 - 2026-05-18

Patch follow-up after validating the 2026.5.8 vLLM path against a real autodeploy reference script (Qwen3.6-35B-A3B served as qwen3.6:35b on port 11436). Fixed three correctness issues that would have produced HTTP 404 on first chat call against any vLLM instance.

Fixed

  • vLLM served-name vs HF-path conflation — The 2026.5.8 wizard prompted for "Generation model name" with default meta-llama/Llama-3.1-8B-Instruct. matric-api sends that string in the OpenAI model field; vLLM compares it against --served-model-name, NOT the HF path. If a user pasted the HF path (the obvious thing to do), every chat call would 404. The wizard now prompts explicitly for the served-model-name with an upfront explanation of the two names, and the template carries the same distinction inline.
  • Wizard default served name now qwen3.5:9b — matches Fortemi's stable Ollama default. Same model string works across ollama / vllm / openrouter / openai backends, so switching backends doesn't require touching any UI or chat-side configuration. Was meta-llama/Llama-3.1-8B-Instruct.
  • Port hint includes the autodeploy convention — Wizard prompt is now vLLM port on host [8000, autodeploy script uses 11436]. Template documents both common ports.

Changed

  • .env.workstation.example vLLM block — Expanded with hardware-friendly defaults (Qwen2.5-7B, Llama-3.1-8B, Phi-3.5-mini sized for 8GB-24GB VRAM) plus a heavy-iron section pointing at the qwen36_vllm_autodeploy_basic.sh-style 35B-on-3×A100 setup. The serve command in the comment now shows the canonical pattern: vllm serve <HF/path> --served-model-name qwen3.5:9b.

No migration impact

  • 2026.5.8 users on Ollama, OpenAI, OpenRouter, or llama.cpp: nothing changes. Only the vLLM wizard branch and the vLLM section of .env.workstation.example are touched.
  • 2026.5.8 users on vLLM: if you already configured .env.workstation and chat works, no action needed. If chat is 404-ing, your OPENAI_GEN_MODEL is probably set to a HF path — change it to whatever you passed to vLLM's --served-model-name.

2026.5.8 - 2026-05-18

Picking a different LLM backend on the workstation no longer requires editing the compose file. New interactive wizard plus an .env.workstation override layer cover the five common cases (Ollama, vLLM, OpenAI, OpenRouter, llama.cpp) without Docker expertise.

Added

  • .env.workstation.example — Template with five copy-paste-ready provider blocks: ollama-local (default), vllm-local, openai-cloud, openrouter, llamacpp-local. Each block sets exactly the env vars the matric-api inference router needs, with inline guidance on host-to-container networking and the embedding pairing (cloud providers without embedding support automatically pair with the containerized Ollama). Copy to .env.workstation and uncomment one block.
  • ./workstation configure-llm — Interactive wizard that walks through the five options. Prompts API keys silently (no terminal echo), prompts host ports for local-on-host backends, writes .env.workstation with mode 600, backs up any existing file to .env.workstation.bak. Three aliases: configure-llm, config-llm, llm.
  • Doctor check #8: LLM backend — Reports which backend is selected (from .env.workstation if present, else "ollama containerized default"), probes the configured endpoint, surfaces friendly remediation when the probe fails. Catches the most common "wrong port" / "vLLM not started" mistakes before up.
  • QUICKSTART Step 3.5 — New optional step with a decision table (have vLLM? have an OpenAI key? …) and a recommendation tree. Stays a skip-by-default step so users who just want the working ollama path see no extra friction.

Changed

  • docker-compose.workstation.yml matric-api service — Now loads .env.workstation via env_file: with required: false (compose 2.24+ spec), so it's silently no-op for users who don't create the file. When present, the override values supersede the inline environment: block. Also adds extra_hosts: "host.docker.internal:host-gateway" so vLLM/llama.cpp on the host work on Linux without IP juggling — same URL works on macOS, Windows, and Linux.

Migration

  • No-op for existing users on Ollama. .env.workstation is gitignored and not auto-created; if it doesn't exist, the workstation behaves exactly as in 2026.5.7.
  • Switching to a cloud provider now takes ~30 seconds. ./workstation configure-llm → pick option 3 or 4 → paste API key → done. No docker-compose.yml edits, no Dockerfile rebuild.

2026.5.7 - 2026-05-18

Local developer workstation: one command brings up Fortemi + HotM + Ollama in containers, with a friendly wrapper, pre-flight doctor, and step-by-step quickstart for users who have never touched Docker.

Added

  • ./workstation wrapper script (#708) — Named subcommands for the full dev-box workflow: up, down, status, doctor, models pull, open, logs, shell, psql, reset, help. The up command waits for healthy state and prints the URL; doctor runs 7 pre-flight checks (Docker, compose, ports, native ollama, HotM sibling repo, GPU passthrough, models) with explicit remediation text for each failure.
  • docker-compose.workstation.yml (#708) — Single unified stack replacing the fragmented per-repo compose files for local dev. Includes ollama (GPU passthrough, bind-mounted ~/.ollama/), postgres (pg18 + pgvector + PostGIS), matric-api (auth off, permissive CORS, rate limit disabled), HotM agent-proxy, and the HotM UI. Three profiles select which services come up:
    • default (./workstation up --backend-only) — ollama + postgres + matric-api. HotM repo not required.
    • hotm profile (./workstation up --no-ui) — adds agent-proxy. Useful for API-only integrations.
    • ui profile (./workstation up, the default) — full stack including HotM UI at http://localhost:4180.
  • QUICKSTART.md — Five-step walkthrough for users new to Docker. Covers cloning both repos as siblings (or --backend-only if you don't want HotM), running doctor, bringing the stack up, pulling models, and verifying in a browser. Includes "what if something breaks?" section for the six most common first-run failures.
  • WORKSTATION-SETUP.md — Operations reference manual: full command list, day-2 troubleshooting beyond the happy path, native-ollama removal (the one step that requires sudo and a human), volume management, GPU verification.
  • README "Local workstation" section — Surfaces the workstation path alongside the bundle and HotM-desktop options so new users know they have three install paths.

Changed

  • agent-proxy is now profile-gated — Was always-on in earlier workstation drafts; now only starts under --profile hotm or --profile ui. Users who don't have the HotM sibling repo can run ./workstation up --backend-only and get a working API without ever pulling HotM.
  • Workstation host ports remapped — Postgres on 5434 (was 5432) and agent-proxy on 3011 (was 3001) to avoid collisions with native postgres and the sysops dashboard commonly running on 3001. matric-api stays on 3000; UI stays on 4180; ollama stays on 11434.

Notes

The workstation stack is for local development only. Production deployments continue to use docker-compose.bundle.yml (single-host headless backend) or the per-service ghcr.io/fortemi/* images. The workstation does not replace either path; it sits alongside them as the third option, optimized for "developer with a GPU laptop who wants to iterate end-to-end without setting up postgres by hand."

The full stack reaches healthy state in roughly 45 seconds on a clean host with the docker base images already pulled. First-time clean-install (everything pulled from scratch) is dominated by the ollama image (~10.6 GB) and the matric-api Rust build.

2026.5.6 - 2026-05-10

Two small but high-impact fixes to support-archive seeding: imported notes now have titles, and the seed no longer pins the GPU for hours.

Added

  • defer_inference flag on POST /api/v1/backup/import (#677) — When true, imported notes land as raw content only; the full NLP pipeline (embeddings, metadata, NER, linking, title generation) is skipped. FTS works immediately via the insert-trigger-maintained tsvector. Semantic backfill is on-demand via POST /api/v1/notes/reprocess. Default false preserves prior behavior.
  • title field on CreateNoteRequest and POST /api/v1/notes (#675) — Optional explicit title. When provided, the AI title-generation pipeline step is skipped (caller's value is authoritative). Bulk-create accepts it on every item. Threaded through to the underlying INSERT INTO note.
  • SEED_WITH_INFERENCE env var on seed-support-archive.sh (#677) — Operators who want immediate inference at seed time set SEED_WITH_INFERENCE=true. Default false — the seed now passes skip_embedding_regen=true via env-driven toggle rather than the previously hard-coded flag.

Fixed

  • Support archive notes had no titles (#675) — scripts/rebuild-docs-shard.sh now derives a title for each doc: first H1 (skipping YAML front-matter) when available, otherwise the filename stem with hyphens/underscores normalised to spaces. Each note in the shard JSON now carries a title field that the API persists on insert.
  • Legacy /backup/import enqueued the full NLP pipeline unconditionally (#677) — In the prior release a manual /backup/import of the support archive could produce ~965 background jobs for 193 notes and pin Ollama for hours on edge hardware. The newer /knowledge-shard/upload endpoint had skip_embedding_regen; /backup/import now has the equivalent defer_inference gate.

2026.5.5 - 2026-05-10

Docker bundle behavior change: the bundled fortemi-docs support archive is now opt-in to mirror the native build path.

Changed

  • Support archive is opt-in by default (#672) — The Docker bundle no longer auto-seeds the bundled Fortémi documentation on first boot. Behavior now matches the native cargo run path (which never auto-seeded). Two opt-in routes:
    • Auto-seed on first boot: set LOAD_SUPPORT_MEMORY=true in .env before docker compose ... up.
    • One-command seed on a running instance: docker compose -f docker-compose.bundle.yml exec fortemi /app/seed-support-archive.sh (idempotent; flag file on the persistent pgdata volume tracks state).
    • Legacy DISABLE_SUPPORT_MEMORY=true still wins as a force-skip — kept for back-compat with bundles that pre-date the flip. Used by docker-compose.minimal.yml to guarantee skip regardless of upstream config.
  • docker/seed-support-archive.sh is now safe to invoke manually at any time inside a running container. The MANUAL_INVOCATION flag (default true) distinguishes operator-invoked from entrypoint-invoked runs; the entrypoint sets it to false so auto-seed requires explicit LOAD_SUPPORT_MEMORY=true.
  • README "Quick Start" updated — no longer claims the bundle auto-seeds the support archive; points operators at the new dedicated section.

Added

  • README "Support Archive (fortemi-docs)" section — what the archive is, both opt-in paths, querying via the X-Fortemi-Memory: fortemi-docs header, the additional POST /api/v1/notes/reprocess opt-in for semantic search, and the refresh-on-upgrade procedure (drop archive + remove flag file + re-seed).
  • .env.example Support Memory Archive block rewritten to document both opt-in paths with copy-paste recipes plus a quick search example.

Migration notes

Existing .env Behavior after upgrade
DISABLE_SUPPORT_MEMORY=true Unchanged — still skipped
DISABLE_SUPPORT_MEMORY=false (prior default) Changed — no longer auto-seeds. Add LOAD_SUPPORT_MEMORY=true to restore.
DISABLE_SUPPORT_MEMORY unset Was never explicit; still no auto-seed
Already-seeded instance (flag file present) Unchanged — flag file persists across restarts; the existing archive stays

No data loss for anyone. Worst case is a previously-default operator notices the docs aren't loaded on a fresh deploy and runs the one-command opt-in.

Fixed (CI infrastructure, already in v2026.5.4 via re-tag but documented here for completeness)

  • Create Gitea Release and Create GitHub Release tag-gating (#669) — Belt-and-suspenders startsWith(github.ref, 'refs/tags/v') clause on both job conditions. Gitea Actions' needs.X.result == 'success' evaluator doesn't propagate skipped upstream the way GitHub Actions does, leading to release-creation jobs firing on push-to-main with tag_name: "main" (rejected by both registries).
  • Publish Dev Image (Gitea + GitHub) tag-gating (#670) — Same evaluator quirk; added !startsWith(github.ref, 'refs/tags/') to keep dev-publish jobs from running on tag pushes and contending with the proper tag-only release publishes. Also bounded the Create GitHub Release curl with --connect-timeout 10 --max-time 60 so future hangs fail fast.
  • Shard-rebuild host-port collision (#671) — scripts/ci/rebuild-shard-in-ci.sh now picks a unique host port (30000 + ($$ % 5000)) instead of fixed 3000. The parallel publish-release (Gitea) and publish-github (ghcr.io) jobs share a runner; both invoke this script; the second-to-arrive previously crashed with "Bind for 0.0.0.0:3000 failed: port is already allocated".

2026.5.4 - 2026-05-10

First-class provider profiles for all advertised inference platforms (#654 series), three runtime-config follow-ups (#655 #656 #657), and CI hardening for the auto-shard-rebuild and release publication paths.

Added — Inference: first-class provider parity (#654)

  • Provider profile catalog (#658) — crates/matric-inference/src/provider_profiles.rs ships a &'static [ProviderProfile] describing the four v1 providers (Ollama, OpenAI, OpenRouter, llama.cpp). Each entry carries the wire protocol family (BackendKind::Ollama or BackendKind::OpenAICompatible), default base URL, required-vs-optional API key, capability list, env-var conventions, recommended default models, extra-header injection rules, and health/models endpoints. Future providers (vLLM, LiteLLM, LocalAI, Groq, Together, …) become 5-line additions to the catalog with no enum touching, no parser surface.
  • Catalog-driven /api/v1/inference/providers (#659) — Replaces hard-coded match arms with a single loop over provider_profiles::iter(). Response gains a supports_embeddings field so BYOK UIs can render the OpenRouter-style "chat only" case correctly.
  • Profile-aware /api/v1/inference/test-connection (#659) — Hints like openrouter or llamacpp route to the right wire-protocol probe (BackendKind::Ollama vs OpenAICompatible) instead of falling through to URL auto-detection.
  • OpenRouter native runtime config (#660) — POST /api/v1/inference/config accepts an openrouter block alongside ollama/openai/llamacpp. HTTP-Referer / X-Title headers default to https://fortemi.io / Fortemi; overridable per-deployment via OPENROUTER_HTTP_REFERER / OPENROUTER_APP_NAME env vars or the runtime http_referer / app_name fields.
  • Independent embedding/generation routing (#661) — MATRIC_EMBEDDING_PROVIDER env var and embedding_backend field on POST /api/v1/inference/config route embedding calls through a different provider than the active default. Killer use case: OpenRouter for chat (no embedding API), local Ollama or llama.cpp for embeddings. Validated against the catalog: pointing at a provider without the Embedding capability returns 400 with a descriptive error before persisting.
  • Atomic-swap and dry-run modes (#659) — POST /api/v1/inference/config?dry_run=true validates the merged config and returns the would-be effective state without persisting or hot-swapping. ?atomic=true probes every backend the request touches before committing; on any probe failure, abort with 503 + structured failures: [...] array. Avoids the brief error window where a half-applied config serves bad creds.

Added — Runtime-config follow-ups

  • InferenceConfigChanged SSE event on hot-swap (#657, #663) — New variant on ServerEvent emitted from POST and DELETE /api/v1/inference/config. Carries default_backend, embedding_backend, and a changed_fields array of dotted field names (openrouter.api_key, embedding_backend). API keys never appear in event payloads — only field names. Reactive UIs (HotM provider pill, MCP-tool clients, dashboards) can update without polling. DELETE events use the sentinel changed_fields: ["__reset__"].
  • Inference config audit log (#656, #664) — New inference_config_audit table records every operator-driven mutation: actor, timestamp, action (set / reset / set_archive / reset_archive), redacted before/after JSON blobs, source IP. New GET /api/v1/inference/config/audit?limit=50&changed_by=&action= endpoint returns recent entries with filter support. Best-effort writer — DB failure logs at warn but never blocks the live config change.
  • Per-archive inference provider override (#655, #665) — Storage + API surface for multi-tenant routing: new archive_inference_override table keyed by schema_name. GET / POST / DELETE /api/v1/inference/config honor X-Fortemi-Memory; archive overrides shallow-merge on top of the global config (precedence: archive_override > db_override > env > default). Audit log distinguishes archive operations via set_archive / reset_archive actions. Live runtime routing (per-archive ProviderRegistry cache + request-time resolver) is filed as #666 — substantial scope, follow-up.

Changed — Inference docs (#662)

  • README "Multi-Provider Inference" rewritten with the catalog-driven profile table (backend protocol, API key requirement, embedding support, default models per profile), runtime reconfiguration recipes (?dry_run=true, ?atomic=true curl examples), and the independent embedding/generation routing story.
  • README "Bring Your Own LLM" uses native profile names (MATRIC_INFERENCE_DEFAULT=llamacpp / openrouter) instead of the legacy MATRIC_INFERENCE_DEFAULT=openai escape hatch. Legacy recipe preserved for unknown OpenAI-compatible endpoints (vLLM, LiteLLM, on-prem).
  • CLAUDE.md "Inference Providers" expanded: dedicated OpenRouter section with all five env vars, new "Independent Embedding/Generation Routing" subsection, runtime hot-swap recipes for embedding_backend set/clear, dry_run, and atomic.
  • .env.exampleMATRIC_INFERENCE_DEFAULT lists all 4 valid ids; new MATRIC_EMBEDDING_PROVIDER block; OPENROUTER_GEN_MODEL / OPENROUTER_APP_NAME (renamed from _X_TITLE for runtime-field consistency); new llama.cpp section.

Fixed

  • matric-core event variant count assertions (#667) — Two test assertions hard-coded 47 for the variant count; #663 added a 48th. Failing CI runs from the prior release surfaced this. Fixed both events.rs::test_all_variants_metadata_is_complete and asyncapi.rs::build_spec_produces_valid_structure to expect 48.
  • Auto-shard-rebuild rate-limit (#668) — scripts/ci/rebuild-shard-in-ci.sh now passes RATE_LIMIT_ENABLED=false to the transient API container. The rebuild fires ~200 POST /api/v1/notes calls in ~4 s; the bundle's default rate limiter (100 req / 60 s) was 429-ing the second half. Companion: scripts/rebuild-docs-shard.sh now aborts with exit 1 if more than 5% of imports fail, so this kind of partial failure aborts loudly instead of silently emitting a half-empty .shard.
  • Release-job tag gating (#669) — Create Gitea Release and Create GitHub Release jobs in ci-builder.yaml had needs: publish-release|github + if: needs.X.result == 'success'; on Gitea Actions this didn't propagate skipped correctly and Create GitHub Release fired on push-to-main, attempting a release with tag_name: "main". Belt-and-suspenders fix adds explicit startsWith(github.ref, 'refs/tags/v') to both job conditions.

2026.5.3 - 2026-05-10

Support-archive pipeline overhaul: always-fresh, never-expensive on first boot.

Added

  • CI-driven fortemi-docs.shard regeneration (#652, PR #653) — scripts/ci/rebuild-shard-in-ci.sh stands up a transient Postgres + freshly-built API stack on an isolated Docker network, runs the existing scripts/rebuild-docs-shard.sh against it, sanity-checks the output (≥100 KB), and tears down via trap cleanup EXIT. Wired into all three bundle-publish jobs in ci-builder.yaml (publish-dev, publish-release, GitHub publish) so every bundle image is built against a shard regenerated from the source tree at the commit being published. Replaces the previous manual rebuild flow where the shard last updated 2026-02-19 and drifted three months out of date by the v2026.5.2 release. set -euo pipefail + size sanity check + cleanup trap means the bundle build fails loudly rather than silently shipping a stale archive. build/README.md documents both the automatic CI path and the manual local rebuild for ad-hoc testing.

Changed

  • First-boot support-archive import is FTS-only by default (PR #653) — docker/seed-support-archive.sh now passes skip_embedding_regen=true on POST /api/v1/backup/knowledge-shard/upload. Notes are imported and the Postgres tsvector triggers populate the FTS index on insert, so full-text search over the support archive works immediately. The NLP pipeline (embedding generation, auto-linking, AI revision, concept tagging) is no longer queued at seed time. This removes the implicit dependency on a working embedding provider for the bundle to be usable on first boot, and removes the cold-start CPU hit from the indexing pass.
  • Semantic search over the support archive is opt-in. The seed script prints the opt-in command on import success, and the README "Resource Requirements" section documents it:
    curl -X POST http://localhost:3000/api/v1/notes/reprocess \
      -H 'X-Fortemi-Memory: fortemi-docs' \
      -H 'Content-Type: application/json' \
      -d '{"steps":["embedding"],"revision_mode":"none"}'
    Add "linking" to the steps array for auto-link generation, or drop revision_mode:"none" to also re-write notes with AI revision. Cost/quality trade-offs depend on the configured inference provider.

2026.5.2 - 2026-05-09

Three deployment-experience fixes plus a diagnostic update on a misfiled regression.

Added

  • docker-compose.llamacpp.yml — bundled llama.cpp inference sidecar (#646, PR #649) — ghcr.io/ggml-org/llama.cpp:server exposed on :8080/v1 (OpenAI-compatible protocol). Brought up alongside the bundle via docker compose -f docker-compose.bundle.yml -f docker-compose.llamacpp.yml up -d. Tunable through LLAMACPP_MODEL_FILE, LLAMACPP_CTX_SIZE, LLAMACPP_GPU_LAYERS. NVIDIA GPU stanza commented out for opt-in. Unblocks operators who already run llama.cpp on the host and don't want Ollama.
  • docker-compose.minimal.yml — minimal-footprint overlay (#648, PR #651) — Reduces idle bundle footprint to ~2 GB by disabling support-archive seeding, swapping the fast extraction model from qwen3.5:9b (~8 GB) to qwen2.5:3b (~2 GB), capping JOB_MAX_CONCURRENT=1 + GPU_MAX_CONCURRENT=1, and trimming MAX_MEMORIES=2. Brought up via docker compose -f docker-compose.bundle.yml -f docker-compose.minimal.yml up -d. Trade-off: qwen2.5:3b chat quality is materially lower than the default — meant for resource-constrained hosts where "make it run" beats "make it good".
  • README "Bring Your Own LLM" subsection (#646, PR #649) — Copy-paste .env recipes for routing inference to llama.cpp, OpenAI proper, or OpenRouter. Explicit instructions for disabling Ollama entirely (set MATRIC_INFERENCE_DEFAULT=openai; the Ollama backend is not constructed when it isn't the default, so host.docker.internal:11434 is not probed). Ollama is now framed as one option among several, not the assumed default.
  • README "Resource Requirements" subsection (#648, PR #651) — Per-component idle RAM table, prominent DISABLE_SUPPORT_MEMORY=true callout, minimal-overlay invocation, and the qwen2.5:3b chat-quality trade-off note. Surfaces resource expectations for operators sizing hosts.
  • CONTRIBUTING.md "sqlx compile-time query checks" subsection (#647, PR #650) — Names the missing graph-class failure mode as a class, documents both resolution paths (live DATABASE_URL or cargo sqlx prepare --workspace + SQLX_OFFLINE=true), and explains when to refresh .sqlx/. README "From Source" section gets a callout pointing here so users who hit the error in the wild find the fix.

Investigated, no code change required

  • #647 "missing graph" regression — Reproducer test showed the codebase does not use sqlx::query! / sqlx::query_as! / sqlx::query_scalar! compile-time macros (only the runtime sqlx::query("…") API). cargo check --workspace succeeds on a fresh clone with no DATABASE_URL, no .sqlx/, and no SQLX_OFFLINE. cargo sqlx prepare --workspace against a clean migrated Postgres reports "no queries found". The error class the issue describes does not reproduce on main. Documentation from PR #650 stays merged as preventative coverage; awaiting the verbatim error text from the reporter to identify the actual source. See crates/matric-jobs/src/pause.rs:173 and crates/matric-db/src/schema_context.rs:108 for the runtime-only pattern this codebase uses.

Changed

  • Cargo.lock refreshed for the 2026.5.1 workspace version bump that landed in the prior release commit.

2026.5.1 - 2026-05-09

Added

  • Sidecar release artifact: matric-api-x86_64-apple-darwin (#644) — publish-sidecar.yml now cross-compiles matric-api for both aarch64-apple-darwin and x86_64-apple-darwin on the mutsu (M4 Mac mini) build host and publishes both binaries to sidecar-latest and versioned releases. The build-macos job loops over targets in one SSH session, pre-installs both rust-std targets idempotently via rustup target add, and uploads two artifacts. Both publish-sidecar-latest and publish-versioned include the new x86_64 binary in their copy/upload loops; release-body docs updated to advertise all three artifacts (Linux x86_64, macOS aarch64, macOS x86_64).
  • Doc site CI/CD for docs.fortemi.io (#645) — Two new Gitea Actions workflows wire the existing docs/ tree through the roctinam/dbbuilder publisher. docsite-build.yml validates builds on PRs and pushes to main when docs/** changes (strictLinks: true catches broken links at PR review). docsite-deploy.yml builds and rsyncs to the docs server on v* tag push (strictLinks: false so a broken link doesn't block a release). Uses the default dbbuilder template with the existing Fortemi-branded docs/config.json. Required secrets: GT_ACCESS_TOKEN, DEPLOY_SSH_KEY, DEPLOY_HOST, DEPLOY_PORT, DEPLOY_USER, DEPLOY_PATH.

Fixed

  • Clippy unnecessary_unwrap in unified_filter.rs — Replaced is_some() + unwrap() pattern with if let destructuring on (security.include_shared, security.shared_with_user). Pre-existing on main; fixed in this release so the workspace passes cargo clippy --workspace --all-targets -- -D warnings.

2026.5.0 - 2026-05-03

Fixed

  • Silent attachment data loss on filesystem backend (#631) — Three-part hardening of the filesystem-backed attachment write/read path to close the failure mode where attachment_blob rows outlived their on-disk files. The atomic write path now performs a best-effort parent-directory fsync after rename and uses a .bin.tmp suffix that the startup sweeper can discriminate. FilesystemBackend::sweep_temp_files() runs at server boot and removes stale .bin.tmp orphans (default threshold 5 minutes) from prior crashed writes. Missing-blob reads now return a structured 404 {error: "blob_missing", attachment_id, expected_path, storage_backend} distinct from the generic 500, so clients can surface a permanent-loss recovery UI instead of retrying a transient I/O fault.
  • CI Docker CLI / daemon API mismatch (#632) — build/Dockerfile.builder now installs docker-ce-cli + docker-buildx-plugin from Docker's official apt repo instead of Debian bookworm's docker.io (CLI 20.10, API 1.41). Pins the in-builder CLI to track upstream Docker so it can talk to host daemons running Docker 25.0+ / API 1.44+. Unblocks test.yml and ci-builder.yaml after the runner host daemon was upgraded to Docker 29.x (API 1.52).

Changed

  • Docs: surface HotM desktop app prominently — Public-facing docs now lead with the HotM (Hall of the Mind) desktop app for end users and clarify that this Fortemi repo is the Docker-only backend service. Adds deep links to HotM prerequisite scripts and install guides.

[2026.4.2] - 2026-04-22

Added

  • Stateless inference endpoints with per-request BYOK (#628) — Three new endpoints let downstream UIs and external integrations drive chat completions through Fortemi as a CORS-bypassing proxy without server-side key storage:
    • POST /api/v1/inference/complete — provider-agnostic chat completion with optional {provider_id, api_key, base_url} in the request body. Falls back to registered config then env vars. Supports ollama, openai, openrouter, llamacpp.
    • POST /api/v1/inference/stream — SSE streaming with real token-by-token output for Ollama; one-chunk fallback for other backends pending their streaming implementations.
    • GET /api/v1/inference/providers — lists known providers with server_configured + requires_user_key flags so BYOK UIs know which keys to prompt for.
    • ProviderRegistry::resolve_generation_inline() — factory that builds a fresh Box<dyn GenerationBackend> from transient credentials without mutating the registry. Never caches between calls.
    • OllamaBackend::set_base_url() — new setter for per-request base URL override.
  • Real token streaming for Ollama (#629) — POST /api/v1/inference/stream now emits one SSE delta event per token for Ollama backends. GenerationBackend trait gains stream_generate() and stream_generate_with_system() with default one-chunk fallback for backends without a streaming implementation. Enables per-token visibility in HotM and other downstream UIs.
  • Periodic inference provider reprobe (#630) — capabilities.inference.available no longer latches false permanently when a provider is unreachable at startup. A background probe re-checks every INFERENCE_PROBE_INTERVAL_SECS (default 30s) and updates AppState.inference_available. Chat handler returns 503 + retry_after when the provider is currently unreachable. Emits InferenceAvailabilityChanged SSE event on transitions so clients can clear/raise offline banners without polling /health.
  • Caller-defined extraction pipeline — Optional pipeline field on CreateNoteRequest to opt-in to specific AI processing stages (revision, title_generation, concept_tagging, reference_extraction, metadata_extraction, document_type_inference). Empty array stores the note without any AI processing. Backwards compatible — omitting pipeline runs the full default pipeline.
  • Configurable archive extraction limitsARCHIVE_MAX_EXTRACT_BYTES (default 1 GB, was 100 MB) and ARCHIVE_MAX_SINGLE_FILE_BYTES (default 50 MB, was 10 MB) env vars replace hardcoded constants. MAX_FILES cap removed entirely.
  • Multi-provider installer manifestsetup.manifest.yaml extended to support configuring multiple inference providers simultaneously during guided deployment. Complements the 8 installer scripts shipped in v2026.4.0.
  • README redesign — Restructured with normalized design language: problem/solution framing, ASCII ingest-to-search pipeline diagram, API endpoint tables, full MCP tool table, search capabilities and media processing sections, multi-provider inference slug reference, and security model overview.
  • Feature and hardware requirements matrix — New docs/content/feature-hardware-matrix.md maps every feature to its minimum hardware requirements, GPU VRAM tiers, and optional dependencies.

Fixed

  • Revision mode "none" creates fake history (#625) — Notes created or updated with revision_mode=none no longer write misleading note_revision records with "Original preserved (no AI revision)" rationale. New sync_revised_to_original_tx keeps FTS content synced without creating fake revision history.
  • Redis connection timeout (#624) — 5-second timeout on ConnectionManager::new() prevents server startup from blocking indefinitely when Redis is unreachable. Bundle image defaults to REDIS_ENABLED=false.
  • arm64 optional components (#623) — Build args ENABLE_OPEN3D, ENABLE_POSTGIS, ENABLE_OCR, ENABLE_FFMPEG allow disabling platform-specific components for cross-architecture builds.
  • Think tokens blocked streaming chunks — Disabled Qwen3.5 think mode during streaming so content delta events flow immediately without waiting for the full <think>…</think> block to complete.
  • :latest Docker tag overwritten by dev builds — CI now reserves :latest and :bundle-latest exclusively for tagged releases. Dev pushes produce :main and :sha-<short> only. Downstream consumers should pin to :main for rolling dev or :<version> for pinned releases. Fixes v2026.4.0 being masked on ghcr.io by later main builds.

[2026.4.0] - 2026-04-04

Added

  • Synchronous chat API (#549) — POST /api/v1/chat provides direct LLM conversation with GPU concurrency semaphore, multi-turn history, and model selection. GET /api/v1/chat/models lists available models with metadata. Health endpoint exposes capabilities.chat for client availability detection.
  • Chunked audio transcription (#540, #541, #542, #543) — Long audio files automatically split into chunks for parallel transcription via fan-out AudioChunkTranscription jobs with atomic dual fan-in for video assembly.
  • Content-type-aware AI revision (#571) — Revision pipeline produces type-specific output (meetings get Decisions/Action Items, movies get Synopsis/Cast). Adaptive chunking, chunk-count-based revision budgets, and user-configurable chunk_max_chars/chunk_overlap per request.
  • Decomposed 3D model extraction (#531, #533, #534, #535) — Atomic per-view vision jobs with RENDER_GPU tier, Open3D EGL GPU support, and AI revision after view assembly.
  • Video keyframe vision (#550) — KeyframeCharacterVision and KeyframeSettingVision job types with improved scene-dialog interleaving and keyframe merging prompts.
  • MMR diversity search and access analytics — Maximal Marginal Relevance for search diversity, access frequency tracking, cold-spot detection, and agent-reflection document type.
  • Inference runtime config API (#568-570) — Runtime Ollama configuration with connection testing via GET/PUT /api/v1/config/inference. Extended with llama.cpp section; every PUT rebuilds the full provider registry for hot-swap without server restart.
  • llama.cpp provider — Register llama.cpp as a first-class inference provider via LLAMACPP_BASE_URL. Routes through the existing OpenAIBackend (same HTTP protocol, zero new backend code). Use provider-qualified slugs (llamacpp:model-name) for per-request routing. LLAMACPP_BASE_URL, LLAMACPP_API_KEY, OPENAI_API_KEY, and OPENROUTER_API_KEY passed through in compose.
  • Inference resilience (#545, #546, #547, #548) — Retry with exponential backoff, circuit breaker, fail-fast detection, and memory limits for sidecar services (Whisper, pyannote, GLiNER).
  • Edge-first hardware profilesCOMPOSE_PROFILES selects deployment tier: edge (CPU sidecars, 6-8GB VRAM), gpu-12gb, gpu-24gb. Defaults target RTX 3060/4060/5060.
  • Qwen3.5 model family — Default generation upgraded to qwen3.5:9b (262K context, natively multimodal). Single model serves generation, fast extraction, and vision with one ~6.5GB VRAM load.
  • HotM consumer contract tests (#549) — Chat endpoint contract tests for HotM integration.
  • Installer scriptssetup.manifest.yaml machine-readable install manifest for the AIWG installer framework. installer/scripts/ contains 8 shell scripts for guided deployment: clone.sh, configure.sh, deploy.sh, pull-models.sh, check-ports.sh, setup-nvidia.sh, verify.sh, reset.sh.

Changed

  • GPU sidecar defaults — Whisper and pyannote now run on CPU by default to preserve GPU VRAM for inference. GPU variants available via --profile gpu-12gb or --profile gpu-24gb. Breaking: existing deployments using GPU sidecars must set COMPOSE_PROFILES=gpu-12gb or gpu-24gb.
  • GPU job scheduling — GPU jobs serialize by default to prevent VRAM contention. Ollama models proactively unloaded between tier transitions.
  • Video chunk sizeREVISION_VIDEO_CHUNK_SIZE_MAX reduced from 60K to 20K characters to prevent job-level timeout on long videos.
  • Job timeoutJOB_TIMEOUT_SECS default raised from 600s to 1800s; env-var clamp raised to 7200s.
  • Concept tagging pipeline (#538, #539) — ConceptTagging chains from AiRevision (operates on enriched content). Enriches with existing DB concepts for better consistency.
  • Media-aware job ordering (#578) — AI revision deferred for notes with pending media attachments; bypass on explicit reprocess.
  • MCP tools — Purge tools exposed in core toolset (#530). Tool count updated to 43.
  • PG 18.3 — Reverted PG 18.2 TOAST workaround after upstream fix (#419). All convert_from(convert_to(...)) instances reverted to substring().
  • Workspace version: 2026.2.132026.4.0

Fixed

  • 3D rendering (#538, #539) — Normalize extreme-scale models to prevent blank renders; validate render quality to prevent grey thumbnails; fix thumbnail MIME type.
  • Inference — Disable thinking mode for Qwen3.5 generation (prevents empty responses from thinking models).
  • AI revision on non-default archives — Single-note reprocess_note was missing the archive schema in the AI revision job payload, causing "Failed to fetch note" errors on non-default archives.
  • Video revision budget — Total revision budget now computed as Σ(per-chunk adaptive timeouts); deadline checked before each chunk so the loop exits cleanly rather than timing out mid-chunk.
  • TUS uploads (#544) — Add GET handler for upload finalization and DefaultBodyLimit on TUS routes.
  • Migrations — Use snake_case enum values for job_type; use correct singular document_type table name.
  • Archives — Sync column drift in archive schemas on auto-migration; exclude identity columns from archive clone.
  • Docker — Add NVIDIA EGL ICD for GPU rendering; add nvidia default runtime migration (#542).
  • Dependencies — Patch security vulnerabilities in npm and Rust dependencies.
  • GLiNER OOM — Increased default memory limit from 2GB to 4GB.

[2026.2.13] - 2026-02-23

Added

  • Independent sidecar CI/CD — GLiNER and pyannote Docker images now have dedicated build-and-release workflows (build-gliner.yaml, build-pyannote.yaml) that publish to both internal and GHCR registries. Sidecar images are released independently from the main API/bundle via their own tags (sidecar-gliner-v*, sidecar-pyannote-v*), avoiding expensive ML image rebuilds on every core release.

Changed

  • docker-compose.bundle.yml default tags — Sidecar image tags changed from *-main (internal-only) to *-latest (available on both registries). Users setting FORTEMI_REGISTRY=ghcr.io no longer need to manually override FORTEMI_GLINER_TAG or FORTEMI_PYANNOTE_TAG.
  • Sidecar builds removed from main release pipelinepublish-release and publish-github jobs in ci-builder.yaml no longer build GLiNER/pyannote, reducing release build time.
  • Workspace version: 2026.2.122026.2.13

2026.2.12 - 2026-02-22

Added

  • TUS v1.0.0 Resumable Upload Protocol (#528) — Standards-compliant tus resumable file uploads with Creation, Termination, and Checksum extensions. Enables reliable upload of large files over unreliable connections with automatic resume from the last successful byte. Endpoints at /api/v1/attachments/{note_id}/tus.
  • Atomic Per-Frame Keyframe Vision Pipeline (#526) — Keyframe vision descriptions processed as individual KeyframeVision jobs instead of inline during extraction. Enables per-frame checkpointing, retry, parallel processing, and independent failure recovery. Keyframes persisted as derived attachments first, then described asynchronously.
  • Thumbnail Sprite Sheet Generation (#525) — ThumbnailSprite handler generates CSS sprite sheets from video keyframes with WebVTT timestamp maps, enabling video preview scrubbing in UI clients.
  • Video Extraction Hardening — Feature-length video support with adaptive keyframe budgets, timeout scaling, and memory-bounded frame processing.
  • 3D Model Rendering as Derived Attachments — Multi-view 3D renderings persisted as derived child attachments with individual AI descriptions per view and preview thumbnail. Ground-plane grid and off-white background for better visual clarity.
  • Speaker Diarization Pipeline (#497) — pyannote-based speaker identification for audio and video transcripts. Runs as a GPU sidecar container (DIARIZATION_BASE_URL). Produces speaker-labeled VTT/SRT/TXT caption files and a speaker configuration block in note content. Speaker names editable via SpeakerRelabel job.
  • Speaker Diarization Foundation Types (#497) — Core inference types and backend abstraction for diarization providers.
  • Media Optimize Handler (#506) — Pre-generates streaming-friendly media variants during attachment upload using ffmpeg. Variant types: faststart (moov atom relocation), web_compatible (H.264+AAC remux), audio_only (extracted audio), preview_720p (downscaled preview), web_audio (AAC transcode), audio_preview (lossless→lossy). Variants stored as derived attachments and served via ?variant= query parameter on the download endpoint.
  • Media Optimize Flag (#506) — media_optimize parameter on attachment upload API and MCP manage_attachments tool. Defaults to true for video/audio content types.
  • Email Extraction Adapter (#508–#512) — RFC 2822 / MIME email parsing (.eml, .mbox). Extracts message body, headers, and binary attachments as derived child attachments that trigger their own extraction jobs.
  • Spreadsheet Extraction Adapter (#508–#512) — Excel (.xlsx, .xls) and ODS spreadsheet extraction. Converts each sheet to markdown tables.
  • Archive Extraction Adapter (#514–#515) — ZIP, tar, and gzip archive extraction. Produces file listing with text content extraction (capped at 1000 files, 100 MB total).
  • Derived Attachment Storage (#498, #502) — Thumbnails, transcripts (VTT, SRT, TXT), and media variants stored as child attachments linked to their source via extracted_metadata JSON.
  • Video Thumbnail & Audio Waveform (#502, #503) — Auto-generated preview images persisted as derived attachments during extraction.
  • MP4 Faststart Optimization (#503) — Automatic moov atom relocation during video extraction for progressive download.
  • Diagramming & Layout Document Types (#516) — New document type category supporting SVG, Graphviz (DOT), Mermaid, D2, PlantUML, and layout formats.
  • HTTP Range Request Support (#493) — Partial content download (Range header) for large attachment files.
  • Open3D GPU Renderer (#492) — Replaces Three.js with Open3D for 3D model multi-view rendering. Supports EGL headless rendering on GPU.
  • Global Attachment ListingGET /api/v1/attachments endpoint for listing all attachments across notes.
  • Related Notes with LLM SummaryGET /api/v1/notes/{id}/related endpoint returns related notes with AI-generated context summary.
  • Document Type Slug Validation (#490, #491) — Accept document_type slug on note creation; validate revision_mode parameter.
  • Handler-Initiated SSE Eventsjob.queued SSE events now emitted for downstream jobs queued by handlers (e.g., Extraction → Embedding), not just API-initiated jobs.

Changed

  • axum 0.7→0.8 Framework Upgrade (#524) — Major dependency upgrade: axum 0.7→0.8, tower 0.4→0.5, tower-http 0.5→0.6, tokio-tungstenite 0.24→0.28. Adapts all Service implementations to call(&self) signature change.
  • Workspace version: 2026.2.112026.2.12 (56 commits)

Fixed

  • KeyframeVision Jobs Silently Orphaned Without Vision Backend (#529) — KeyframeVisionHandler is now always registered regardless of vision backend availability. When the vision backend is unavailable, jobs return Retry and stay in the queue until the backend is configured, rather than being silently orphaned with no handler to execute them. Added startup warning when vision backend is missing.
  • Keyframe DerivedFiles Lost to TempDir Drop — Keyframe JPEG bytes now read inline before the temp directory is dropped, matching the audio extraction pattern. Previously all keyframe attachments were silently lost, breaking the entire downstream pipeline (no KeyframeVision jobs, no ThumbnailSprite content).
  • Keyframe Extraction Gated on Vision Backend (#527) — Video keyframes now extract regardless of vision backend availability. Keyframes are valuable for thumbnails and sprite sheets even without AI descriptions.
  • Native uuidv7() for TUS Uploads (#528) — Switched TUS upload tracking from application-generated UUIDs to PostgreSQL native uuidv7(); suppressed clippy too_many_arguments on upload handler.
  • Audio Transcript Key Normalization (#523) — Unified transcript_segments key in API responses for UI consistency.
  • ETag Middleware Bypass for Downloads (#522) — File download responses now skip ETag calculation, fixing slow responses for large attachments.
  • Video Audio Track and extracted_text (#517–#521) — Populate extracted_text from transcription and persist video audio track as derived attachment.
  • GLB Adapter and Diagnostics Fixes (#517–#521) — Multiple fixes for 3D model extraction, AI revision context, and diagnostic snapshot handling.
  • Derived Caption Deduplication (#516) — Prevent duplicate VTT/SRT/TXT caption files; add speaker labels to diarized captions.
  • Pyannote 4.x Compatibility — Handle DiarizeOutput from pyannote.audio 4.x, replace deprecated use_auth_token parameter, normalize audio to WAV before diarization.
  • Nginx Upload and Proxy Configuration — Dedicated upload endpoint with proxy_request_buffering off for streaming; 1 GB upload limit; optimized proxy headers for large file operations.
  • AI Revision Cross-Contamination (#494) — Prevent RAG revision from injecting unrelated note content.
  • Extraction Reliability — Fix job deduplication, OGG format detection, and timeout handling.
  • Inline Disposition for Media — Use inline content disposition for browser-playable media types; fix CORS headers for streaming.
  • Extraction Re-queue Logic — Only re-queue downstream NLP jobs when extraction actually updates note content.
  • AI Description Propagation (#492) — Persist ai_description from vision/3D extraction and propagate to note metadata.
  • SSE Progress Alignment — Align progress events with documented checkpoint percentages across all handlers.
  • Clippy Lint Warnings — Fix nonminimal_bool, neg_cmp_op_on_partial_ord, and approx_constant in test assertions.

Documentation

  • CPU-only deployment guide (docs/content/cpu-only-deployment.md)
  • Job monitoring guide expanded with multi-chunk tracking, tier escalation, and SSE event emission completeness table
  • Extraction pipeline design updated with all 13 extraction strategies and derived file documentation
  • MediaOptimize handler documented in job monitoring guide (progress stages, variant types, download endpoint)
  • KeyframeVision, KeyframeAssembly, and ThumbnailSprite handlers documented in job monitoring guide
  • Media Integration Guide (docs/content/media-integration-guide.md) — frontend integration for streaming playback, subtitles, sprite sheets, TUS uploads, SSE events
  • Full documentation sync with current code state

2026.2.11 - 2026-02-20

Fixed

  • Stale Job Reaping — Worker automatically reaps orphaned running jobs on startup. Jobs stuck longer than 2× the timeout threshold (600 s) are reset to pending (with retries remaining) or failed. Uses FOR UPDATE SKIP LOCKED CTE to avoid blocking concurrent workers. (ADR-084)
  • PDF Null Byte SanitizationPdfTextAdapter now strips null bytes (\0) from both pdfinfo metadata values and extracted text before database insertion, preventing PostgreSQL 22P05 encoding errors on legacy PDFs (Acrobat 3.0/4.0 era). (ADR-085)
  • Ambiguous Column in Reap CTE — Fully qualified retry_count reference in the stale-job reap query to prevent PostgreSQL ambiguity error.
  • Archive Note Countslist_archive_schemas now computes live note counts instead of returning stale cached values.

Changed

  • Testdb max_locks_per_transaction — Increased to 256 in the test database image to support parallel archive schema tests without lock exhaustion.

Documentation

  • ADR-084: Stale job reaping on worker startup
  • ADR-085: Null byte sanitization in PDF extraction pipeline
  • ADR index backfilled with all entries ADR-037 through ADR-085
  • Operators guide: automatic stale job recovery section
  • Troubleshooting: stale jobs, PDF null byte errors
  • Retired stale UAT reports

2026.2.10 - 2026-02-19

Highlights

This release delivers 86 commits, 206 files changed, +19K/-41K lines closing 64 issues (#422–#485) across three major themes: a complete Graph Quality Overhaul (Louvain community detection, SNN scoring, PFNET sparsification, automated maintenance pipeline), a comprehensive SSE Event System (46 event types, replay, filtering, backpressure), and a hardened Extraction Pipeline (GLiNER NER, tiered job architecture, cascaded model routing, configurable document composition for embeddings).

What Changed Why You Care Issues
Graph Quality Pipeline Automated normalize → SNN → PFNET → diagnostics in a single API call. Breaks the "seashell" pattern of noisy, unstructured graphs. #470–#484
Louvain Community Detection Topically cohesive note clusters with SKOS-derived labels #473
SNN + PFNET Graph Analysis Structural similarity scoring and topology-preserving edge pruning #474, #476
SSE Event System Overhaul 46 event types, versioned envelope, replay, auth-scoped filtering, backpressure #450–#465
Tiered Job Architecture Three-tier compute model (CPU_NER → FAST_GPU → STANDARD_GPU) with queue-based escalation #436–#449
GLiNER NER Sidecar Zero-shot NER at <300ms/doc, CPU-only, enabled by default #437
Configurable Embedding Composition Choose what goes into embeddings (title, content, concepts, tags) per embedding set #485
Versioned Graph API v1 payload contract with community hints and server-side guardrails #467–#469
Pause/Resume Jobs Global and per-archive job processing control, persisted across restarts #466
Multi-Provider Inference Provider-qualified model slugs with discovery endpoint #431
12 New ADRs Architecture decisions documented for graph, embeddings, jobs, inference, and branding ADR-072–ADR-083

Added

  • Graph Quality Maintenance Pipeline (#482) — Automated normalize → SNN → PFNET → diagnostics snapshot pipeline triggered via POST /api/v1/graph/maintenance. Brings graph topology to a consistent, analytically useful state in a single operation.

  • Louvain Community Detection (#473) — Partition the knowledge graph into topically cohesive communities using the Louvain algorithm. Community labels are derived from SKOS concept terms for human-readable groupings.

  • SNN Similarity Scoring (#474) — Shared Nearest Neighbor (SNN) scoring for structural graph analysis. Identifies strongly connected note clusters based on neighborhood overlap rather than raw embedding distance.

  • PFNET Sparsification (#476) — Pathfinder Network (PFNET) algorithm for topology-preserving edge pruning. Removes redundant edges while retaining the shortest-path skeleton of the knowledge graph.

  • MRL 64-dim Coarse Community Detection (#477) — Fast community detection using 64-dimensional Matryoshka Representation Learning embeddings. Provides coarse-grained topic groupings at significantly lower compute cost than full-dimension clustering.

  • Edge Community Filter and Structural Collection Edges (#480) — Filter graph edges by community membership (intra-community or inter-community). Structural edges from collection membership are included in graph payloads for richer topology.

  • Embedding Quality Diagnostics with Snapshot Comparison (#483, #484) — Capture point-in-time embedding quality metrics (coverage, dimension statistics, cluster cohesion) and compare against prior snapshots to detect quality drift over time.

  • Graph Maintenance API Endpoint (#482) — POST /api/v1/graph/maintenance triggers the full maintenance pipeline. Returns a structured report with per-step outcomes and timing.

  • 7 New Graph API Endpoints — New endpoints for graph maintenance, diagnostics, community inspection, SNN scoring, PFNET sparsification, coarse community detection, and edge community filtering.

  • 2 New MCP Core Toolstrigger_graph_maintenance and coarse_community_detection added to the core tool surface (37 core tools total).

  • 20 Graph Algorithm Unit Tests — Unit test coverage for normalization, SNN, Louvain, and PFNET implementations.

  • Pause/Resume Job Processing (#466) — Global and per-archive pause/resume control for the job worker. State persisted in system_config table across container restarts. Endpoints: GET /api/v1/jobs/status, POST /api/v1/jobs/pause, POST /api/v1/jobs/resume, POST /api/v1/jobs/pause/{archive}, POST /api/v1/jobs/resume/{archive}.

  • Tiered Job Architecture — Three-tier compute model: CPU_NER (tier 0, GLiNER), FAST_GPU (tier 1, qwen3:8b), STANDARD_GPU (tier 2, gpt-oss:20b). Queue-based tier escalation replaces inline model fallback — each job runs exactly one model, failures enqueue at the next tier.

  • GLiNER NER Sidecar (#437) — Zero-shot named entity recognition via GLiNER (0.5B BERT, CPU-only, <300ms/doc). Runs as a Docker sidecar (http://gliner:8090). Enabled by default in Docker bundle; set GLINER_BASE_URL= to disable.

  • Fast-First Chunked Extraction (#439) — Small model (qwen3:8b) handles concept tagging, reference extraction, and title generation with automatic document chunking. Large documents split into context-window-sized chunks and processed in parallel.

  • Related Concept Inference (#435) — New pipeline step infers skos:related relationships between extracted concepts using LLM analysis.

  • Reference Extraction — Bibliographic reference and entity extraction pipeline step with provenance metadata.

  • Metadata Extraction & Document Type Inference (#430) — AI-extracted structured metadata (authors, year, venue, DOI) and automatic document type classification from filename/MIME/content.

  • SSE Event System Overhaul (#451-#465) — Versioned envelope schema, memory-scoped auth routing, server-side type/entity filtering, Last-Event-ID replay, backpressure with event coalescing, health metrics, and expanded catalog to 46 event types covering notes, attachments, collections, archives, jobs, and system events.

  • Multi-Provider Inference Routing (#431) — Provider-qualified model slugs (ollama:qwen3:8b, openai:gpt-4o) with model discovery endpoint and per-operation model selection.

  • SKOS Concept Scheme Managementmanage_concepts MCP tool extended with scheme CRUD operations.

  • Constrained JSON Decoding — Structured JSON output for extraction jobs using Ollama's constrained generation.

  • SKOS Concepts in NoteFull — API responses include SKOS concept tags on note detail endpoints.

  • Hierarchical SKOS Auto-Tagging (#425) — Concept tagging uses hierarchical SKOS broader/narrower relationships for richer taxonomy.

  • AsyncAPI 3.0 Spec — Runtime-generated AsyncAPI 3.0 specification for the SSE event catalog at /api/v1/asyncapi.

  • Configurable Embedding Document Composition (#485) — Choose which fields compose embedding text (title, content, concepts, tags) per embedding set. Stored as DocumentComposition model with migration. MCP manage_embeddings tool extended with document_composition action.

  • Auto Re-embed on Composition Change (#485) — Changing an embedding set's composition config automatically triggers re-embedding of all affected notes.

  • Versioned Graph API Payload Contract (#467, #468, #469) — v1 versioned response format for all graph endpoints with community hints, neighborhood explainability, and server-side guardrails with tuning defaults.

  • Automatic Graph Maintenance After Embedding — Graph maintenance pipeline auto-triggers after note ingest completes, keeping graph topology fresh without manual intervention.

  • RNG Linking Strategy (#478) — Relative Neighborhood Graph for local edge sparsification, complementing PFNET for different graph density regimes.

  • Parallel Subtask Execution (#441) — Extraction pipeline subtasks execute in parallel where dependencies allow, improving throughput on multi-core systems.

Changed

  • Embedding enrichment instruction prefix (#472) — Embeddings generated for graph use now include a clustering: instruction prefix, improving cluster cohesion in community detection.

  • Embedding content separated from record metadata (#479) — Embedding payloads no longer bundle record metadata fields. Content and metadata are stored and retrieved separately, reducing payload size and eliminating cross-contamination during similarity scoring.

  • TF-IDF concept filtering excludes high-frequency concepts (#475) — Concepts appearing in a large proportion of notes are treated as "stopword" concepts and excluded from graph edge weighting, reducing noise in dense graphs.

  • Edge weight normalization with configurable gamma (#470) — Graph edge weights are normalized using a configurable gamma parameter, making weight distributions comparable across archives of different sizes.

  • GraphConfig extended with 10+ new tuning parameters — New parameters cover SNN neighborhood size, PFNET q and r values, Louvain resolution, MRL dimension selection, gamma normalization, and community filter mode.

  • MCP core tools expanded from 27 to 37: added trigger_graph_maintenance, coarse_community_detection, and 8 additional graph/observability tools.

  • Default concept target lowered from 15 to 5 per note (EXTRACTION_TARGET_CONCEPTS).

  • Pipeline reorder (#424) — Embedding now runs after concept tagging, using enriched content for better semantic search.

  • GLiNER extracted to sidecar — Removed from bundle image, runs as independent container for simpler upgrades and resource isolation.

  • Inference endpoint — Switched from /api/generate to /api/chat for Ollama generation.

  • Job handlers normalized — MetadataExtraction, TitleGeneration, and RelatedConceptInference handlers now use queue-based tier escalation (matching ConceptTagging and ReferenceExtraction pattern).

  • Workspace version: 2026.2.92026.2.10 (86 commits)

Deprecated

  • Mutual k-NN filter (#471) — Deferred as a no-op. The create_reciprocal step already enforces bidirectional edges, making a separate mutual k-NN filter redundant. The filter remains in the codebase but is not applied during graph construction.

Fixed

  • LLM returning object instead of array in JSON parsing for extraction jobs.
  • SKOS breadth limit only counts promoted concepts toward the limit.
  • SKOS breadth limit raised from 50 to 200 children per concept.
  • WebSocket endpoint routing at /api/v1/ws (#423).
  • Multi-memory schema context in extraction pipeline (#426).
  • MCP StreamableHTTP transport JSON responses (#422).
  • Default concept scheme seeding in new archives.
  • Chunked extraction resilience to partial failures.
  • Prevent 20B model escalation when GLiNER produces enough concepts.
  • Three graph bugs causing stuck jobs and empty graph output (#485).
  • Concept labels removed from default embedding text to reduce noise in similarity scoring (#485).
  • Tiered job escalation: phase-2 and phase-3 jobs no longer queue prematurely on tier escalation (#444, #445).
  • Escalation methods now use queue_deduplicated() instead of queue() (#446).
  • Job warmup() timeout added to prevent stalling the drain loop (#447).
  • Tier-0 NER skips DB round-trip when GLiNER backend is unavailable (#448).
  • Clippy warnings resolved for Rust 1.92 compatibility.

Documentation

  • Comprehensive documentation overhaul — Rewrote and consolidated graph quality pipeline docs, extraction pipeline docs, and architecture guides.
  • 12 new ADRs (ADR-072 through ADR-083): inference provider abstraction, graph quality pipeline, Louvain community detection, PFNET sparsification, MRL coarse community detection, embedding content separation, SNN sparse graph guard, global job deduplication, auto graph maintenance, document composition, queue-based tier escalation, brand naming.
  • SSE event catalog and migration guide rewritten (#461).
  • Tagging and inference docs updated to clarify automatic vs manual tagging.
  • fortemi-docs shard rebuilt from current sources.
  • CLAUDE.md accuracy pass — Updated MCP core tool list to 37 tools, full tool count to 202, test isolation guidance to recommend UUIDs.
  • README.md accuracy pass — Updated full tool count from 187 to 202.

Issues Resolved

64 issues closed (#422–#485):

  • Epics: #436 (RLM Extraction Pipeline), #450 (SSE Multi-Client Reactive State), #481 (Graph Quality Overhaul)
  • Graph Quality: #467–#470, #471, #472, #473, #474, #475, #476, #477, #478, #479, #480, #482, #483, #484
  • SSE Events: #451–#465
  • Extraction Pipeline: #437, #438, #439, #440, #441, #442
  • Tiered Jobs: #443–#449, #466
  • Embeddings: #485
  • Multi-Provider Inference: #431
  • SKOS/Tagging: #425, #430, #435
  • Fixes: #422, #423, #424, #426, #428, #429

2026.2.9 - 2026-02-16

Highlights

This is the largest release since the project's inception — 290 commits, 756 files changed, +103,000 / -154,000 lines across every layer of the stack. The headline feature is Multi-Memory Architecture: fully isolated knowledge bases backed by PostgreSQL schema-per-memory isolation, with zero-drift cloning, per-request routing, session-scoped MCP memory selection, federated cross-memory search, and memory-scoped backup/restore.

Alongside multi-memory, this release resolves 100+ issues discovered during comprehensive UAT (530+ MCP test cases, 96.3% pass rate), upgrades PostgreSQL 16 → 18, enables SCRAM-SHA-256 auth, ships native uuidv7() defaults, adds agent-friendly MCP tool surface (27 core tools with discriminated-union pattern), hardens security with resource limits and SQL injection fixes, rewrites the database restore pipeline, adds comprehensive content extraction framework, ships multimodal capabilities (vision, audio, video, 3D models), and includes a built-in 243-note documentation archive loaded on first boot.

What Changed Why You Care Learn More
Multi-Memory Architecture Create, switch, and isolate independent knowledge bases per PostgreSQL schema User Guide · Design · ADR-068
PostgreSQL 18 + SCRAM-SHA-256 Major database upgrade with enhanced password authentication security ADR-096
MCP Agent-Friendly Tools 23-tool "core" mode with discriminated-union pattern (capture_knowledge, search, record_provenance, manage_tags, manage_collection, manage_concepts) MCP Guide · ADR-095
X-Fortemi-Memory Header Per-request memory routing with 3-step fallback (header → default cache → public) Architecture
MCP Session Memory select_memory / get_active_memory tools bind a memory to an AI agent session MCP Guide · Agent Guide
Federated Search Search across multiple memories in a single query Search Guide
Per-Archive Search Enable semantic and FTS search in non-default archives with schema-pinned connection pools Search Guide
Memory-Scoped Backup/Restore Per-memory pg_dump --schema and DROP SCHEMA CASCADE restore Backup Guide · Operations
Content Extraction Pipeline Document type registry (131 types), smart chunking, PDF/code/media adapters Document Types · Extraction Design
Video Multimodal Extraction Scene-detection keyframe extraction + audio-visual alignment + temporal context Video Guide
3D Model Understanding Multi-view rendering extraction via Three.js + vision model description 3D Models Guide
Auth Middleware & OAuth Scopes Centralized scope enforcement, configurable token lifetimes, API key support Authentication · ADR-071
MCP File-Based I/O Replaced base64 binary tools with HTTP API upload/download for remote agents MCP Guide · File Attachments
Database Restore Rewrite Thread-safe psql pipe, extension-owned object exclusion, FTS index rebuild Backup Guide
Security Hardening SQL injection fixes, resource limits, input validation, wildcard injection prevention Security
Multipart Shard Upload Upload knowledge shards via multipart/form-data — no base64 overhead, supports large shards Backup Guide
Built-in Documentation Archive 243-note fortemi-docs knowledge base automatically loaded on first boot Getting Started
Adaptive Tag-Boosted Linking Two-phase linking pipeline: tag-overlap candidates boosted before semantic scoring Knowledge Graph
Event-Driven Job Worker PostgreSQL NOTIFY/LISTEN wake pattern with concurrent job processing Configuration
PG 18.2 TOAST Workaround Automatic workaround for PostgreSQL 18.2 substring/left() UTF-8 bug (#19406) Troubleshooting

Added

  • Multipart Shard UploadPOST /api/v1/backup/knowledge-shard/upload accepts multipart/form-data file uploads, eliminating base64 encoding overhead and ARG_MAX limits for large shards. JSON endpoint preserved for backward compatibility.

  • Built-in Documentation Archive (#411) — On first boot, the Docker bundle automatically imports a 243-note fortemi-docs knowledge base containing all user guides, architecture docs, research papers, ADRs, and SDLC artifacts. Idempotent via flag file at $PGDATA/.fortemi-docs-seeded.

  • Adaptive Tag-Boosted Linking (#420) — Two-phase auto-linking pipeline: Phase 1 discovers tag-overlap candidates and boosts their similarity scores; Phase 2 applies standard embedding-based linking. Produces denser, more meaningful knowledge graphs.

  • Event-Driven Job Worker — PostgreSQL NOTIFY/LISTEN wake pattern replaces polling for immediate job pickup. Configurable concurrent processing via JOB_MAX_CONCURRENT (default: 4) with drain-loop shutdown.

  • MCP Core Tools Expanded — Added manage_archives, manage_encryption, manage_backups, and manage_embeddings to the 23-tool core surface (now 27 core tools).

  • Vision and Audio Enabled by DefaultOLLAMA_VISION_MODEL and WHISPER_BASE_URL now configured by default in Docker bundle for out-of-box multimodal extraction.

  • Capacity PlanningMAX_MEMORIES scales with hardware: 10 (8GB), 50 (16GB), 200 (32GB), 500 (64GB+). Documentation updated with sizing guidance.

  • Per-Archive Search — Enable search in non-default archives

    • Per-schema connection pools with search_path pinned per archive
    • Cached HybridSearchEngine instances per schema
    • Removes the 400 guard for non-default archives
    • Enables semantic and FTS search in all memory archives
  • HNSW Algorithm 4 Graph Topology (#386) — Graph topology statistics using HNSW Algorithm 4 for efficient neighbor traversal

  • Light Revision + Softer Licensing — AI revision defaults to light mode with improved licensing messaging

  • Live Health Probe/health/live readiness probe with dependency checks for all critical services

  • Move Collection with Cycle Detection — Move collections in hierarchy with circular reference prevention

  • PKE Keyset REST API — Full REST API endpoints for PKE keyset management

  • Ad-hoc Image Description APIPOST /api/v1/vision/describe + MCP describe_image tool

  • Auto-Generated OpenAPI Spec — utoipa replaces static OpenAPI spec with auto-generation from code annotations

  • EXIF Metadata Extraction (#278) — Automatic EXIF metadata extraction on image upload

  • Note-Level Provenance (#262) — Notes can have location + time provenance for spatial-temporal context

  • Provenance Creation MCP Tools (#261) — MCP tools for recording W3C PROV provenance

Multi-Memory Architecture (#170 Epic, #171–#181)

The flagship feature of this release. Each "memory" is a fully isolated PostgreSQL schema containing all per-memory tables (notes, tags, collections, links, embeddings, SKOS concepts, files, templates, etc.) while sharing infrastructure tables (auth, jobs, migrations) in the public schema.

  • Zero-drift schema cloning (#171) — CREATE TABLE ... (LIKE public.table INCLUDING ALL) with deny-list approach. New migrations automatically included without code changes. FK discovery from information_schema with proper schema-qualification.
  • Text search config cloning (#172) — Custom FTS configurations (e.g., matric_english) cloned into each memory schema via pg_ts_config catalog queries.
  • Per-request memory selection (#173) — X-Fortemi-Memory header on every API request. Middleware validates memory exists (404), resolves schema, injects ArchiveContext. 3-step fallback: header → DefaultArchiveCache (60s TTL) → public schema.
  • All 91 API handlers routed — Every handler uses SchemaContext with SET LOCAL search_path per transaction. _tx method pattern on all repositories for transaction-scoped isolation.
  • MCP session memory selection (#174) — select_memory and get_active_memory tools. Session state tracked per transport. All MCP API calls automatically include X-Fortemi-Memory header.
  • Memory-scoped backup (#175) — GET /api/v1/backup/memory/:name using pg_dump --schema.
  • Memory-scoped restore (#176) — DROP SCHEMA IF EXISTS CASCADE + pg_restore. Clean and atomic because memories are self-contained schemas.
  • Cross-memory federated search (#177) — POST /api/v1/search/federated with dynamic UNION ALL across specified schemas. Results annotated with memory_name.
  • Memory clone endpoint (#178) — POST /api/v1/memories/:name/clone with FK-ordered INSERT...SELECT via recursive CTE. Handles generated columns. No superuser required.
  • Memory API naming (#179) — /api/v1/memories/* routes with /api/v1/archives/* backward compatibility. MCP tools use "memory" terminology.
  • Schema drift detection test (#180) — CI-time integration test comparing archive table/column structure against public schema.
  • Default archive seed migration (#158) — Fresh deployments now seed a default archive pointing to the public schema.
  • Archive schema version trackingschema_version column on archive_registry for auto-sync detection.

Video Multimodal Extraction

Enhanced video processing via attachment pipeline:

  • Scene-detection keyframe extraction using ffmpeg (select='gt(scene,0.3)')
  • Frame-to-frame temporal context: sliding window of 3 previous descriptions in vision prompts
  • Audio-visual alignment: transcript segments matched to frame timestamps (+/- 5s window)
  • KeyframeStrategy enum: Interval, SceneDetection, Hybrid modes
  • VideoMultimodalAdapter wired into extraction pipeline (requires ffmpeg + vision/whisper)
  • MCP process_video guidance tool directs agents to attachment upload workflow
  • MCP documentation topic (get_documentation({ topic: "video" }))
  • get_system_info reports video extraction status (extraction.video)
  • UAT Phase 2F with 10 test cases (4 always-execute, 6 conditional on ffmpeg)
  • All video processing goes through attachment pipeline — no ad-hoc base64 API

3D Model Understanding

Multi-view rendering extraction via attachment pipeline:

  • Glb3DModelAdapter with Three.js headless multi-view rendering + vision model description
  • ExtractionStrategy::Glb3DModel variant routes all model/* MIME types
  • Lightweight Node.js renderer using Three.js + headless-gl (replaces heavyweight Blender)
  • Configurable view count (default 6, min 3, max 15) from multiple camera angles
  • Composite synthesis: individual view descriptions combined into holistic summary
  • MCP process_3d_model guidance tool directs agents to attachment upload workflow
  • MCP documentation topic (get_documentation({ topic: "3d-models" }))
  • get_system_info reports 3D model extraction status (extraction.3d_model)
  • Bundled Three.js renderer at RENDERER_URL (default: localhost:8080) + vision backend
  • UAT Phase 2G with 10 test cases (5 always-execute, 5 conditional on renderer + vision)
  • All 3D model processing goes through attachment pipeline — no ad-hoc base64 API

Audio Transcription

Ad-hoc audio transcription via Whisper-compatible backend:

  • Wires existing TranscriptionBackend trait + WhisperBackend into API server
  • POST /api/v1/audio/transcribe API endpoint (base64 audio, optional mime_type and language)
  • MCP transcribe_audio tool for agent access
  • AudioTranscribeAdapter registered in extraction pipeline for automatic attachment processing
  • Configurable via WHISPER_BASE_URL and WHISPER_MODEL env vars
  • Returns transcription text, timestamped segments, detected language, duration, model, and audio size
  • 503 Service Unavailable when transcription backend not configured
  • Health check integration via get_system_info (extraction.audio.enabled)
  • Supports WAV, MP3, OGG, FLAC, AAC, WebM formats
  • MCP documentation topic (get_documentation({ topic: "audio" }))
  • Bundled with GPU Whisper by default in Docker bundle

Vision (Image Description)

Ad-hoc image description via Ollama vision LLM:

  • VisionBackend trait + OllamaVisionBackend in matric-inference crate
  • POST /api/v1/vision/describe API endpoint (base64 image, optional mime_type and prompt)
  • MCP describe_image tool for agent access
  • Configurable via OLLAMA_VISION_MODEL env var (e.g., qwen3-vl:8b, llava)
  • Returns AI-generated description, model name, and decoded image size
  • 503 Service Unavailable when vision model not configured
  • Health check integration via get_system_info (extraction.vision.available)
  • UAT Phase 2D with 8 test cases

Content Extraction Pipeline (#87–#99, #101, #102)

  • Complete content extraction framework with pluggable adapters
  • Document Type Registry — 131 pre-configured types across 19 categories
  • Auto-detection from filename patterns, extensions, and content analysis
  • Category-specific chunking strategies (semantic, syntactic, per_section, fixed)
  • REST API and MCP tools for document type management
  • See: Document Types Guide, Extraction Design

Authentication & Security (#103, #111, #112, #114, #115, #118, #119)

  • Auth middleware — Centralized Bearer token validation with REQUIRE_AUTH toggle
  • OAuth2 scope enforcement — Centralized scope checks for all mutation endpoints
  • Configurable OAuth token lifetimesACCESS_TOKEN_TTL and REFRESH_TOKEN_TTL env vars
  • API key systemPOST /api/v1/api-keys for programmatic access
  • PKE HTTP API — Encryption tools accessible via REST (not just CLI binary)
  • See: Authentication Guide, ADR-071

Archive Isolation Pipeline (#86, #107–#110, #113)

  • Archive creation with full schema cloning
  • Archive metadata (stats, version tracking)
  • PKE keyset registry per archive
  • Archive-scoped operations

MCP Server Improvements

  • Agent-friendly tool surface (#365) — 23-tool "core" mode with discriminated-union pattern (capture_knowledge, search, record_provenance, manage_tags, manage_collection, manage_concepts). Set MCP_TOOL_MODE=full for all 187 tools.
  • File-based I/O pattern — Replaced base64 binary tools with HTTP API upload/download. MCP tools now guide agents to use POST /api/v1/attachments multipart upload.
  • Tool definition extractiontools.js extracted from index.js for maintainability
  • Automated JSON Schema validation — All 100+ MCP tool schemas validated against draft 2020-12 on startup. One broken schema no longer blocks all tools.
  • MCP OAuth auto-registration — Bundle entrypoint auto-registers OAuth client credentials on first startup. Credentials persisted at $PGDATA/.fortemi-mcp-credentials.
  • 10+ tool descriptions updated with memory scoping context and search limitation warnings
  • Session memory sync — MCP session state sync when setting default archive (Issue #316)
  • See: MCP Guide, MCP Deployment

Eventing & Streaming Infrastructure

  • Server-Sent Events (SSE) for real-time note change notifications
  • WebSocket support for bidirectional streaming
  • Webhook system for external integrations
  • See: Real-time Events

Documentation (10 files, +851/-216 lines)

  • NEW: Multi-Memory Agent Guide — 15KB purpose-built guide for AI agents with decision matrix, 5 segmentation strategies, tradeoffs table, and common mistakes. See: Agent Guide
  • ADR-068 rewritten — Full implementation status with all 91 handlers documented
  • Architecture docs updated — Routing flow, transaction patterns, SchemaContext
  • Operations guide expanded — Multi-memory monitoring, per-memory backup, troubleshooting
  • Backup guide expanded — Per-memory backup procedures and restore caveats
  • MCP tool table expanded — 8 → 12 memory management tools documented
  • CLAUDE.md updated — Multi-memory section, MAX_MEMORIES config
  • Consolidated MCP docs — Fixed public URLs, added OpenAPI CI export
  • UAT suite rewrite — Rewrote UAT suite for 23-tool core surface

Fixed

  • PG 18.2 TOAST UTF-8 bug (#418) — Workaround for PostgreSQL bug #19406 where substring()/left() fail on TOAST-compressed text with multi-byte UTF-8 characters. Applied left(convert_from(content::bytea, 'UTF8'), N) pattern across 18 instances in 5 files. Revert tracked in #419 (after PG 18.3+).

  • Backup multi-memory headers (#421) — All backup and restore handlers now properly respect the X-Fortemi-Memory header for memory-scoped operations, including knowledge-shard export and full backup endpoints.

  • Schema-qualified FTS configs (#412) — Text search configurations now use schema-qualified names in all queries, fixing FTS failures in non-default memory archives.

  • Archive AI pipeline — AI pipeline jobs (embedding, linking, title generation, concept tagging) now execute correctly for notes in non-default memory archives.

  • Orphaned job cleanup — Archive deletion now cleans up orphaned jobs and FTS configurations, preventing stale job references.

  • MCP parameter validation (#398) — Validate required params before search URLSearchParams serialization (prevents MCP crash on missing required search params)

  • Template tag merge — Template instantiation now merges tags instead of override, preserving existing tags

  • Whisper transcription bundled — Enable Whisper transcription by default with GPU in Docker bundle

  • MPEG-2/2.5 MP3 detection — Detect MPEG-2/2.5 MP3 files for audio transcription (broader MP3 format support)

  • Attachment blob refcount — Preserve shared blobs on sibling deletion (blob refcount safety)

  • Link similarity calibration — Calibrate similarity thresholds by content type for better auto-linking accuracy

  • EXIF extraction gaps — Resolve GPS, camera, and datetime extraction gaps (improved EXIF field coverage)

  • Temporal search null safety — Resolve temporal search inconsistencies with null provenance

  • Content type validation (#253) — Validate actual content type via magic bytes (security: prevent fake content type uploads)

  • Configurable upload size (#257) — Make upload size limit configurable via MATRIC_MAX_BODY_SIZE_BYTES env var

  • Search cache invalidation (#247) — Invalidate search cache on note delete/purge/restore (cache consistency)

  • Empty content support — Accept empty content in create_note and bulk_create_notes (allow content-free notes for attachment-only)

  • Job deduplication — Deduplicate against running jobs, not just pending (prevent duplicate job execution)

  • MCP numeric arguments — Coerce numeric tool arguments to numbers before API calls (MCP parameter type safety)

  • MCP non-JSON responses — Handle non-JSON responses in apiRequest (graceful error handling)

  • Binary media validation — Enforce magic byte detection for binary media types (security: binary file validation)

  • Vision Ollama URL in DockerOllamaVisionBackend now reads OLLAMA_BASE env var first (matching embedding backend), fixing 500 errors in Docker containers where only OLLAMA_BASE is set

Database Restore Pipeline (Complete Rewrite)

The restore system was rewritten for correctness and robustness:

  • Thread-safe psql pipe (#166) — tokio::task::spawn_blocking to prevent pipe deadlocks with large dumps
  • Extension-owned object exclusion — DROP script queries pg_depend with deptype = 'e' to skip PostGIS-owned objects like spatial_ref_sys
  • Comprehensive object cleanup — DROP tables, enum types, functions, text search configs, dictionaries, views, and sequences before restore
  • FTS index rebuild — REINDEX + ANALYZE after restore to rebuild search indexes (#166)
  • See: Backup Guide

UAT Wave 1: 17 Issues (#132–#151)

  • SKOS search fixes (#132, #133, #134, #149) — autocomplete_concepts and search_concepts now work with custom schemes; get_concept_full returns complete data
  • Auth scope fixes (#135, #138, #139, #140) — MCP tools for backup, archive, location search, and embedding config no longer return 403
  • Attachment upload (#137, #150, #153, #154, #155) — File storage diagnostics, volume mount validation, HTTP API upload guidance for remote agents
  • MCP response fixes (#141, #142) — add_skos_collection_member JSON parsing, update_concept null return
  • PKE address format (#143) — PEM-stored keys now correctly converted to raw 32-byte binary
  • Time search validation (#144–#148) — ISO 8601 timestamps with colons accepted; invalid coordinates rejected with 400
  • Job worker isolation (#151) — Workers only claim jobs they have handlers for

UAT Wave 2: 18 Issues (#152–#169)

  • Error message sanitization (#152, #163) — Raw SQL constraint names replaced with user-friendly messages
  • Attachment file I/O (#153, #154, #155, #157) — Complete HTTP API workflow documented for remote MCP agents
  • MCP test tool names (#156) — Attachment test references corrected
  • Default archive on fresh deploy (#158) — list_archives() no longer returns empty
  • Archive note routing (#159) — Notes now land in the active archive, not always public
  • SKOS relation cleanup (#160) — remove_related cleans up inverse relations
  • SKOS export all schemes (#161) — Export without scheme_id now exports all schemes
  • PKE remote access (#162) — PKE tools work via API, not just local filesystem
  • Job reprocessing (#164) — reprocess_note respects steps parameter
  • SKOS cascade delete (#165) — delete_concept_scheme(force=true) cascade-deletes concepts
  • Archive schema completeness (#169) — note_original table included in schema cloning

CI/CD & Build Fixes

  • Migration timestamp deduplication — Duplicate prefixes cause _sqlx_migrations_pkey violations
  • CONCURRENTLY removed from migrations — sqlx wraps migrations in transactions
  • COMMENT ON EXTENSION removed — Requires superuser; non-owner can't comment
  • Env var race conditions eliminated — Constructor injection replaces std::env::set_var in tests
  • Auto-migrate on startupsqlx::migrate!() runs on API startup from main.rs
  • Stale container cleanup — CI kills containers by port before starting new ones

Refactoring

  • Centralized constants (#60) — Magic numbers moved to defaults.rs
  • Algorithm config (#61, #62) — Runtime-overridable algorithm parameters
  • Hardcoded chunking eliminated — Chunking config now driven by document type registry

Security

  • SQL injection prevention (#215, #216, #217) — Critical SQL injection fixes across multiple endpoints
  • Public schema protection (#244) — Prevent DROP SCHEMA public CASCADE in archive delete and restore (critical: prevent accidental public schema deletion)
  • Resource limits (#218, #189) — Rate limiting, input size validation, connection pool limits
  • Input validation (#218) — Comprehensive input validation across all endpoints
  • Wildcard injection prevention (#216) — Prevent wildcard injection in pattern matching

Changed

  • Workspace version: 2026.2.72026.2.9 (290 commits)
  • PostgreSQL: 16 → 18 (#396)
  • Authentication: SCRAM-SHA-256 password authentication enabled (#397)
  • UUID generation: Native uuidv7() function for UUID generation (#397)
  • MCP tool surface: ~95 → 27 core tools (discriminated-union) / 187 full tools (#365)
  • Migration count: 57 → 59+ migration files
  • API handler count: 91 handlers, all schema-routed
  • Test infrastructure: Two UAT passes (530+ MCP test cases, 96.3% pass rate)
  • UAT test cases: 530+ MCP tests across multiple passes
  • Issues resolved: 100+ issues since v2026.2.7

Database Migrations

Migration Purpose
20260208000002_seed_default_archive.sql Seed default archive for fresh deployments
20260208100000_archive_schema_version.sql Add schema_version tracking to archive_registry
PostgreSQL 18 upgrade Major database engine upgrade
SCRAM-SHA-256 auth Enhanced password authentication security
Native uuidv7() Time-ordered UUID generation

Plus significant refactoring of 28 existing migrations (removed CONCURRENTLY, fixed timestamps, separated schema DDL from seed data).

Breaking Changes

None. Full backward compatibility maintained:

  • /api/v1/archives/* routes continue to work alongside new /api/v1/memories/*
  • Default behavior (no X-Fortemi-Memory header) routes to public schema as before
  • Existing MCP tool names preserved; new tools added with "memory" terminology

Upgrade Notes

  1. Database migrations run automatically on startup via sqlx::migrate!()
  2. PostgreSQL 18 upgrade — Review upgrade notes in ADR-096
  3. SCRAM-SHA-256 auth — Existing passwords automatically upgraded on next login
  4. Fresh deployments now seed a default archive — list_archives() returns the public schema
  5. MCP clients should update tool descriptions — memory-scoping context added to 10+ tools
  6. Backup scripts — If using custom backup scripts, consider switching to per-memory backup (GET /api/v1/backup/memory/:name) for targeted exports
  7. Docker bundle — MCP OAuth credentials now auto-registered on first startup; manual registration no longer required
  8. Whisper transcription — Now bundled by default with GPU in Docker bundle
  9. Shard import — New POST /api/v1/backup/knowledge-shard/upload multipart endpoint available; existing JSON base64 endpoint remains for backward compatibility
  10. First-boot documentation — Fresh Docker bundle deployments automatically load the fortemi-docs archive (243 notes of product documentation)

Issues Resolved

100+ issues closed in this release:

  • Epic: #170 (Multi-Memory Schema Isolation)
  • Multi-Memory: #158, #159, #169, #171–#181
  • Auth & Security: #135, #138, #139, #140, #152, #163, #215, #216, #217, #218, #244
  • MCP Server: #134, #137, #141, #142, #149, #153, #174, #316, #365, #398
  • Backup & Restore: #136, #166, #167, #168, #175, #176
  • Search: #132, #133, #144–#148, #177
  • SKOS: #160, #161, #165
  • Attachments: #150, #154, #155, #157, #247, #253, #257
  • CI/Testing: #151, #156, #319
  • PKE: #143, #162
  • Jobs: #164
  • Documentation: #181
  • Database: #396, #397
  • Extraction: #278, #386
  • Provenance: #261, #262
  • Linking: #420
  • Seed: #411
  • PG Compat: #418

2026.2.7 - 2026-02-05

Fixed

  • SKOS Collections MCP endpoints - Fixed 404 errors on all SKOS collection tools (list_skos_collections, create_skos_collection, etc.) by correcting API paths from /api/v1/skos/collections to /api/v1/concepts/collections (#36)
  • SKOS Turtle export - Fixed export_skos_turtle to use scheme_id as path parameter instead of query parameter (#36)
  • SKOS Collection create/update - Fixed orderedis_ordered field name mapping (#36)

2026.2.6 - 2026-02-05

Changed

  • Repository reset - Squashed history for clean baseline
  • License date updated - BSL change date set to February 16th, 2030

2026.2.5 - 2026-02-05

Fixed

  • Emoji search for ⭐ and arrow symbols - Added missing Unicode range U+2B00-U+2BFF (Miscellaneous Symbols and Arrows) to emoji detection, fixing search for ⭐, ⬆️, ⬇️, etc.

Documentation

  • Updated search guide with comprehensive emoji Unicode range reference

2026.2.4 - 2026-02-05

Fixed

  • MCP limit=0 parameter - Fixed JavaScript falsy check that skipped limit=0 parameter (#29)
    • Changed from if (args.limit) to if (args.limit !== undefined && args.limit !== null)
    • API now correctly returns 400 "limit must be >= 1"

Verified

  • CJK 2+ character search - Works correctly; single-char limitation is industry standard (#30)
  • Emoji search - All patterns work: single, repeated, adjacent different emojis (#31)

2026.2.3 - 2026-02-05

Fixed

  • UAT issues #29-#31 resolved
    • limit=0 now returns 400 "limit must be >= 1" instead of all notes (#29)
    • CJK single-character search now works (FTS flags enabled by default) (#30)
    • Emoji search now works (trigram fallback enabled) (#31)
  • OAuth endpoints routing - Fixed nginx returning 405 HTML instead of proxying to API
  • CI/CD pipeline - Fixed host runner PATH, duplicate docker socket mount, clippy compliance

Added

  • Nginx proxy documentation - deploy/nginx/README.md with SPA+API routing guidance

2026.2.2 - 2026-02-04

Fixed

  • UAT findings resolved (#13-#26) - All user acceptance testing issues addressed
  • MCP authorization_servers metadata - Now correctly uses ISSUER_URL for OAuth discovery

Changed

  • README enhanced with plain-language vision statement
  • Removed build status badge from README (unreliable external service)

2026.2.0 - 2026-02-02

Highlights

What Changed Why You Care
CI/CD Pipeline Stabilization All tests pass reliably - no more #[ignore] workarounds
Redis Container Integration Test container now includes Redis for full integration testing
Worker Test Infrastructure Background job tests run serially with proper isolation

Fixed

  • CI worker tests - Converted from #[sqlx::test] to #[tokio::test] to avoid CREATE INDEX CONCURRENTLY transaction conflicts
  • CI slow tests - Fixed table name (note_revised_current), unique identifiers, check_source constraint, tstzrange bounds
  • CI Test Container - Added Redis container for search cache integration testing
  • Checksum test flakiness - Fixed Base58 non-uniformity causing intermittent test failures
  • Hierarchical tag filtering (#283) - Tags now match with hierarchical prefix (e.g., project matches project/alpha)
  • limit=0 parameter handling (#284) - MCP server now correctly returns empty array when limit=0
  • Case-insensitive tag matching (#290) - Tag queries now use LOWER() for case-insensitive comparison
  • Ollama connectivity in Docker (#287, #320) - Added extra_hosts configuration for Linux Docker containers
    • host.docker.internal:host-gateway enables container-to-host Ollama communication
    • OLLAMA_BASE environment variable now properly configured

Added

  • File Attachment System (#430-#440) - Intelligent file processing with provenance tracking
    • Content-addressable storage with BLAKE3 deduplication
    • EXIF metadata extraction (GPS, camera info, timestamps)
    • Multi-layer file safety validation (magic bytes, blocklist, sanitization)
    • Support for images, documents, audio, video, 3D models, and code files
    • Automatic processing via extraction strategies (Vision, AudioTranscribe, CodeAst, etc.)
    • UUIDv7 filesystem paths for large files
  • Temporal-Spatial Memory Search (#437) - PostGIS-powered memory queries
    • Search by geographic location (radius queries)
    • Search by time range (capture date filtering)
    • Combined location + time intersection queries
    • Full provenance chain retrieval (location, device, temporal context)
  • W3C PROV Integration (#434) - Standards-based provenance tracking
    • prov:atLocation with PostGIS geography type
    • prov:wasGeneratedBy for device attribution
    • Temporal ranges with tstzrange for capture time uncertainty
  • 3D File Analysis (#438) - Support for GLB, STL, OBJ formats
    • Geometric metadata extraction (vertices, faces, bounds)
    • Thumbnail generation via trimesh
  • Structured Media Formats (#439) - SVG, MIDI, tracker module support
  • Embedding config MCP tools (#298)
    • list_embedding_configs - List all embedding configurations
    • get_default_embedding_config - Get the default embedding configuration
  • Document Type Registry - 131 pre-configured document types across 19 categories (#391-#411)
    • Automatic detection from filename, extension, and content patterns
    • Category-specific chunking strategies (semantic, syntactic, per_section, etc.)
    • REST API and MCP tools for document type management
    • Extensible with custom document types

Changed

  • Tag filtering in list_notes, search_notes, strict_filter, and embedding_sets now supports:
    • Case-insensitive matching: LOWER(tag_name) = LOWER($1)
    • Hierarchical matching: LOWER(tag_name) LIKE LOWER($1) || '/%'

2026.1.12 - 2026-02-01

Highlights

What Changed Why You Care
FTS Unicode Normalization Search now matches accented/unaccented text (café ↔ cafe)
MCP Security Hardening Error messages no longer leak implementation details
Metadata API Notes can now store custom JSON metadata
Tag Filtering in Search Search results can be filtered by tags

Added

  • metadata field exposed in create/update note API endpoints (#359)
  • tags parameter for search_notes MCP tool with strict filtering (#315)
  • validateUUID() helper for clear parameter validation errors (#348)
  • sanitizeError() helper to prevent information leakage (#346)
  • FTS test suite for text search configuration verification

Fixed

  • FTS accent/diacritic folding - "café" now matches "cafe" search (#328)
    • Added unaccent PostgreSQL extension
    • Created matric_english text search configuration
    • All FTS queries updated to use new configuration
  • Embedding set ID assignment - Embeddings now properly assigned to sets (#353)
    • store() method now sets embedding_set_id from default set
    • Migration backfills orphaned embeddings
  • MCP parameter validation - Clear error messages for missing/invalid UUIDs (#348)
  • MCP error sanitization - Internal errors no longer exposed to clients (#346)

Changed

  • All Rust FTS queries use matric_english config instead of english
  • MCP error responses now return safe, user-friendly messages

Database Migrations

  • 20260131000000_fts_unicode_normalization.sql - Unicode search support
  • 20260131000001_fix_embedding_set_id.sql - Embedding set backfill

[2026.1.11] - 2026-01-31

Fixed

  • CI race condition when main branch and tag pushes run simultaneously
    • Container names now include GITHUB_RUN_ID for uniqueness
    • Database ports dynamically assigned to avoid conflicts
    • Affects both build and test-container jobs

[2026.1.10] - 2026-01-31

Highlights

What Changed Why You Care
CI Consolidation Single builder-based CI workflow for consistent, reproducible builds
Test Infrastructure PostgreSQL test database properly integrated in CI pipeline

Changed

  • Consolidated CI to single builder-only workflow (ci-builder.yaml)
    • Removed redundant ci.yaml (bare runner)
    • All builds now use pre-built builder container for consistency
  • CI workflow renamed from "CI (Builder)" to "CI"

Fixed

  • PostgreSQL test database now properly spun up in CI for database-dependent tests
  • 16 tag_resolver tests no longer fail due to missing database connection

Removed

  • ci.yaml - redundant bare-runner workflow (superseded by builder-based CI)

[2026.1.9] - 2026-01-30

Highlights

What Changed Why You Care
License Migration Moved from MIT/Apache-2.0 to BSL 1.1 with AGPL-3.0 change license
Dependency Audit All 400+ dependencies verified BSL-compatible (no GPL conflicts)
Licensing Documentation Plain-English licensing guide for users and enterprises

Changed

  • License: Migrated from MIT/Apache-2.0 to Business Source License 1.1
    • Current: BSL 1.1 (production use requires commercial license)
    • After February 16, 2030: Converts to AGPL-3.0 (open source)
    • Personal, educational, and evaluation use remains free
    • See docs/content/licensing.md for plain-English explanation

Added

  • LICENSE - BSL 1.1 license terms with parameters
  • LICENSE.txt - AGPL-3.0 full text (change license, effective 2030)
  • NOTICE - Copyright and third-party attribution
  • docs/content/licensing.md - Comprehensive licensing FAQ and guide

Fixed

  • Missing license metadata in matric-search crate Cargo.toml

Security

  • Completed dependency license audit: 400+ packages verified
  • No GPL-only dependencies found (all permissive: MIT, Apache, BSD, ISC)
  • All dependencies compatible with BSL 1.1 during proprietary period

[2026.1.8] - 2026-01-30

Highlights

What Changed Why You Care
CI/CD Pipeline Hardened Both ci.yaml and ci-builder.yaml now pass reliably with proper isolation
GPU Tests Fixed Properly NVML driver mismatch resolved - no tests skipped or bypassed
Build Container Docs Clear rationale for why we use containerized builds at Integro Labs

Fixed

  • Issue #207: NVML driver/library version mismatch causing GPU integration test failures
    • Root cause: Kernel module out of sync with userspace libraries after update
    • Resolution: System reboot to load updated NVIDIA kernel module
  • ci-builder PostgreSQL connectivity issues in Docker-based runners
    • Changed from services: directive to manual container management
    • Used isolated port 15432 to avoid conflicts with host PostgreSQL
  • Workflow execution order for builder image updates
    • Added paths-ignore to prevent CI race conditions with builder updates
    • Added trigger-ci job in build-builder.yaml to dispatch CI after builder publishes

Added

  • Build container architecture documentation in build/RUNNER_SETUP.md
    • Runner label strategy (matric-builder, titan, gpu)
    • Rationale: Isolation, reproducibility, no version conflicts on shared dev servers
  • Comprehensive environment variables in .env.example and Dockerfile

Changed

  • ci-builder.yaml now uses port 15432 for PostgreSQL (isolated from host)
  • build-builder.yaml triggers CI workflows after successful builder image push

[2026.1.7] - 2026-01-30

Highlights

What Changed Why You Care
All-in-one Docker bundle Single container with PostgreSQL + API + MCP server for easy deployment
matric-pke bundled PKE encryption binary included in container for MCP keyset operations
Comprehensive env var docs All environment variables documented with comments in Dockerfile

Added

  • All-in-one Docker bundle (Dockerfile.bundle, docker-compose.bundle.yml)
    • Embedded PostgreSQL 16 with pgvector extension
    • matric-api server on port 3000
    • MCP server on port 3001
    • matric-pke binary at /usr/local/bin/matric-pke
  • Comprehensive environment variable documentation in Dockerfile
    • PostgreSQL, API, Ollama, OpenAI, and MCP configuration sections
    • Rate limiting controls (disabled by default in bundle)
    • OAuth/MCP client credential configuration

Fixed

  • MCP OAuth metadata now uses external ISSUER_URL instead of internal address
  • MCP protected resource URL configurable via MCP_BASE_URL

[2026.1.6] - 2026-01-30

Highlights

What Changed Why You Care
update_note returns entity API now returns full note object after update (REST best practice)
Backup auto-provisioning Backup directory created automatically on first use
PKE keyset management 7 new MCP tools for managing encryption identities

Fixed

  • Issue #203: update_note now returns full NoteFull object instead of HTTP 204
  • Issue #204: backup_status auto-creates backup directory with graceful permission handling
  • Issue #205: Backup tools now work out of the box (resolved by #204)

Added

  • PKE keyset management MCP tools:
    • pke_list_keysets - List all keysets in ~/.matric/keys/
    • pke_create_keyset - Create new named keyset with passphrase
    • pke_get_active_keyset - Get currently active keyset info
    • pke_set_active_keyset - Set active keyset by name
    • pke_export_keyset - Export keyset to directory for backup/transfer
    • pke_import_keyset - Import keyset from files or export directory
    • pke_delete_keyset - Delete a keyset permanently
  • Backup status now returns "cannot_create_directory: {error}" on permission failure

Changed

  • update_note MCP handler returns { success: true, note } instead of just { success: true }
  • Backup directory defaults to /var/backups/matric-memory (auto-created)

[2026.1.5] - 2026-01-29

Highlights

What Changed Why You Care
SQL Parameter Fix update_note with single field (archived/starred only) now works correctly
String Tag Search search_notes_strict now accepts simple string tags, not just SKOS URIs
MCP Content-Type Handling diff_note_versions returns plain text correctly
PKE Deployment matric-pke encryption binary now deployed to production

Fixed

  • Issue #198: SQL parameter mismatch in update_note when updating only archived or starred
  • Issue #199: search_notes_strict with required_tags now supports simple string tags via fallback
  • Issue #201: MCP server now handles text/plain responses (e.g., version diffs) correctly
  • Issue #202: matric-pke binary built and deployed to /usr/local/bin

Added

  • StrictTagFilter now supports required_string_tags, any_string_tags, excluded_string_tags
  • simple_tag_exists() method for simple tag lookup fallback
  • Content-Type aware response parsing in MCP server

Changed

  • Dynamic SQL parameter indexing in note update operations
  • Tag resolver tries SKOS concept first, falls back to simple tag if not found

[2026.1.4] - 2026-01-29

Highlights

What Changed Why You Care
Semantic Search Isolation Fix Critical fix: strict_filter now applies to vector search, preventing data leakage
SKOS ENUM Fixes All SKOS APIs now correctly handle PostgreSQL ENUM types
MCP strict_filter Fix MCP server correctly passes strict_filter parameter

Fixed

  • Critical: Semantic search data isolation - strict_filter was only applied to FTS, not vector search
  • SKOS ENUM type casting (Issue #197) - All SELECT/INSERT queries now properly cast ENUMs
  • MCP server strict_filter parameter - Changed from "filters" to "strict_filter"
  • API strict_filter JSON parsing - Query string now correctly deserializes nested JSON

Added

  • find_similar_with_strict_filter() for isolated semantic search
  • test-skos-regression.sh - 17 regression tests for SKOS ENUM fixes
  • test-strict-search.sh - 7 data isolation tests for strict_filter

2026.1.0 - 2026-01-24 (previous)

Added

Research-Backed Modules (#162-165, #167-170, #172, #174, #176-177)

  • W3C PROV provenance tracking (#162) - Activity/entity/relation models with full CRUD and chain queries
  • Self-Refine iterative revision (#163) - Multi-pass AI revision pipeline with quality scoring
  • ReAct agent pattern (#164) - Thought/action/observation traces for structured reasoning
  • Reflexion self-improvement (#165) - Episodic memory for learning from past revisions
  • E5 embedding model support (#167) - Asymmetric prefix support, ReEmbedAll job type
  • Miller's Law context limits (#168) - 7±2 chunk limits for cognitive load management
  • BM25F field-weighted scoring (#169) - Weighted scoring across title/body/tags fields
  • FAIR metadata export (#170) - Dublin Core (ISO 15836), JSON-LD, compliance scoring
  • Few-shot prompt builder (#172) - Curated in-context learning examples
  • Semantic link classification (#174) - Typed links: supports/contradicts/extends
  • Adaptive RRF k-parameter (#176) - Query-dependent k tuning (default k=20)
  • Dynamic HNSW ef_search (#177) - Recall/latency trade-off tuning
  • SKOS Collections (#175) - W3C SKOS labeled/ordered concept groups with full CRUD
  • RRF parameter tuning (#187) - K=60→K=20, adaptive weights, Relative Score Fusion (RSF)

Infrastructure

  • UUIDv7 identifiers (#178) - Time-ordered UUIDs with timestamp extraction
  • Unified strict filter system (#179-184) - Multi-dimensional pre-search filtering (tags, temporal, collections, security)
  • Docker builder pattern for CI/CD - Multi-stage builds with isolated container testing
  • Container API test suite - 64 assertions covering all major API endpoints
  • Pre-commit hooks - Automated formatting and lint checks

Documentation

  • Comprehensive operators guide (docs/guides/operators-guide.md)
  • Research foundation analysis with paper-level citations
  • Architecture Decision Records (ADR) and test strategy documentation
  • Professionalized multi-audience documentation structure

Fixed

  • Note versioning: populate note_revised_current on creation, fix provenance column name
  • Note revision INSERT statements: correct column names
  • Note original table: add missing id column
  • Database constraints: use UNIQUE instead of duplicate PRIMARY KEY
  • Database indexes: remove non-IMMUTABLE NOW() from index predicates
  • CI pipeline: restructured build-before-test, GPU runner for integration tests

Changed

  • RRF default k parameter from 60 to 20 (better discrimination for small result sets)
  • Test count: 933 → 1,056 tests passing (6 ignored)

2026.1.0 - 2026-01-24

Highlights

What Changed Why You Care
Strict Tag Filtering Guaranteed data segregation by SKOS tags/schemes - enables multi-tenancy
W3C SKOS Tagging Hierarchical semantic tagging with broader/narrower/related relations
Hybrid Search FTS + semantic + RRF fusion for best-of-both-worlds search
MCP Server 65+ tools for AI agent integration (Claude, etc.)
PKE Encryption X25519 public-key encryption for secure note sharing
OpenAI Backend Support for OpenAI-compatible APIs (OpenAI, vLLM, OpenRouter)

Added

Core Features

  • Hybrid search engine with Reciprocal Rank Fusion (RRF)
    • Full-text search via PostgreSQL tsvector/GIN
    • Semantic search via pgvector cosine similarity
    • Configurable weights and modes (hybrid/fts/semantic)
  • AI enhancement pipeline for notes
    • Automatic revision with context from related notes
    • Embedding generation for semantic search
    • Title generation from content
    • Bidirectional semantic link creation (>70% similarity)
  • W3C SKOS-compliant tagging system
    • Hierarchical concepts with broader/narrower/related relations
    • Concept schemes for vocabulary organization
    • Faceted classification (PMEST facets)
    • Tag governance with candidate/controlled/deprecated status
  • Strict tag filtering (Epic #145)
    • Pre-search WHERE clause filtering for guaranteed isolation
    • Filter types: required_tags (AND), any_tags (OR), excluded_tags (NOT)
    • Scheme isolation: required_schemes, excluded_schemes
    • Foundation for multi-tenancy without separate databases
  • Collections - Hierarchical folder organization for notes
  • Templates - Reusable note structures with {{variable}} substitution
  • Note versioning - Dual-track versioning preserving original and revised content

Infrastructure

  • MCP Server with 65+ tools for AI agent integration
    • Note management (CRUD, search, export)
    • Collection and template management
    • SKOS concept management
    • Backup and knowledge shard operations
    • PKE encryption tools
  • PKE encryption (matric-crypto crate)
    • X25519 ECDH key exchange
    • AES-256-GCM symmetric encryption
    • Multi-recipient envelope encryption
    • Wallet-style addresses with checksums
    • Argon2id-protected private key storage
  • Pluggable inference backends
    • Ollama (default) - local inference
    • OpenAI-compatible APIs (feature-gated)
    • Model capability registry for recommendations
  • Background job processing
    • Async NLP pipelines (embedding, revision, linking, title generation)
    • Priority-based job queue
    • Status tracking and monitoring
  • CI/CD pipeline via GitHub Actions
    • Format checking, linting, testing
    • Integration tests with GPU + Ollama
    • Docker image builds

API & Documentation

  • RESTful API with OpenAPI 3.1 specification
  • Swagger UI at /docs
  • Comprehensive documentation
    • Architecture guide
    • Integration guide
    • API reference
    • MCP server documentation
    • Encryption guide

Database

  • PostgreSQL 14+ with pgvector extension
  • HNSW indexes for vector similarity search
  • GIN indexes for full-text search
  • Optimized indexes for strict tag filtering

Security

  • Input validation on all endpoints
  • CORS support for browser access
  • TLS termination at reverse proxy
  • No stored credentials in codebase

Version Format

This project uses CalVer (Calendar Versioning):

  • Format: YYYY.M.PATCH (e.g., 2026.1.0, 2026.12.3)
  • Year: 4 digits
  • Month: 1-2 digits, no leading zeros
  • Patch: Resets each month, starts at 0

Tags use v prefix: v2026.1.0