GCA · CORE CONCEPTS

    Privacy Boundary

    The non-negotiable principle: your code never leaves your machine.

    The Privacy Boundary: Code Never Leaves the Machine

    Gadriel Code is bound to one non-negotiable principle:

    All scanning, graph construction, and secret detection runs locally. The only outbound calls are explicitly enumerated, configurable, and minimal. Hard requirement — non-negotiable.

    This is what distinguishes Gadriel from every SaaS code-scanning service. Snyk, Semgrep Cloud, GitHub Advanced Security, and SonarCloud all upload code (or build artifacts, or graph data) to a vendor-controlled service — they have to, because their analysis runs there. Gadriel's analysis runs on your machine, so it does not.

    Related: AI-coding integration · Tiered analysis model


    1. What must never leave the machine

    Without explicit, per-command, user opt-in, the CLI must not send any of:

    • Source code in any form — raw, tokenized, AST, or embedding
    • File paths or filenames from the scanned project
    • Commit messages, branch names, or git history content
    • Finding bodies, finding code-context fields, or finding metadata
    • Pillar scores, overall scores, SBOM contents
    • Dependency lockfile contents
    • Container image layer contents
    • Configuration file contents (.env, *.tf, *.yaml, …)
    • The .security/audit-log.jsonl recording AI-coding-agent round-trips

    Anything not in the closed list below is forbidden by default.


    2. The closed list — the only permitted outbound calls

    Every outbound call the CLI makes falls into exactly one of these categories. Every endpoint is configurable via .gadriel.toml / ~/.gadriel/config.toml and an environment-variable override.

    #PurposeDefault endpointWhat gets sentWhat never gets sent
    1Portal auth & licensing (mandatory at startup)app.gadriel.aiCredentials at login; just the refresh token on refresh. JWT cached at ~/.gadriel/auth/token.jwt (mode 0600).No code. No findings. No paths. No SBOM.
    2OSV snapshot sync (gadriel code policies update --osv)osv-vulnerabilities.storage.googleapis.com (Google's OSV CDN)A single bulk-snapshot GET of the whole OSV database.No per-finding query. No PURLs. No dependency names. No code.
    3Optional Claude reasoning (Layer-4 confirmation, gadriel code fix)api.anthropic.comFor the finding under review only: a redacted excerpt of ~5 graph-neighbor code chunks (typically 50–200 lines), graph metadata (numbers, not the graph), the finding body, and a 256-bit codebase fingerprint. Uses the runner's own key (e.g. ANTHROPIC_API_KEY), not the portal token.The full file. Other files. The full graph. Git history. The SBOM. Other findings. Repo/branch names or any path outside the neighbor chunks.
    4Private policy-repo fetch (gadriel code policies update)github.com/Gadriel-ai/gadriel-code-policiesAn auth-gated git fetch/tarball pull of the GVL rule corpus. Read-only. License token sent only as the Authorization: Bearer header, not the body.No code. No findings. No telemetry.
    5Opt-in metrics (--telemetry-enabled, default OFF)sync.gadriel.aiAggregate counts (findings-by-pillar, intercept-layer counts), durations, CLI version, OS string.Code. Paths. Finding bodies. Repo/branch names. Anything PII or workspace-identifying.

    Row 1 is mandatory but is a license gate, not a content channel — only auth material crosses the wire. Rows 3 and 5 are opt-in. Everything else — source, paths, findings, scores, SBOM, and the local audit log — stays on the machine.

    The OSV mirror is bulk-sync, not per-finding query

    A naive integration would query OSV once per dependency, leaking your entire dependency tree. Gadriel forbids that pattern:

    gadriel code scan  ──▶ reads lockfiles locally
                       ──▶ queries the LOCAL OSV snapshot (~/.cache/gadriel/osv-snapshot/)
                           └─ no network call
    
    gadriel code policies update --osv
                       ──▶ one wholesale GET of the entire DB blob
                           └─ request body has no PURLs, no dep names, no code
    

    A network observer sees one large opaque transfer to a mirror — they learn nothing about which dependencies you have. The snapshot is signature-verified locally before it is trusted.


    3. The AI coding agent runs locally — it is not a SaaS endpoint

    A common misreading: "the assistant makes an LLM call, so my code goes to the cloud." That is wrong on two counts:

    1. The AI coding agent runs in your environment. Claude Code, Cursor, Copilot, Codex, Aider — all local. None is a SaaS service you upload your repository to.
    2. The LLM call those agents make is the agent's call, gated by your keys, on your machine. Gadriel does not proxy it. Gadriel makes its own LLM call only at the carve-out points in row 3, sending only the redacted contract — never raw source.

    The find→fix→re-validate loop, end to end, is entirely local except for the agent's own (already-existing) LLM call:

    [user machine — all local]
      developer / AI agent writes code
            ↓
      gadriel scans (no outbound)
            ↓
      finding pushed to the agent (local MCP over stdio, or file-watch)
            ↓
      agent applies fix (its own local LLM call, its own credentials — NOT proxied)
            ↓
      gadriel re-scans (no outbound)
            ↓
      .security/audit-log.jsonl written (LOCAL ONLY — never sent anywhere)
    

    4. How Tier 2 preserves privacy

    Tier 2 (see tiers.md) reaches the semantic long-tail by borrowing the LLM the host assistant is already running — over the local MCP submit_ai_findings callback. Crucially, this creates no new egress relationship: Gadriel does not open its own connection to send code for Tier-2 analysis. It hands the semantic question to the assistant already in the loop, which uses its existing, user-authenticated LLM connection.

    Tier 2 is also off by default and privacy-gated:

    [tier2]
    enabled = false # default OFF
    source_consent = false # default OFF — required before any source excerpt is sent

    source_consent defaults false and never auto-enables. It corresponds to the persisted --send-source consent — without it, no source excerpt is placed in a Tier-2 request. With Tier 2 off, the MCP server emits tier2_request: null and no session is ever opened.


    5. Build-time enforcement

    The boundary is not just policy — it is enforced by cargo-deny in CI. crates/code-security/deny.toml is a positive allowlist naming the only crates permitted to depend on an HTTP/network client (the auth, OSV-sync, policy-fetch, and Claude-reasoning crates). Any new dependency that pulls in reqwest/hyper/tonic/etc. into a scanner crate fails cargo deny check bans before the PR can merge:

    cargo deny --all-features check --config crates/code-security/deny.toml bans

    The check is scoped to the code-security member so it is isolated from the Preflight runtime layer, which legitimately uses HTTP clients (a different deployment model, a different boundary). Local persistence (the rvf store at ~/.gadriel/store/ per-machine and ./.security/store/ per-repo) is signature-verified at rest and never crosses the boundary — it stays local.


    6. Running fully air-gapped

    Gadriel works with no network at all. Government, defence, and security-sensitive customers run it this way.

    1. Force offline mode:

      export PREFLIGHT_OFFLINE=1
    2. Pre-warm the OSV snapshot — sync it once on a connected machine (or point osv_mirror_url / GADRIEL_OSV_MIRROR_URL at an internal mirror), then carry ~/.cache/gadriel/osv-snapshot/ across. SCA queries are local file reads against that snapshot, so no per-finding call is ever made.

    3. Offline license + policies: air-gapped customers receive an offline license grant and a GVL policy snapshot bundle from Gadriel support, rather than the online portal refresh (row 1) and git fetch policy update (row 4).

    With those in place, all six scan types, the graph build, secret detection, scoring, the SBOM, and the report all run with zero outbound traffic. Tier 2 is simply absent (no host agent / no LLM), and analysis cleanly degrades to Tier 0 — the deterministic gate is unchanged.


    Summary

    • Analysis is local. Only the five enumerated calls in §2 ever go out; rows 3 and 5 are opt-in, and none of them carry raw source.
    • The AI agent's LLM call is its own, on your machine, not proxied by Gadriel.
    • Tier 2 adds no new egress — it reuses the host assistant's existing LLM connection, is off by default, and is source-consent gated.
    • The boundary is enforced at build time by cargo-deny, and the whole product can run fully air-gapped.