Tacit
Your team's memory, wired into every AI tool.
Tacit syncs a team's Slack, GitHub and Notion into a permission-aware knowledge graph, so you can ask "why did we do X, who decided, and where's the evidence?" from the web app, or from Claude and Cursor over MCP.
Status: early development. Auth, workspaces, the GitHub connector, hybrid search with citations, the MCP server and the knowledge graph (Graphiti, opt-in) work. Slack/Notion connectors and the public benchmark are next.
Architecture (so far)
Browser ──► Next.js (apps/web) ── Better Auth ──► Postgres (auth tables, as tacit_auth)
│ short-lived JWT (EdDSA)
▼
FastAPI (apps/api) ── verifies JWT via /api/auth/jwks
│ SET LOCAL app.user_id / app.workspace_id
▼
Postgres with row-level security (as tacit_api)
- Workspaces are Better Auth organizations (members, roles, email invites).
- Isolation is enforced twice: every query filters by workspace in code, and Postgres RLS blocks anything a filter misses. The API's database role can't read sessions, OAuth tokens or signing keys at all.
- Four DB roles:
tacit_migratorowns tables (Alembic),tacit_authis used by Better Auth,tacit_apiby FastAPI and MCP (read-only on synced content), andtacit_workerby Celery jobs (scoped to one workspace per transaction). - Source permissions are enforced in Postgres: a restrictive RLS policy shows a synced document only if it's public or your linked GitHub account can access the repo, based on access data less than 6 hours old. Otherwise access is denied.
Search
Synced documents are split into ~400-token chunks and indexed two ways: embeddings (pgvector, HNSW) and Postgres full-text (tsvector). A query runs both and fuses them with Reciprocal Rank Fusion. Each result is the best chunk per document, with a highlighted snippet and a link to the exact PR, issue, comment or doc. Documents are only re-embedded when their content hash changes.
Embeddings default to a local model (fastembed bge-small-en-v1.5, 384 dims): no API
key, no per-document cost, reproducible. Set EMBEDDING_PROVIDER=openai (plus
OPENAI_API_KEY) to use text-embedding-3-small at 384 dims instead. Ranking weights
and re-ranking are deliberately untuned until the benchmark (build step 7) measures them.
Knowledge graph (opt-in)
Merged PRs with their reviews, discussed issues and docs become Graphiti episodes on Neo4j: people, components, technologies and decisions, with facts that know when they became true and when they were replaced. Answers can say what the current decision is and what it superseded.
- Never leaks: one graph group per repo. A search only covers repos you can read right now (from Postgres), never calls Graphiti with an empty group list, and drops any fact Postgres can't tie to a visible source, which is also where its citation comes from.
- Costs money, so it's off by default. It needs
GRAPH_ENABLED=true, an LLM key (claude-haiku-5-5by default), and a per-workspace opt-in by an admin. Private repos need a second opt-in, because their text goes to the LLM provider. Every call is recorded inllm_usage, and a monthly budget per workspace is a hard stop. - Run
docker compose --profile graph up -d neo4j. Seedocs/design/graph-layer.md.
Benchmark
On the fictional Larkspur dataset (52 "why" questions, local embeddings, $0), hybrid search beats vector-only RAG (recall@10 0.865 vs 0.750, MRR 0.545 vs 0.386). Keyword-only scored highest (0.904), probably because the questions reuse the corpus's wording. The graph arm hasn't run yet. Numbers, caveats and how to reproduce: benchmark/README.md.
Use it from Claude or Cursor (MCP)
Tacit is a remote MCP server at http://localhost:8000/mcp with OAuth 2.1. The web app is
the authorization server (dynamic client registration, PKCE, consent screen), and tokens
are bound to the MCP resource. Every tool call runs with your permissions, so the
assistant sees exactly what you can see.
# Claude Code
claude mcp add --transport http tacit http://localhost:8000/mcp
For Claude Desktop or Cursor, add a remote MCP server with that URL. The client opens a browser to sign in and approve access.
Tools: list_workspaces, search (hybrid, cited), get_document (a source plus its
review/comment thread) and search_facts (time-aware graph facts with citations, when
the graph is enabled).
Local development
Needs Docker, uv, Node 22+ and pnpm.
pnpm bootstrap # .env + dependencies
pnpm infra:up # Postgres (pgvector), Redis, Mailpit
pnpm db:migrate # create tables, grants, RLS policies
pnpm dev:api # http://localhost:8000 (in one terminal)
pnpm dev:web # http://localhost:3000 (in another)
Emails (verification, invites) land in Mailpit at http://localhost:8025.
To sync GitHub, create the dev GitHub App with pnpm github:create-app and run
pnpm dev:worker and pnpm dev:webhooks. See infra/github/README.md.
pnpm test # API tests (uses a throwaway tacit_test database)
pnpm lint && pnpm typecheck
Repo layout
| Path | What |
|---|---|
apps/web |
Next.js 16, Better Auth, shadcn/ui |
apps/api |
FastAPI, SQLAlchemy (async), Alembic migrations, Celery workers |
apps/api/src/tacit_api/connectors/github |
GitHub connector (githubkit) |
docs/design |
Design docs |
infra/postgres |
Role setup (pnpm db:roles re-applies it) |
infra/github |
GitHub App manifest and setup guide |
docker-compose.yml |
Local infrastructure |
License
Apache 2.0