Self-hosted knowledge index with fine-grained access control. MCP + REST. Apache 2.0.
638
Your index. Your access rules. Your perimeter. Agents see exactly what they're allowed to see.
nacre.work · Docs · Quickstart · Source · Releases · Discussions
Nacre is a self-hosted knowledge index with fine-grained access control. Agents reach it over MCP, applications over a REST API. No chat interface, no company assistant — just the context layer underneath them.
One image, three processes, one migrator. The entry point decides which:
| command | runs |
|---|---|
node packages/api/dist/main.js | the REST API and authorization service, port 8080 (default) |
node packages/mcp/dist/main.js | the MCP server, Streamable HTTP on port 8081 |
node packages/worker/dist/main.js | the indexing pipeline: parse, chunk, embed |
node packages/core/dist/migrate-main.js | the schema migrator, run once before the others |
It needs PostgreSQL, Qdrant and Redis beside it, the parser sidecar nacrecontextlayer/nacre-parser and an OpenAI-compatible embedding endpoint. The Compose files in the repository wire all of it; the Helm chart is for Kubernetes. Every variable is documented in docs/config.md.
Tags: {version} and latest, amd64 and arm64. ghcr.io/nacre-work/nacre is the canonical address; this repository is a mirror pushed by the same build at the same tags, so the two are byte for byte the same image.
Vector search is a solved problem. What isn't solved: making sure an agent querying a company index sees exactly the documents the requesting user is cleared for — and being able to prove it to an auditor.
read/write/admin inherited top-down. write does not imply read; admin implies both. Document-level grants and deny rules are commercial — this build refuses them and says so, rather than accepting a rule it cannot propagate.top_k returns k permitted results rather than k minus whatever got stripped out.SQLSTATE 23505, an invoice number or a variable name needs the literal match, and a dense-only index does not reliably return it.npx @nacre.work/cli signs in, creates a layer, walks a directory into it and searches. A document that fails to index is a non-zero exit, so a nightly ingest cannot report success having indexed nothing.git clone https://github.com/nacre-work/nacre && cd nacre
cp .env.example .env
docker compose --profile minimal up -d
Full walkthrough: docs/quickstart.md. On an Apple Silicon Mac, read docs/apple-silicon.md first — the images are arm64 and the stack is native, but the embedder is the one piece you run on the host. Upgrading between versions: docs/upgrading.md.
Create an organization, create a layer, grant someone read, ingest a document, poll the job to indexed, search and get the chunk back — and search as someone without the grant and get nothing while the vectors are still sitting in the index. Revoking a grant removes the document from the next search; the permitted set is computed per request, so there is nothing to wait for.
A document can be sent as JSON or uploaded as a form, and a PDF, an Office or OpenDocument file, an EPUB or an RTF is extracted by the parser sidecar. Both signals have to agree — the declared type and the bytes' signature — because a declared type the bytes contradict is a disagreement, and sniffing alone would make the declared type decoration.
Sign-in is email and password with rotating refresh tokens, an optional second factor (TOTP or WebAuthn), and tokens signed with a shared secret or an Ed25519 key whose public half is published at /.well-known/jwks.json. The access log is readable at GET /v1/audit — newest first, cursor-paged, as JSON, JSONL or CSV.
Search is rate limited per organization, unsafe methods take an Idempotency-Key, collections page by cursor, and a layer can be moved onto a different embedding model with search staying available throughout.
Six rules. Breaking any of them is a security incident, not a bug. Details in docs/authz.md.
write does not imply read.Reference connectors keep a source in sync with an index — git, S3, SQL, Google Drive, MongoDB and IMAP — each one container, configured through its environment. They are nacrecontextlayer/connector-<name> here and github.com/nacre-work/connectors at the source.
Apache 2.0 — all of it. Everything in this image is in the public repository and stays there.
Multi-tenancy, SSO/SCIM, document-level deny rules, EMA, SIEM export, global admin and backup are commercial modules. They live in a separate private repository under a separate license and are not distributed with this image — see docs/licensing.md for the line between the two. Where this build meets one of them it refuses in the open: a deny rule or a document-scoped grant is answered 400 with the reason, rather than accepted and silently not enforced.
The Nacre name and mark are trademarks; see TRADEMARK.md.
Content type
Image
Digest
sha256:0fe6d5232…
Size
96.6 MB
Last updated
5 minutes ago
docker pull nacrecontextlayer/nacre