Skip to content

Security model

CodeSynapse reads untrusted repositories and serves their contents to LLMs. Each boundary below has a rule that must not be loosened.

Trust boundaries

flowchart LR
    subgraph untrusted[Untrusted input]
        repo[Repository contents]
        agentText[Agent / client text]
        modelOut[Model output]
        remote[Git remotes and forge APIs]
        reqs[HTTP requests]
    end
    subgraph guards[Guards]
        gitx[gitx: hardened git runner,<br/>ValidateRemote, ValidateRev]
        fs[insideSources confinement,<br/>no symlink following]
        redact[redact.Summary / redact.Free]
        params[Parameterised queries,<br/>allowlisted labels]
        parse[Defensive parsing,<br/>enum and path validation]
        auth[Auth, tenant scope, limits]
    end
    subgraph trusted[Core]
        graph[(Graph store)]
        llm[llm registry<br/>local vs cloud]
    end

    remote --> gitx --> graph
    repo --> fs --> redact --> graph
    agentText --> redact
    agentText --> params --> graph
    modelOut --> parse --> redact
    reqs --> auth --> params
    graph --> llm

Rules by area

Area Rule
Cypher Values only through parameters. Labels, types, depth and ORDER BY only from model allowlists, store.ClampDepth or metricCypher
Gremlin Scripts are constants; values, labels and IDs travel in bindings. Always password-protected, never published, no raw-Gremlin tool. Tenant data traversals start at has('tenant_id', ...).has('ns', ...)
Vectors Filter templates with exprParams; every item has a tenant; vector.Isolate drops foreign hits
git Only through gitx.Run: no fsmonitor, hooks, external diff or textconv; ignores global config; no shell
go list CGO_ENABLED=0, GOTOOLCHAIN=local, GOPROXY=off unless opted in
Filesystem API ingest confined to CODESYNAPSE_SOURCES_ROOT; docs walking never follows symlinks
Graph content No source bodies or YAML values; hashes, signatures, key paths, line ranges, redacted summaries
Remote git gitx.Clone / FetchCheckout / RemoteBranches; https only, no credentials in URLs, no private hosts unless CODESYNAPSE_ALLOW_PRIVATE_GIT; tokens via GIT_CONFIG_* env, never argv
Forges One client per host, no redirects; clone URLs built from validated host, allowlisted login and validRepoSegment name, never taken from API answers
Dependency data Only ecosystem, name and version leave the machine
MCP tools Read-only (ReadOnlyHint), except review tools and memory tools, which write only to their own stores. No raw-Cypher tool (read transactions still allow LOAD CSV)
LLM calls Only through services/api/internal/llm; prompts pass redact.Free; system prompts constant for cache; no source to non-local providers except a diff the user asked to review; keys from api_key_env
Network services Refuse non-loopback bind without bearer token (app.ErrTokenRequired)
Web proxy Rejects cross-origin mutations; forwards only cs_session and X-CodeSynapse-Org, never the API token with accounts on; fails closed on unreadable mode
Proxies Client address from X-Forwarded-For only from CODESYNAPSE_TRUSTED_PROXIES
Secrets From the environment, never flags or logs

Telemetry cardinality

Metrics carry only bounded labels. Prompt text, queries, paths, repository names and user IDs never become labels; they belong in logs.

flowchart LR
    call[Tool call / LLM call] --> label{Label source}
    label --> t[tenant_id: authenticated caller,<br/>must look like org ID]
    label --> tool[tool name: only after server accepted the call]
    label --> prov[provider, model: from configuration]
    label --> op[operation: fixed set]
    t --> push
    tool --> push
    prov --> push
    op --> push[Push every 15 s to VictoriaMetrics<br/>series capped per metric]
    push --> q[GET /api/v1/stats/series<br/>fixed MetricsQL, extra_filters = caller's orgs]

The database enforces the tenant scope; cross-tenant panels are platform-only.

Destructive actions

Delete repository and delete review are confirmed in the UI. The one automatic removal is reapIfGone: a cloned repository is removed only after the provider API answers a clean 404 three scheduled syncs in a row. Deleting cloned files is limited to a single directory inside the workspace, and mounted sources are never deleted.

stateDiagram-v2
    [*] --> Healthy
    Healthy --> Suspect1: sync gets clean 404
    Suspect1 --> Suspect2: clean 404 again
    Suspect2 --> Reaped: clean 404 third time
    Suspect1 --> Healthy: any other answer
    Suspect2 --> Healthy: any other answer
    Reaped --> [*]: graph, schedule and clone removed

Limits

store.MaxLimit, store.MaxDepth, request body caps and query timeouts. Memory is capped at 10,000 facts per repository; scans queue at most 10 clones each and back off an hour after a failure.