# Security Considerations for Deploying Code-Graph-RAG in Production

> Learn security considerations for deploying Code-Graph-RAG in production. Secure your deployment by binding services, disabling untrusted front-ends, and restricting API keys.

- Repository: [Vitali Avagyan/code-graph-rag](https://github.com/vitali87/code-graph-rag)
- Tags: best-practices
- Published: 2026-08-19

---

**Deploy Code-Graph-RAG securely by binding the Memgraph and Qdrant services to loopback interfaces, disabling hybrid front-ends for untrusted repositories, enabling bearer-token authentication for the MCP server, and storing API keys in restricted environment files with `600` permissions.**

Code-Graph-RAG builds knowledge graphs of codebases using Tree-sitter parsers and stores them in a local Memgraph instance, with optional vector embeddings in Qdrant. When moving from local experimentation to production environments, understanding the trust boundaries and threat model is essential to prevent unauthorized network access, code execution, and data exfiltration. This guide covers the security architecture, threat model, and hardening steps required for a safe deployment.

## Core Security Architecture

Code-Graph-RAG integrates several components, each with specific security concerns and mitigations defined in [`docs/architecture/security.md`](https://github.com/vitali87/code-graph-rag/blob/main/docs/architecture/security.md):

- **Tree-sitter Parsing** reads source files and produces ASTs. Pure parsers never execute repository code, but the C++ (`CPP_FRONTEND=hybrid`) and C# (`CSHARP_FRONTEND=auto`) front-ends invoke external toolchains that may run parts of the repository under the current user's privileges. For untrusted repositories, explicitly set `CSHARP_FRONTEND=treesitter` to avoid invoking the Roslyn compiler.

- **Memgraph Graph Store** holds the structural graph locally. By default, Docker exposes port `7687` on all network interfaces, making it reachable from any host on the same network if not properly bound.

- **Qdrant Vector Store** stores embeddings for semantic search. Like Memgraph, it publishes port `6333` to the host network by default and requires the same loopback binding hardening.

- **LLM and Embedding Providers** send code snippets and queries to embedding services. All external traffic uses TLS enforced via `httpx`. By default, the system uses a local UniXcoder model; external providers like OpenAI are only contacted when `CGR_EMBEDDING_PROVIDER=openai` is explicitly set.

- **MCP Server** provides an HTTP API for agents. It binds to `127.0.0.1` by default. Binding to a non-loopback address requires setting the `MCP_HTTP_AUTH_TOKEN` environment variable and implementing bearer-token validation.

- **Agent Tools (CLI)** execute shell commands and edit files on user request. The default mode screens commands against an allowlist and blocks destructive paths. A "YOLO" mode exists to disable this protection but is off by default.

- **XML and Other Parsers** use `defusedxml` to safely parse XML from target projects, disabling dangerous features that could lead to entity-expansion attacks.

## Threat Model and Trust Boundaries

The security model defines four primary trust boundaries that operators must enforce:

1. **Untrusted Repository Input** – The repository being indexed is treated as hostile input. While pure Tree-sitter parsers cannot execute code, hybrid front-ends for C++ and C# may invoke toolchains that run repository build scripts under the current user's privileges.

2. **Local Service Exposure** – Memgraph and Qdrant run in Docker containers with ports published to the host network. Without explicit loopback binding, these services are reachable from any host on the same network, creating an unauthenticated access vector.

3. **LLM and Embedding Provider Boundary** – When configured to use remote providers, code snippets travel over TLS. API keys are supplied via environment variables (`.env`) and are never stored inside the graph database.

4. **MCP Server Boundary** – By default, the server only listens on `127.0.0.1`. If exposed to the network, authentication via a bearer token is mandatory.

The security model guarantees that **credentials never appear in the graph** and that **network-exposed services are unauthenticated by default**, requiring the operator to implement additional network controls or authentication layers.

## Production Hardening Recommendations

Implement these controls to secure a production deployment:

- **Restrict Docker Port Bindings** – Prevent unauthenticated remote access by binding Memgraph and Qdrant to localhost only. Use `-p 127.0.0.1:7687:7687` for Memgraph and `-p 127.0.0.1:6333:6333` for Qdrant. This addresses the exposure tracked in issue [#1012](https://github.com/vitali87/code-graph-rag/issues/1012).

- **Disable Hybrid Front-ends for Untrusted Code** – Hybrid front-ends may execute build scripts. Export `CSHARP_FRONTEND=treesitter` and `CPP_FRONTEND=treesitter` before running `cgr start` to force pure parsing mode.

- **Run the Daemon Stack Behind a Firewall** – Limit exposure to internal networks only by using Docker's private bridge networks or restricting traffic to trusted IP ranges via host-level firewalls.

- **Secure LLM API Keys** – Store keys in a `.env` file with restrictive permissions (`chmod 600 .env`) and never commit this file to version control. The [`codebase_rag/stack/manager.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/stack/manager.py) file handles daemon lifecycle commands and respects these environment variables.

- **Enable MCP Authentication** – When binding the MCP server to a non-loopback address, set `MCP_HTTP_AUTH_TOKEN=<strong-token>` and configure clients to send the bearer token in the HTTP Authorization header.

- **Audit Dependencies** – Supply-chain attacks are mitigated by pinned dependencies in `uv.lock`. The project runs OSV-Scanner and Dependabot checks via CI. Keep `uv.lock` updated and verify checksums.

- **Use Signed Releases** – Verify release artifacts with Sigstore to guarantee integrity. Use `cosign verify-blob` with the provided public key for the release tarball.

- **Apply Least-Privilege Runtime** – Run the daemon under a dedicated system user with limited permissions. Start the Docker stack with that user's UID to minimize blast radius.

## Configuration Examples

Apply these hardening configurations in your production environment:

```bash

# 1. Restrict Docker port bindings to localhost (loopback)

docker run -d \
  --name memgraph \
  -p 127.0.0.1:7687:7687 \
  memgraph/memgraph

docker run -d \
  --name qdrant \
  -p 127.0.0.1:6333:6333 \
  qdrant/qdrant

```

```bash

# 2. Disable C# and C++ hybrid front-ends for untrusted repos

export CSHARP_FRONTEND=treesitter
export CPP_FRONTEND=treesitter
cgr start --repo-path /path/to/repo --update-graph

```

```bash

# 3. Secure MCP server with token authentication

export MCP_HTTP_AUTH_TOKEN="s3cr3t-token-$(uuidgen)"
cgr daemon up

# To bind to a specific interface with auth:

# cgr daemon up --bind 0.0.0.0 --token "$MCP_HTTP_AUTH_TOKEN"

```

```bash

# 4. Store API keys in a protected .env file

cat > .env <<EOF
OPENAI_API_KEY=sk-********************
MCP_HTTP_AUTH_TOKEN=s3cr3t-token-$(uuidgen)
EOF
chmod 600 .env

```

```bash

# 5. Verify a signed release artifact (example for version 0.0.484)

cosign verify-blob --key cosign.pub code-graph-rag-0.0.484.tar.gz

```

## Key Implementation Files

Reference these source files to understand and configure the security posture:

- **[`docs/architecture/security.md`](https://github.com/vitali87/code-graph-rag/blob/main/docs/architecture/security.md)** – Contains the full security model, threat analysis, and assurance case for the system.

- **[`.github/SECURITY.md`](https://github.com/vitali87/code-graph-rag/blob/main/.github/SECURITY.md)** – Defines the vulnerability reporting policy and security response procedures.

- **[`codebase_rag/stack/manager.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/stack/manager.py)** – Implements daemon lifecycle commands including `daemon_up` and `daemon_down`, handling service startup and binding logic.

- **[`codebase_rag/stack/constants.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/stack/constants.py)** – Stores user-facing error messages and guidance for daemon binding and authentication failures.

- **[`codebase_rag/cli.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/cli.py)** – Serves as the CLI entry point and includes routing logic for MCP and daemon commands.

- **[`docs/architecture/graph-schema.md`](https://github.com/vitali87/code-graph-rag/blob/main/docs/architecture/graph-schema.md)** – Describes how front-ends map source code to the unified graph schema, which is relevant when evaluating parsing modes.

## Summary

- Bind Memgraph (port `7687`) and Qdrant (port `6333`) to `127.0.0.1` to prevent unauthenticated remote access.
- Set `CSHARP_FRONTEND=treesitter` and `CPP_FRONTEND=treesitter` when indexing untrusted repositories to avoid arbitrary code execution via build toolchains.
- Protect API keys in `.env` files with `chmod 600` permissions, and enable `MCP_HTTP_AUTH_TOKEN` when exposing the MCP server beyond localhost.
- Verify release integrity using Sigstore cosign and maintain dependency hygiene by auditing the pinned `uv.lock` file.

## Frequently Asked Questions

### Does Code-Graph-RAG execute the code it indexes?

Pure Tree-sitter parsers do not execute code, but the C++ and C# hybrid front-ends (`CPP_FRONTEND=hybrid`, `CSHARP_FRONTEND=auto`) may invoke external toolchains that run repository build scripts under the current user's privileges. To prevent execution, set these environment variables to `treesitter` before running `cgr start`.

### How do I prevent remote access to the Memgraph database?

By default, Docker publishes port `7687` on all interfaces. Bind it to loopback only by using the flag `-p 127.0.0.1:7687:7687` when starting the container. The project tracks a change to make this the default behavior in issue [#1012](https://github.com/vitali87/code-graph-rag/issues/1012).

### Is data sent to external LLM providers encrypted?

Yes, all HTTP traffic to external providers uses TLS via the `httpx` library. However, be aware that sensitive code leaves your machine when using external providers. The default configuration uses a local UniXcoder model; external providers are only contacted when `CGR_EMBEDDING_PROVIDER=openai` is explicitly configured.

### How does the MCP server handle authentication?

By default, the MCP server binds to `127.0.0.1` and requires no authentication. If you bind it to a non-loopback address using `--bind`, you must set the `MCP_HTTP_AUTH_TOKEN` environment variable. Clients must then include the token as a `Bearer` token in the `Authorization` HTTP header, as enforced by the logic in [`codebase_rag/cli.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/cli.py).