feat(local-deploy): Artifactory as single source of truth — capture writes to RT first, pgvector via sync only#3
Merged
safixdev merged 10 commits intoJun 18, 2026
Conversation
…pgvector via sync only - capture_thought pushes artifact to RT before any local write - pgvector is populated exclusively through runAutoSync (no direct DB writes from capture path) - artifactory.ts: add probeRtRoot, JF_PATH/ENRICHED_PATH env resolution for Docker - docker-compose: pass JF_PATH env var to server container Co-authored-by: Cursor <cursoragent@cursor.com>
…e deletes Memories now carry full provenance and are deletable with an audit trail, and metadata is supplied by the calling agent instead of a local chat model. - capture_thought accepts structured metadata (type, topics, people, action_items, dates_mentioned, source) plus git_user/repo/context; the server no longer runs an LLM to infer it, falling back to minimal defaults. - pushArtifact writes provenance as RT properties (git_user, user_id, repo, context, created_at, type, topics, content) via --target-props, so memories are listable/queryable from Artifactory without downloads. - delete_thought tool: writes a tombstone (<id>.deleted.json) recording who deleted what and when, removes the live artifact, and deletes from pgvector. - autosync reconciles tombstones every run (removes tombstoned memories from pgvector, never re-indexes them); re-capturing content resurrects it. - db: add deleteByArtifactPath (postgres + supabase drivers). - llm: remove extractMetadata/chat-model path; embedding is the only model. Co-authored-by: Cursor <cursoragent@cursor.com>
- ollama-pull pulls only the embedding model (no gemma3:4b). - .env.example: remove CHAT_MODEL; add JF_SERVER_ID, RT_REPO, GIT_USER and Artifactory guidance; document embedding-only design. - README: reflect agent-owned metadata, Artifactory-as-SOT, no chat model, and updated resource expectations; point operators at the open-brain-up skill. - add docker/.gitignore (jfrog-config/, openbrain-data/, .env.secrets). Co-authored-by: Cursor <cursoragent@cursor.com>
Production runbook packaged as an agent skill: collects the team's Artifactory inputs (URL, token, server-id, repo), generates the jf config the server mounts, templates env/secrets, builds with corporate-CA support for TLS-intercepting proxies, starts the stack, and verifies with a capture -> search round trip. Bundles list_rt_memories.sh for inspecting live and tombstoned memories. Co-authored-by: Cursor <cursoragent@cursor.com>
search_thoughts now takes optional `repo` (scope to the agent's current
project) and `all_repos` (global override), filtering pgvector via
metadata @> {repo}. Reduce the tool surface to the agent-facing core —
capture_thought, search_thoughts, list_thoughts, delete_thought — dropping
the ChatGPT-compat search/fetch tools, thought_stats, and trigger_sync
(autosync runs on capture and on a timer). Remove the now-dead citation
helpers and reset_cursor path.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the ~4 GB Ollama image with HuggingFace Text Embeddings Inference
(~343 MB Rust CPU server, OpenAI-compatible /v1/embeddings), keeping
mxbai-embed-large-v1 so vectors stay 1024-dim and no re-embedding is needed.
This cuts the embedding stack footprint from ~4.8 GB to ~1 GB.
Add a CDN-blocked / air-gapped path: pre-fetch the model on the host into
tei-model/ and load it via EMBED_MODEL=/model (corp proxies often reach
huggingface.co but block its CDN). Document the arm64 TEI tag.
Add option-B distribution: server now carries an image: ref
(${OPENBRAIN_IMAGE:-openbrain-mcp-server:local}) alongside build:, so teams
can pull a prebuilt image instead of building from source. Update the
open-brain-up skill with a Publishing section, the TEI model steps, the
4-tool client list, and corrected resource sizing.
Co-authored-by: Cursor <cursoragent@cursor.com>
…d auth The MCP endpoint is a private per-developer service (sharing is via Artifactory, the SOT), so harden it for that model: - Bind the published port to 127.0.0.1 only — closes the prior 0.0.0.0 LAN exposure. Networked use must add auth + TLS at an ingress. - Remove the dead MCP_ACCESS_KEY (it was read but never enforced) and the misleading "Auth" labeling; document that access control lives in RT (repo permissions + access token), not this port. - Add a GET /health route and a server container healthcheck so Docker can detect and restart a hung server. - Persist the autosync cursor in Postgres (new sync_state table, lazily created for existing deploys) and restore it on startup, so a restart resumes incremental sync instead of re-scanning/re-downloading the whole RT repo. - Update the up-skill, docker README, and secrets template to match (no auth header, loopback URL, POSTGRES_PASSWORD only). Co-authored-by: Cursor <cursoragent@cursor.com>
… delivery Storage layout is now foldered per git repo: RT_REPO/<repo>/thoughts/<sha>.json (+ .deleted.json tombstones) `repo` is required on capture_memory and delete_memory so each project's memories are physically separated and recall stays repo-scoped. Search/list and the tombstone reconcile scan RT_REPO/*/thoughts/*. Also finalizes the local delivery in this batch: - MCP tools renamed to *_memory (capture/search/list/delete) - Postgres trust auth (no password); db.ts tolerates empty password - self-contained docker-compose (hardcoded plumbing, no .env templates) - offline bundle (build_bundle.sh) + RT installer (install_from_bundle.sh) - docs/skill updated to the new layout and bootstrap flow Co-authored-by: Cursor <cursoragent@cursor.com>
Two production gaps in multi-user sync: 1. Repo-scoped cache. Sync no longer mirrors every repo's memories onto every client. The scope is a set seeded from SYNC_REPOS and grown lazily whenever a repo is captured-to or searched; each client only pulls RT_REPO/<repo>/thoughts/* for repos it actually uses. A newly scoped repo is backfilled immediately so the triggering request sees its data. listArtifacts/listTombstones now take an optional repo. 2. Skew-safe cursors. Per-repo cursor (sync_state key "cursor:<repo>") advances to the max RT `created` timestamp actually observed instead of the client wall clock, so local clock skew can't silently skip a teammate's new memory. Fetch uses `>= cursor` (idempotent upsert) to avoid boundary misses. getSyncCursor/setSyncCursor take a key. compose: optional SYNC_REPOS + AUTOSYNC_INTERVAL_MS passthroughs. Co-authored-by: Cursor <cursoragent@cursor.com>
The installer no longer assumes the friend's default jf server points at the right Artifactory. Connection precedence: 1. JF_URL + JF_ACCESS_TOKEN → register a server on the fly (optional JF_SERVER_ID) 2. JF_SERVER_ID → use an already-configured server 3. default jf server → fallback Validates a specified server exists; clear error otherwise. Docs updated with a connection table and a repo21 (entplus) example. Co-authored-by: Cursor <cursoragent@cursor.com>
safixdev
approved these changes
Jun 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
capture_thoughtnow pushes the artifact to Artifactory (RT) before any local write — RT is the SOTrunAutoSync; no direct DB writes from the capture pathartifactory.ts: addsprobeRtRootfor startup diagnostics, resolvesJF_PATH/ENRICHED_PATHsojfCLI works correctly inside the Docker containerdocker-compose.yml: passesJF_PATHenv var to the server containerMotivation
Previously,
capture_thoughtwrote directly to pgvector and never touched Artifactory. This meant memories captured via MCP were invisible to any other memory system (simple-brain, mem0, etc.) sharing the same RT repo. With this change, RT is the authoritative store — a thought that doesn't exist in RT doesn't exist.Write flow (after this PR)
Test plan
capture_thoughtvia MCP → verify artifact appears inopen-brain-memories/thoughts/in RTlist_thoughtsreturns the captured thought after synctrigger_syncwithreset_cursor: truere-populates pgvector from RT from scratchMade with Cursor