All notable changes to Fortémi are documented here.
The format follows Keep a Changelog,
and this project uses CalVer versioning: YYYY.M.PATCH.
Corrective publication for 2026.7.18. Knowledge Shard formats, exact
2.0.0/full-v1 evidence, supported platform cells, and consumer revisions are
unchanged.
- 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-builderjobs 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.
Corrective publication for 2026.7.17. Knowledge Shard formats, exact
2.0.0/full-v1 evidence, supported platform cells, and consumer revisions are
unchanged.
- 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.
Corrective publication for 2026.7.16. Knowledge Shard formats, exact
2.0.0/full-v1 evidence, supported platform cells, and consumer revisions are
unchanged.
- Give each Linux arm64 sidecar build a run-scoped Colima profile on
/Volumes/buildand 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.
Corrective publication for 2026.7.15. Knowledge Shard formats, exact
2.0.0/full-v1 evidence, supported platform cells, and consumer revisions are
unchanged.
- 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.
Corrective publication for 2026.7.14. Knowledge Shard formats, exact
2.0.0/full-v1 evidence, supported platform cells, and consumer revisions are
unchanged.
- Pin the Linux arm64 and macOS sidecar coordinator jobs to the
titanhost 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.
Corrective publication for 2026.7.13. Knowledge Shard formats, exact
2.0.0/full-v1 evidence, supported platform cells, and consumer revisions are
unchanged.
- 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.
- Add exact Knowledge Shard
2.0.0readers for the three registered profiles and receipt-bound opt-in export for exact2.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-v1remains the default export;2.0.0/core-v1and2.0.0/record-v1remain unadvertised. - Add the Fortemi-owned supported-platform aggregate, binding the same server
authority,
@fortemi/coreconsumer, and HotM application-consumer contract surface on Linux x86_64, Linux arm64, and macOS arm64 onmutsu. 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.
- Reconcile legacy Knowledge Shard guidance with the schema
1.2.0named profiles and exact2.0.0opt-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.
- 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.
- 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.
- Updated the MCP server's transitive dependency resolution to remove the
high-severity
fast-urihost-confusion advisory and current Hono and body-parser advisories covered by non-breaking dependency updates.
- 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-v1profile (#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-v1remains reserved. - OpenAPI response coverage (#1060): every documented operation now
publishes the global rate-limit middleware's schema-bearing RFC 9457
429response, and contract validation rejects missing statuses, media types, orProblemDetailsschema 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-v1REST exports can now opt into verified, digest-deduplicatedblobs/<digest>entries withinclude_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 orfull-v1conformance.
- Browser CORS compatibility (#1062): configured HotM origins may send the
standard
Cache-Controlrequest 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-v1imports 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 thecore-v1archive. - 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.
- 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
wipeswaps 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.
- 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.
- Legacy bundle authentication (#1048): existing data directories created
with an older password default now align the application role with the
configured
POSTGRES_PASSWORDbefore TCP clients start. - Pre-migration backup reliability (#1049, #1050): bundle upgrades use
compressed
pg_dumpoutput, 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_DEFAULTnow 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_ISSUERis 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.
- OpenBao-backed CI credentials: Gitea build, image publication, documentation deployment, and release workflows now fetch scoped runtime secrets through the repository's OpenBao integration.
- 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 matchingblake3:<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.
- February-to-current upgrade path: exact
v2026.2.0migration 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 FROMtrigger 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.
- Added a February-to-current upgrade runbook, audit report, and fixture evidence
covering exact
v2026.2.0restore, 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.
- 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.
- Security headers on all HTTP responses.
- The incoming-webhook receive endpoint authenticates via HMAC signature and no longer requires a bearer token.
- 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
anyhow1.0.102 → 1.0.103 (RUSTSEC-2026-0190).
- 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 modelqwen3.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.
| 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. |
-
Streaming chat endpoint —
POST /api/v1/chat/stream(#812). Same request contract asPOST /api/v1/chat(input, optionalmodel, optionalcontext.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 alongsidesseandrtpwith process-lifetime counters:chat_stream_started_total,chat_stream_completed_total,chat_stream_errored_total,chat_stream_client_disconnect_total,chat_stream_tokens_total, andchat_stream_dropped_tokens_total. -
Multi-turn streaming backend —
OllamaBackend::chat_multi_turn_stream. Streams/api/chatwithstream: true, sharing NDJSON line parsing with the existing single-turn streamer via a commonollama_chat_ndjson_streamhelper. -
Last-Event-IDresumption for/api/v1/chat/stream(#815). Redis-backed per-stream cursor (60s TTL) lets a reconnecting client resume from the last delivered event id.
- Receiver registration + receive + delete.
POST /api/v1/webhooks/incomingregisters a receiver (HMAC secret + schema ref);POST /api/v1/webhooks/incoming/{slug}receives;DELETE /api/v1/webhooks/incoming/{slug}removes one (204 / 404, idempotentdelete_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_doccolumn stores per-receiver JSON Schema; the built-in Twilio schemas were converted to embedded JSON Schema so every payload validates through onejsonschema-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-Keydedupe via Redis, 24h TTL (#822). An opt-inIdempotency-Keyheader maps toidem:{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.receivedrow to the sharedevent_outbox(entity_type = "incoming_webhook",entity_id = <receiver id>, payload carriesslug,provider,schema_ref, parsedpayload, andside_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.
- NDJSON bulk ingest —
POST /api/v1/ingest/stream(#825).application/x-ndjsonrequest body parsed line-by-line with a per-lineinsert_tx; SSEack/doneresponse contract. - Per-line validation + progress frames (#826). DB-free schema validation per line;
progress {processed:N}everyFORTEMI_INGEST_PROGRESS_INTERVAL(default 100); a malformed line errors only that line. - Backpressure — bounded buffer + 429 (#827). Configurable
FORTEMI_INGEST_STREAM_BUFFERchannel with escalating thresholds:warningat 80%,429 {retry_after_ms, INGEST_BACKPRESSURE}at 95%, TCP block-sender backpressure at 100%;ingest_stream_buffer_pressuregauge + 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 Gonebeyond TTL. - Per-stream bearer token auth + rate limit (#829).
POST /api/v1/ingest/tokensmint +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 whenINGEST_REQUIRE_TOKEN=true(default).ingest_stream_rate_limited_totalon/health/streaming. - Outbox integration (#830). Each ingested note appends a
note.createdoutbox 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_uploadtable (offset +expires_at), and TTL cleanup — multi-GB uploads resume after interruption.
- Pluggable
InboundEventSourceframework (#833). Connector trait + registry; a lifecycle supervisor that validates each event, writes it to the sharedevent_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; andPOST/GET/DELETE /api/v1/inbound-sources. All connectors are opt-in viaINBOUND_EXTERNAL_SOURCES_ENABLED=false(default — standby cost gate). - Redis Stream connector (#834). Consumer-group
XREADGROUPwithXACKonly 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-streamconsumer withLast-Event-IDresumption 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
kafkaCargo feature (off by default, so default/edge builds never compile librdkafka — built self-contained via vendored OpenSSL) plus runtimeINBOUND_KAFKA_ENABLED=false.
- CI: GHCR/registry publish jobs no longer mask failed pushes (#882).
push_with_retrynowexit 1on final failure (was an ignoredreturn 1) and all four publish blocks (publish-dev,publish-release,publish-github-dev,publish-github) run underset -euo pipefail, so a faileddocker login/build/pushfails the job instead of reporting a false green. This restored GHCR publishing (ghcr.io/fortemi/fortemi:mainverified republished).workflow_dispatchwas also added to the CI workflow for manual re-runs.
- Backpressure / dropped tokens. Each chat
deltasend has a window controlled byCHAT_STREAM_SEND_TIMEOUT_SECS(default30). If a client stops draining the bounded SSE buffer (capacity 256 events), the stalled token is shed and counted inchat_stream_dropped_tokens_totalrather than holding the GPU permit indefinitely. A mid-stream client disconnect is likewise counted as dropped and recorded inchat_stream_client_disconnect_total. Under normal client pacing, no tokens are dropped. - Event shape consistency.
/api/v1/chat/streamuses the samedelta/done/errorSSE event vocabulary as the existingPOST /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), browserEventSourcecannot be used directly; consume them with afetch()+ReadableStreamreader or a POST-capable SSE client.
- The HotM client-side consumer of
/api/v1/chat/streamis 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.
cargo fmt --all -- --checkcargo clippy --all-targets --all-features -- -D warningscargo 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.
- Rust advisory updates - Updated
opensslfrom0.10.79to0.10.80,rand0.8.5to0.8.6, andrand0.9.2to0.9.3inCargo.lock. - npm advisory update - Pinned the MCP server dependency graph to
qs6.15.2viamcp-server/package.jsonoverrides and lockfile update. - Advisory allowlist cleanup - Removed the temporary
RUSTSEC-2026-0097ignores fromcargo auditandcargo denyconfiguration now that the patchedrandlines 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.
- 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.
cargo auditcargo deny check advisoriesnpm ls qsnpm 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.
| 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. |
- Incoming webhook receivers —
POST /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_sessionsmetadata, call detail lookup atGET /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 foundation —
event_outboxtable 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, andendedevents 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 documentation —
docs/deployment/realtime-providers.mdnow covers Twilio + Deepgram setup, consent/disclosure, troubleshooting, and contract completeness for local, single-tenant, multi-tenant, regulated, and future provider deployments.
- Authentication defaults are fail-closed —
REQUIRE_AUTHnow defaults totrue. Anonymous mode requires bothREQUIRE_AUTH=falseandI_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.
- 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.
- 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=truewhen settingREQUIRE_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-HostandX-Forwarded-Protofor validation to succeed.
- 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.completedcallbacks 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.
For consumers landing on this changelog and wanting the short version of what shipped under the "workstation" theme:
- 2026.5.7 —
./workstationwrapper,docker-compose.workstation.ymlunified 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.exampletemplate +./workstation configure-llmwizard covering ollama, vllm, openai, openrouter, llamacpp.extra_hosts: host.docker.internal:host-gatewaywired 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.shreference: wizard now prompts for the served-model-name (not the HF path), defaults toqwen3.5:9bso 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.
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.
- 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 afterup. - 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-llminvocation) 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 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.
- 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 OpenAImodelfield; 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. Wasmeta-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.
.env.workstation.examplevLLM 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 theqwen36_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.
- 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.exampleare touched. - 2026.5.8 users on vLLM: if you already configured
.env.workstationand chat works, no action needed. If chat is 404-ing, yourOPENAI_GEN_MODELis 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.
.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.workstationand 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.workstationwith 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.workstationif 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 beforeup. - 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.
docker-compose.workstation.ymlmatric-api service — Now loads.env.workstationviaenv_file:withrequired: 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 inlineenvironment:block. Also addsextra_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.
- No-op for existing users on Ollama.
.env.workstationis 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. Nodocker-compose.ymledits, 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.
./workstationwrapper script (#708) — Named subcommands for the full dev-box workflow:up,down,status,doctor,models pull,open,logs,shell,psql,reset,help. Theupcommand waits for healthy state and prints the URL;doctorruns 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. hotmprofile (./workstation up --no-ui) — adds agent-proxy. Useful for API-only integrations.uiprofile (./workstation up, the default) — full stack including HotM UI athttp://localhost:4180.
- default (
- QUICKSTART.md — Five-step walkthrough for users new to Docker. Covers cloning both repos as siblings (or
--backend-onlyif 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
sudoand 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.
agent-proxyis now profile-gated — Was always-on in earlier workstation drafts; now only starts under--profile hotmor--profile ui. Users who don't have the HotM sibling repo can run./workstation up --backend-onlyand get a working API without ever pulling HotM.- Workstation host ports remapped — Postgres on
5434(was5432) and agent-proxy on3011(was3001) to avoid collisions with native postgres and the sysops dashboard commonly running on3001. matric-api stays on3000; UI stays on4180; ollama stays on11434.
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.
defer_inferenceflag onPOST /api/v1/backup/import(#677) — Whentrue, 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 viaPOST /api/v1/notes/reprocess. Defaultfalsepreserves prior behavior.titlefield onCreateNoteRequestandPOST /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 underlyingINSERT INTO note.SEED_WITH_INFERENCEenv var onseed-support-archive.sh(#677) — Operators who want immediate inference at seed time setSEED_WITH_INFERENCE=true. Defaultfalse— the seed now passesskip_embedding_regen=truevia env-driven toggle rather than the previously hard-coded flag.
- Support archive notes had no titles (#675) —
scripts/rebuild-docs-shard.shnow 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 atitlefield that the API persists on insert. - Legacy
/backup/importenqueued the full NLP pipeline unconditionally (#677) — In the prior release a manual/backup/importof the support archive could produce ~965 background jobs for 193 notes and pin Ollama for hours on edge hardware. The newer/knowledge-shard/uploadendpoint hadskip_embedding_regen;/backup/importnow has the equivalentdefer_inferencegate.
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.
- 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 runpath (which never auto-seeded). Two opt-in routes:- Auto-seed on first boot: set
LOAD_SUPPORT_MEMORY=truein.envbeforedocker 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 persistentpgdatavolume tracks state). - Legacy
DISABLE_SUPPORT_MEMORY=truestill wins as a force-skip — kept for back-compat with bundles that pre-date the flip. Used bydocker-compose.minimal.ymlto guarantee skip regardless of upstream config.
- Auto-seed on first boot: set
docker/seed-support-archive.shis now safe to invoke manually at any time inside a running container. TheMANUAL_INVOCATIONflag (defaulttrue) distinguishes operator-invoked from entrypoint-invoked runs; the entrypoint sets it tofalseso auto-seed requires explicitLOAD_SUPPORT_MEMORY=true.- README "Quick Start" updated — no longer claims the bundle auto-seeds the support archive; points operators at the new dedicated section.
- README "Support Archive (fortemi-docs)" section — what the archive is, both opt-in paths, querying via the
X-Fortemi-Memory: fortemi-docsheader, the additionalPOST /api/v1/notes/reprocessopt-in for semantic search, and the refresh-on-upgrade procedure (drop archive + remove flag file + re-seed). .env.exampleSupport Memory Archive block rewritten to document both opt-in paths with copy-paste recipes plus a quick search example.
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.
Create Gitea ReleaseandCreate GitHub Releasetag-gating (#669) — Belt-and-suspendersstartsWith(github.ref, 'refs/tags/v')clause on both job conditions. Gitea Actions'needs.X.result == 'success'evaluator doesn't propagateskippedupstream the way GitHub Actions does, leading to release-creation jobs firing on push-to-main withtag_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 theCreate GitHub Releasecurl with--connect-timeout 10 --max-time 60so future hangs fail fast.- Shard-rebuild host-port collision (#671) —
scripts/ci/rebuild-shard-in-ci.shnow picks a unique host port (30000 + ($$ % 5000)) instead of fixed3000. The parallelpublish-release(Gitea) andpublish-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.
- Provider profile catalog (#658) —
crates/matric-inference/src/provider_profiles.rsships a&'static [ProviderProfile]describing the four v1 providers (Ollama, OpenAI, OpenRouter, llama.cpp). Each entry carries the wire protocol family (BackendKind::OllamaorBackendKind::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 overprovider_profiles::iter(). Response gains asupports_embeddingsfield so BYOK UIs can render the OpenRouter-style "chat only" case correctly. - Profile-aware
/api/v1/inference/test-connection(#659) — Hints likeopenrouterorllamacpproute to the right wire-protocol probe (BackendKind::OllamavsOpenAICompatible) instead of falling through to URL auto-detection. - OpenRouter native runtime config (#660) —
POST /api/v1/inference/configaccepts anopenrouterblock alongsideollama/openai/llamacpp.HTTP-Referer/X-Titleheaders default tohttps://fortemi.io/Fortemi; overridable per-deployment viaOPENROUTER_HTTP_REFERER/OPENROUTER_APP_NAMEenv vars or the runtimehttp_referer/app_namefields. - Independent embedding/generation routing (#661) —
MATRIC_EMBEDDING_PROVIDERenv var andembedding_backendfield onPOST /api/v1/inference/configroute 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=truevalidates the merged config and returns the would-be effective state without persisting or hot-swapping.?atomic=trueprobes every backend the request touches before committing; on any probe failure, abort with 503 + structuredfailures: [...]array. Avoids the brief error window where a half-applied config serves bad creds.
InferenceConfigChangedSSE event on hot-swap (#657, #663) — New variant onServerEventemitted fromPOSTandDELETE/api/v1/inference/config. Carriesdefault_backend,embedding_backend, and achanged_fieldsarray 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.DELETEevents use the sentinelchanged_fields: ["__reset__"].- Inference config audit log (#656, #664) — New
inference_config_audittable records every operator-driven mutation: actor, timestamp, action (set/reset/set_archive/reset_archive), redacted before/after JSON blobs, source IP. NewGET /api/v1/inference/config/audit?limit=50&changed_by=&action=endpoint returns recent entries with filter support. Best-effort writer — DB failure logs atwarnbut never blocks the live config change. - Per-archive inference provider override (#655, #665) — Storage + API surface for multi-tenant routing: new
archive_inference_overridetable keyed byschema_name.GET/POST/DELETE/api/v1/inference/confighonorX-Fortemi-Memory; archive overrides shallow-merge on top of the global config (precedence:archive_override > db_override > env > default). Audit log distinguishes archive operations viaset_archive/reset_archiveactions. Live runtime routing (per-archiveProviderRegistrycache + request-time resolver) is filed as #666 — substantial scope, follow-up.
- 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=truecurl 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 legacyMATRIC_INFERENCE_DEFAULT=openaiescape 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_backendset/clear,dry_run, andatomic. .env.example—MATRIC_INFERENCE_DEFAULTlists all 4 valid ids; newMATRIC_EMBEDDING_PROVIDERblock;OPENROUTER_GEN_MODEL/OPENROUTER_APP_NAME(renamed from_X_TITLEfor runtime-field consistency); new llama.cpp section.
matric-coreevent variant count assertions (#667) — Two test assertions hard-coded47for the variant count; #663 added a 48th. Failing CI runs from the prior release surfaced this. Fixed bothevents.rs::test_all_variants_metadata_is_completeandasyncapi.rs::build_spec_produces_valid_structureto expect 48.- Auto-shard-rebuild rate-limit (#668) —
scripts/ci/rebuild-shard-in-ci.shnow passesRATE_LIMIT_ENABLED=falseto the transient API container. The rebuild fires ~200POST /api/v1/notescalls in ~4 s; the bundle's default rate limiter (100 req / 60 s) was 429-ing the second half. Companion:scripts/rebuild-docs-shard.shnow aborts withexit 1if 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 ReleaseandCreate GitHub Releasejobs inci-builder.yamlhadneeds: publish-release|github+if: needs.X.result == 'success'; on Gitea Actions this didn't propagateskippedcorrectly andCreate GitHub Releasefired on push-to-main, attempting a release withtag_name: "main". Belt-and-suspenders fix adds explicitstartsWith(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.
- CI-driven
fortemi-docs.shardregeneration (#652, PR #653) —scripts/ci/rebuild-shard-in-ci.shstands up a transient Postgres + freshly-built API stack on an isolated Docker network, runs the existingscripts/rebuild-docs-shard.shagainst it, sanity-checks the output (≥100 KB), and tears down viatrap cleanup EXIT. Wired into all three bundle-publish jobs inci-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.mddocuments both the automatic CI path and the manual local rebuild for ad-hoc testing.
- First-boot support-archive import is FTS-only by default (PR #653) —
docker/seed-support-archive.shnow passesskip_embedding_regen=trueonPOST /api/v1/backup/knowledge-shard/upload. Notes are imported and the Postgrestsvectortriggers 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:
Add
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"}'
"linking"to thestepsarray for auto-link generation, or droprevision_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.
docker-compose.llamacpp.yml— bundled llama.cpp inference sidecar (#646, PR #649) —ghcr.io/ggml-org/llama.cpp:serverexposed on:8080/v1(OpenAI-compatible protocol). Brought up alongside the bundle viadocker compose -f docker-compose.bundle.yml -f docker-compose.llamacpp.yml up -d. Tunable throughLLAMACPP_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 fromqwen3.5:9b(~8 GB) toqwen2.5:3b(~2 GB), cappingJOB_MAX_CONCURRENT=1+GPU_MAX_CONCURRENT=1, and trimmingMAX_MEMORIES=2. Brought up viadocker compose -f docker-compose.bundle.yml -f docker-compose.minimal.yml up -d. Trade-off:qwen2.5:3bchat 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
.envrecipes for routing inference to llama.cpp, OpenAI proper, or OpenRouter. Explicit instructions for disabling Ollama entirely (setMATRIC_INFERENCE_DEFAULT=openai; the Ollama backend is not constructed when it isn't the default, sohost.docker.internal:11434is 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=truecallout, minimal-overlay invocation, and theqwen2.5:3bchat-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 (liveDATABASE_URLorcargo 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.
- #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 runtimesqlx::query("…")API).cargo check --workspacesucceeds on a fresh clone with noDATABASE_URL, no.sqlx/, and noSQLX_OFFLINE.cargo sqlx prepare --workspaceagainst a clean migrated Postgres reports "no queries found". The error class the issue describes does not reproduce onmain. Documentation from PR #650 stays merged as preventative coverage; awaiting the verbatim error text from the reporter to identify the actual source. Seecrates/matric-jobs/src/pause.rs:173andcrates/matric-db/src/schema_context.rs:108for the runtime-only pattern this codebase uses.
Cargo.lockrefreshed for the2026.5.1workspace version bump that landed in the prior release commit.
2026.5.1 - 2026-05-09
- Sidecar release artifact:
matric-api-x86_64-apple-darwin(#644) —publish-sidecar.ymlnow cross-compilesmatric-apifor bothaarch64-apple-darwinandx86_64-apple-darwinon the mutsu (M4 Mac mini) build host and publishes both binaries tosidecar-latestand versioned releases. Thebuild-macosjob loops over targets in one SSH session, pre-installs both rust-std targets idempotently viarustup target add, and uploads two artifacts. Bothpublish-sidecar-latestandpublish-versionedinclude 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 theroctinam/dbbuilderpublisher.docsite-build.ymlvalidates builds on PRs and pushes tomainwhendocs/**changes (strictLinks: truecatches broken links at PR review).docsite-deploy.ymlbuilds and rsyncs to the docs server onv*tag push (strictLinks: falseso a broken link doesn't block a release). Uses the default dbbuilder template with the existing Fortemi-brandeddocs/config.json. Required secrets:GT_ACCESS_TOKEN,DEPLOY_SSH_KEY,DEPLOY_HOST,DEPLOY_PORT,DEPLOY_USER,DEPLOY_PATH.
- Clippy
unnecessary_unwrapinunified_filter.rs— Replacedis_some()+unwrap()pattern withif letdestructuring on(security.include_shared, security.shared_with_user). Pre-existing onmain; fixed in this release so the workspace passescargo clippy --workspace --all-targets -- -D warnings.
2026.5.0 - 2026-05-03
- 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_blobrows outlived their on-disk files. The atomic write path now performs a best-effort parent-directory fsync after rename and uses a.bin.tmpsuffix that the startup sweeper can discriminate.FilesystemBackend::sweep_temp_files()runs at server boot and removes stale.bin.tmporphans (default threshold 5 minutes) from prior crashed writes. Missing-blob reads now return a structured404 {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.buildernow installsdocker-ce-cli+docker-buildx-pluginfrom Docker's official apt repo instead of Debian bookworm'sdocker.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+. Unblockstest.ymlandci-builder.yamlafter the runner host daemon was upgraded to Docker 29.x (API 1.52).
- 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.
- 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. Supportsollama,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 withserver_configured+requires_user_keyflags so BYOK UIs know which keys to prompt for.ProviderRegistry::resolve_generation_inline()— factory that builds a freshBox<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/streamnow emits one SSEdeltaevent per token for Ollama backends.GenerationBackendtrait gainsstream_generate()andstream_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.availableno longer latches false permanently when a provider is unreachable at startup. A background probe re-checks everyINFERENCE_PROBE_INTERVAL_SECS(default 30s) and updatesAppState.inference_available. Chat handler returns 503 +retry_afterwhen the provider is currently unreachable. EmitsInferenceAvailabilityChangedSSE event on transitions so clients can clear/raise offline banners without polling/health. - Caller-defined extraction pipeline — Optional
pipelinefield onCreateNoteRequestto 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 — omittingpipelineruns the full default pipeline. - Configurable archive extraction limits —
ARCHIVE_MAX_EXTRACT_BYTES(default 1 GB, was 100 MB) andARCHIVE_MAX_SINGLE_FILE_BYTES(default 50 MB, was 10 MB) env vars replace hardcoded constants.MAX_FILEScap removed entirely. - Multi-provider installer manifest —
setup.manifest.yamlextended 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.mdmaps every feature to its minimum hardware requirements, GPU VRAM tiers, and optional dependencies.
- Revision mode "none" creates fake history (#625) — Notes created or updated with
revision_mode=noneno longer write misleadingnote_revisionrecords with "Original preserved (no AI revision)" rationale. Newsync_revised_to_original_txkeeps 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 toREDIS_ENABLED=false. - arm64 optional components (#623) — Build args
ENABLE_OPEN3D,ENABLE_POSTGIS,ENABLE_OCR,ENABLE_FFMPEGallow disabling platform-specific components for cross-architecture builds. - Think tokens blocked streaming chunks — Disabled Qwen3.5
thinkmode during streaming so content delta events flow immediately without waiting for the full<think>…</think>block to complete. :latestDocker tag overwritten by dev builds — CI now reserves:latestand:bundle-latestexclusively for tagged releases. Dev pushes produce:mainand:sha-<short>only. Downstream consumers should pin to:mainfor rolling dev or:<version>for pinned releases. Fixes v2026.4.0 being masked onghcr.ioby later main builds.
- Synchronous chat API (#549) —
POST /api/v1/chatprovides direct LLM conversation with GPU concurrency semaphore, multi-turn history, and model selection.GET /api/v1/chat/modelslists available models with metadata. Health endpoint exposescapabilities.chatfor client availability detection. - Chunked audio transcription (#540, #541, #542, #543) — Long audio files automatically split into chunks for parallel transcription via fan-out
AudioChunkTranscriptionjobs 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_overlapper request. - Decomposed 3D model extraction (#531, #533, #534, #535) — Atomic per-view vision jobs with
RENDER_GPUtier, Open3D EGL GPU support, and AI revision after view assembly. - Video keyframe vision (#550) —
KeyframeCharacterVisionandKeyframeSettingVisionjob 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-reflectiondocument 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 existingOpenAIBackend(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, andOPENROUTER_API_KEYpassed 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 profiles —
COMPOSE_PROFILESselects 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 scripts —
setup.manifest.yamlmachine-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.
- GPU sidecar defaults — Whisper and pyannote now run on CPU by default to preserve GPU VRAM for inference. GPU variants available via
--profile gpu-12gbor--profile gpu-24gb. Breaking: existing deployments using GPU sidecars must setCOMPOSE_PROFILES=gpu-12gborgpu-24gb. - GPU job scheduling — GPU jobs serialize by default to prevent VRAM contention. Ollama models proactively unloaded between tier transitions.
- Video chunk size —
REVISION_VIDEO_CHUNK_SIZE_MAXreduced from 60K to 20K characters to prevent job-level timeout on long videos. - Job timeout —
JOB_TIMEOUT_SECSdefault raised from 600s to 1800s; env-var clamp raised to 7200s. - Concept tagging pipeline (#538, #539) —
ConceptTaggingchains fromAiRevision(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 tosubstring(). - Workspace version:
2026.2.13→2026.4.0
- 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_notewas 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
DefaultBodyLimiton TUS routes. - Migrations — Use snake_case enum values for
job_type; use correct singulardocument_typetable 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.
- 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.
- docker-compose.bundle.yml default tags — Sidecar image tags changed from
*-main(internal-only) to*-latest(available on both registries). Users settingFORTEMI_REGISTRY=ghcr.iono longer need to manually overrideFORTEMI_GLINER_TAGorFORTEMI_PYANNOTE_TAG. - Sidecar builds removed from main release pipeline —
publish-releaseandpublish-githubjobs inci-builder.yamlno longer build GLiNER/pyannote, reducing release build time. - Workspace version:
2026.2.12→2026.2.13
2026.2.12 - 2026-02-22
- 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
KeyframeVisionjobs 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) —
ThumbnailSpritehandler 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 viaSpeakerRelabeljob. - 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_optimizeparameter on attachment upload API and MCPmanage_attachmentstool. 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_metadataJSON. - 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 (
Rangeheader) 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 Listing —
GET /api/v1/attachmentsendpoint for listing all attachments across notes. - Related Notes with LLM Summary —
GET /api/v1/notes/{id}/relatedendpoint returns related notes with AI-generated context summary. - Document Type Slug Validation (#490, #491) — Accept
document_typeslug on note creation; validaterevision_modeparameter. - Handler-Initiated SSE Events —
job.queuedSSE events now emitted for downstream jobs queued by handlers (e.g., Extraction → Embedding), not just API-initiated jobs.
- 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
Serviceimplementations tocall(&self)signature change. - Workspace version:
2026.2.11→2026.2.12(56 commits)
- KeyframeVision Jobs Silently Orphaned Without Vision Backend (#529) —
KeyframeVisionHandleris now always registered regardless of vision backend availability. When the vision backend is unavailable, jobs returnRetryand 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 clippytoo_many_argumentson upload handler. - Audio Transcript Key Normalization (#523) — Unified
transcript_segmentskey 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_textfrom 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
DiarizeOutputfrom pyannote.audio 4.x, replace deprecateduse_auth_tokenparameter, normalize audio to WAV before diarization. - Nginx Upload and Proxy Configuration — Dedicated upload endpoint with
proxy_request_buffering offfor 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_descriptionfrom 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, andapprox_constantin test assertions.
- 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
- Stale Job Reaping — Worker automatically reaps orphaned
runningjobs on startup. Jobs stuck longer than 2× the timeout threshold (600 s) are reset topending(with retries remaining) or failed. UsesFOR UPDATE SKIP LOCKEDCTE to avoid blocking concurrent workers. (ADR-084) - PDF Null Byte Sanitization —
PdfTextAdapternow strips null bytes (\0) from bothpdfinfometadata values and extracted text before database insertion, preventing PostgreSQL22P05encoding errors on legacy PDFs (Acrobat 3.0/4.0 era). (ADR-085) - Ambiguous Column in Reap CTE — Fully qualified
retry_countreference in the stale-job reap query to prevent PostgreSQL ambiguity error. - Archive Note Counts —
list_archive_schemasnow computes live note counts instead of returning stale cached values.
- Testdb
max_locks_per_transaction— Increased to 256 in the test database image to support parallel archive schema tests without lock exhaustion.
- 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
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 |
-
Graph Quality Maintenance Pipeline (#482) — Automated
normalize → SNN → PFNET → diagnostics snapshotpipeline triggered viaPOST /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/maintenancetriggers 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 Tools —
trigger_graph_maintenanceandcoarse_community_detectionadded 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_configtable 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; setGLINER_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:relatedrelationships 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-IDreplay, 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 Management —
manage_conceptsMCP 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
DocumentCompositionmodel with migration. MCPmanage_embeddingstool extended withdocument_compositionaction. -
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.
-
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
qandrvalues, 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/generateto/api/chatfor 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.9→2026.2.10(86 commits)
- Mutual k-NN filter (#471) — Deferred as a no-op. The
create_reciprocalstep 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.
- 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 ofqueue()(#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.
- 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.
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
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 |
-
Multipart Shard Upload —
POST /api/v1/backup/knowledge-shard/uploadacceptsmultipart/form-datafile 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-docsknowledge 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/LISTENwake pattern replaces polling for immediate job pickup. Configurable concurrent processing viaJOB_MAX_CONCURRENT(default: 4) with drain-loop shutdown. -
MCP Core Tools Expanded — Added
manage_archives,manage_encryption,manage_backups, andmanage_embeddingsto the 23-tool core surface (now 27 core tools). -
Vision and Audio Enabled by Default —
OLLAMA_VISION_MODELandWHISPER_BASE_URLnow configured by default in Docker bundle for out-of-box multimodal extraction. -
Capacity Planning —
MAX_MEMORIESscales 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_pathpinned per archive - Cached
HybridSearchEngineinstances per schema - Removes the 400 guard for non-default archives
- Enables semantic and FTS search in all memory archives
- Per-schema connection pools with
-
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/livereadiness 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 API —
POST /api/v1/vision/describe+ MCPdescribe_imagetool -
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
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 frominformation_schemawith proper schema-qualification. - Text search config cloning (#172) — Custom FTS configurations (e.g.,
matric_english) cloned into each memory schema viapg_ts_configcatalog queries. - Per-request memory selection (#173) —
X-Fortemi-Memoryheader on every API request. Middleware validates memory exists (404), resolves schema, injectsArchiveContext. 3-step fallback: header →DefaultArchiveCache(60s TTL) → public schema. - All 91 API handlers routed — Every handler uses
SchemaContextwithSET LOCAL search_pathper transaction._txmethod pattern on all repositories for transaction-scoped isolation. - MCP session memory selection (#174) —
select_memoryandget_active_memorytools. Session state tracked per transport. All MCP API calls automatically includeX-Fortemi-Memoryheader. - Memory-scoped backup (#175) —
GET /api/v1/backup/memory/:nameusingpg_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/federatedwith dynamic UNION ALL across specified schemas. Results annotated withmemory_name. - Memory clone endpoint (#178) —
POST /api/v1/memories/:name/clonewith FK-orderedINSERT...SELECTvia 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 tracking —
schema_versioncolumn onarchive_registryfor auto-sync detection.
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)
KeyframeStrategyenum:Interval,SceneDetection,HybridmodesVideoMultimodalAdapterwired into extraction pipeline (requires ffmpeg + vision/whisper)- MCP
process_videoguidance tool directs agents to attachment upload workflow - MCP documentation topic (
get_documentation({ topic: "video" })) get_system_inforeports 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
Multi-view rendering extraction via attachment pipeline:
Glb3DModelAdapterwith Three.js headless multi-view rendering + vision model descriptionExtractionStrategy::Glb3DModelvariant routes allmodel/*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_modelguidance tool directs agents to attachment upload workflow - MCP documentation topic (
get_documentation({ topic: "3d-models" })) get_system_inforeports 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
Ad-hoc audio transcription via Whisper-compatible backend:
- Wires existing
TranscriptionBackendtrait +WhisperBackendinto API server POST /api/v1/audio/transcribeAPI endpoint (base64 audio, optional mime_type and language)- MCP
transcribe_audiotool for agent access AudioTranscribeAdapterregistered in extraction pipeline for automatic attachment processing- Configurable via
WHISPER_BASE_URLandWHISPER_MODELenv 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
Ad-hoc image description via Ollama vision LLM:
VisionBackendtrait +OllamaVisionBackendinmatric-inferencecratePOST /api/v1/vision/describeAPI endpoint (base64 image, optional mime_type and prompt)- MCP
describe_imagetool for agent access - Configurable via
OLLAMA_VISION_MODELenv 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
- 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
- Auth middleware — Centralized Bearer token validation with
REQUIRE_AUTHtoggle - OAuth2 scope enforcement — Centralized scope checks for all mutation endpoints
- Configurable OAuth token lifetimes —
ACCESS_TOKEN_TTLandREFRESH_TOKEN_TTLenv vars - API key system —
POST /api/v1/api-keysfor programmatic access - PKE HTTP API — Encryption tools accessible via REST (not just CLI binary)
- See: Authentication Guide, ADR-071
- Archive creation with full schema cloning
- Archive metadata (stats, version tracking)
- PKE keyset registry per archive
- Archive-scoped operations
- 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=fullfor 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/attachmentsmultipart upload. - Tool definition extraction —
tools.jsextracted fromindex.jsfor 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
- Server-Sent Events (SSE) for real-time note change notifications
- WebSocket support for bidirectional streaming
- Webhook system for external integrations
- See: Real-time Events
- 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
-
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. Appliedleft(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-Memoryheader 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_BYTESenv 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 Docker —
OllamaVisionBackendnow readsOLLAMA_BASEenv var first (matching embedding backend), fixing 500 errors in Docker containers where onlyOLLAMA_BASEis set
The restore system was rewritten for correctness and robustness:
- Thread-safe psql pipe (#166) —
tokio::task::spawn_blockingto prevent pipe deadlocks with large dumps - Extension-owned object exclusion — DROP script queries
pg_dependwithdeptype = 'e'to skip PostGIS-owned objects likespatial_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
- SKOS search fixes (#132, #133, #134, #149) —
autocomplete_conceptsandsearch_conceptsnow work with custom schemes;get_concept_fullreturns 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_memberJSON parsing,update_conceptnull 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
- 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_relatedcleans 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_noterespectsstepsparameter - SKOS cascade delete (#165) —
delete_concept_scheme(force=true)cascade-deletes concepts - Archive schema completeness (#169) —
note_originaltable included in schema cloning
- Migration timestamp deduplication — Duplicate prefixes cause
_sqlx_migrations_pkeyviolations - 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_varin tests - Auto-migrate on startup —
sqlx::migrate!()runs on API startup frommain.rs - Stale container cleanup — CI kills containers by port before starting new ones
- 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
- 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
- Workspace version:
2026.2.7→2026.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
| 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).
None. Full backward compatibility maintained:
/api/v1/archives/*routes continue to work alongside new/api/v1/memories/*- Default behavior (no
X-Fortemi-Memoryheader) routes to public schema as before - Existing MCP tool names preserved; new tools added with "memory" terminology
- Database migrations run automatically on startup via
sqlx::migrate!() - PostgreSQL 18 upgrade — Review upgrade notes in ADR-096
- SCRAM-SHA-256 auth — Existing passwords automatically upgraded on next login
- Fresh deployments now seed a default archive —
list_archives()returns the public schema - MCP clients should update tool descriptions — memory-scoping context added to 10+ tools
- Backup scripts — If using custom backup scripts, consider switching to per-memory backup
(
GET /api/v1/backup/memory/:name) for targeted exports - Docker bundle — MCP OAuth credentials now auto-registered on first startup; manual registration no longer required
- Whisper transcription — Now bundled by default with GPU in Docker bundle
- Shard import — New
POST /api/v1/backup/knowledge-shard/uploadmultipart endpoint available; existing JSON base64 endpoint remains for backward compatibility - First-boot documentation — Fresh Docker bundle deployments automatically load the
fortemi-docsarchive (243 notes of product documentation)
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
- 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/collectionsto/api/v1/concepts/collections(#36) - SKOS Turtle export - Fixed
export_skos_turtleto use scheme_id as path parameter instead of query parameter (#36) - SKOS Collection create/update - Fixed
ordered→is_orderedfield name mapping (#36)
2026.2.6 - 2026-02-05
- Repository reset - Squashed history for clean baseline
- License date updated - BSL change date set to February 16th, 2030
2026.2.5 - 2026-02-05
- 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.
- Updated search guide with comprehensive emoji Unicode range reference
2026.2.4 - 2026-02-05
- MCP limit=0 parameter - Fixed JavaScript falsy check that skipped
limit=0parameter (#29)- Changed from
if (args.limit)toif (args.limit !== undefined && args.limit !== null) - API now correctly returns 400 "limit must be >= 1"
- Changed from
- 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
- UAT issues #29-#31 resolved
limit=0now 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
- Nginx proxy documentation -
deploy/nginx/README.mdwith SPA+API routing guidance
2026.2.2 - 2026-02-04
- UAT findings resolved (#13-#26) - All user acceptance testing issues addressed
- MCP authorization_servers metadata - Now correctly uses ISSUER_URL for OAuth discovery
- README enhanced with plain-language vision statement
- Removed build status badge from README (unreliable external service)
2026.2.0 - 2026-02-02
| 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 |
- CI worker tests - Converted from
#[sqlx::test]to#[tokio::test]to avoidCREATE INDEX CONCURRENTLYtransaction 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.,
projectmatchesproject/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_hostsconfiguration for Linux Docker containershost.docker.internal:host-gatewayenables container-to-host Ollama communication- OLLAMA_BASE environment variable now properly configured
- 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 configurationsget_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
- Tag filtering in
list_notes,search_notes,strict_filter, andembedding_setsnow supports:- Case-insensitive matching:
LOWER(tag_name) = LOWER($1) - Hierarchical matching:
LOWER(tag_name) LIKE LOWER($1) || '/%'
- Case-insensitive matching:
2026.1.12 - 2026-02-01
| 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 |
metadatafield exposed in create/update note API endpoints (#359)tagsparameter forsearch_notesMCP 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
- FTS accent/diacritic folding - "café" now matches "cafe" search (#328)
- Added
unaccentPostgreSQL extension - Created
matric_englishtext search configuration - All FTS queries updated to use new configuration
- Added
- Embedding set ID assignment - Embeddings now properly assigned to sets (#353)
store()method now setsembedding_set_idfrom 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)
- All Rust FTS queries use
matric_englishconfig instead ofenglish - MCP error responses now return safe, user-friendly messages
20260131000000_fts_unicode_normalization.sql- Unicode search support20260131000001_fix_embedding_set_id.sql- Embedding set backfill
- CI race condition when main branch and tag pushes run simultaneously
- Container names now include
GITHUB_RUN_IDfor uniqueness - Database ports dynamically assigned to avoid conflicts
- Affects both build and test-container jobs
- Container names now include
| 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 |
- 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
- Removed redundant
- CI workflow renamed from "CI (Builder)" to "CI"
- 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
ci.yaml- redundant bare-runner workflow (superseded by builder-based CI)
| 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 |
- 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.mdfor plain-English explanation
LICENSE- BSL 1.1 license terms with parametersLICENSE.txt- AGPL-3.0 full text (change license, effective 2030)NOTICE- Copyright and third-party attributiondocs/content/licensing.md- Comprehensive licensing FAQ and guide
- Missing license metadata in
matric-searchcrate Cargo.toml
- 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
| 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 |
- 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
- Changed from
- Workflow execution order for builder image updates
- Added
paths-ignoreto prevent CI race conditions with builder updates - Added
trigger-cijob 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.exampleand Dockerfile
- ci-builder.yaml now uses port 15432 for PostgreSQL (isolated from host)
- build-builder.yaml triggers CI workflows after successful builder image push
| 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 |
- 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-pkebinary 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
- MCP OAuth metadata now uses external
ISSUER_URLinstead of internal address - MCP protected resource URL configurable via
MCP_BASE_URL
| 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 |
- Issue #203:
update_notenow returns fullNoteFullobject instead of HTTP 204 - Issue #204:
backup_statusauto-creates backup directory with graceful permission handling - Issue #205: Backup tools now work out of the box (resolved by #204)
- PKE keyset management MCP tools:
pke_list_keysets- List all keysets in ~/.matric/keys/pke_create_keyset- Create new named keyset with passphrasepke_get_active_keyset- Get currently active keyset infopke_set_active_keyset- Set active keyset by namepke_export_keyset- Export keyset to directory for backup/transferpke_import_keyset- Import keyset from files or export directorypke_delete_keyset- Delete a keyset permanently
- Backup status now returns "cannot_create_directory: {error}" on permission failure
update_noteMCP handler returns{ success: true, note }instead of just{ success: true }- Backup directory defaults to
/var/backups/matric-memory(auto-created)
| 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 |
- Issue #198: SQL parameter mismatch in
update_notewhen updating onlyarchivedorstarred - Issue #199:
search_notes_strictwithrequired_tagsnow supports simple string tags via fallback - Issue #201: MCP server now handles
text/plainresponses (e.g., version diffs) correctly - Issue #202:
matric-pkebinary built and deployed to/usr/local/bin
StrictTagFilternow supportsrequired_string_tags,any_string_tags,excluded_string_tagssimple_tag_exists()method for simple tag lookup fallback- Content-Type aware response parsing in MCP server
- Dynamic SQL parameter indexing in note update operations
- Tag resolver tries SKOS concept first, falls back to simple tag if not found
| 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 |
- 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
find_similar_with_strict_filter()for isolated semantic searchtest-skos-regression.sh- 17 regression tests for SKOS ENUM fixestest-strict-search.sh- 7 data isolation tests for strict_filter
2026.1.0 - 2026-01-24 (previous)
- 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)
- 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
- 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
- Note versioning: populate
note_revised_currenton creation, fix provenance column name - Note revision INSERT statements: correct column names
- Note original table: add missing
idcolumn - 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
- 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
| 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) |
- 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
- 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
- RESTful API with OpenAPI 3.1 specification
- Swagger UI at
/docs - Comprehensive documentation
- Architecture guide
- Integration guide
- API reference
- MCP server documentation
- Encryption guide
- PostgreSQL 14+ with pgvector extension
- HNSW indexes for vector similarity search
- GIN indexes for full-text search
- Optimized indexes for strict tag filtering
- Input validation on all endpoints
- CORS support for browser access
- TLS termination at reverse proxy
- No stored credentials in codebase
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