What BrassCoders Sends to Its Servers (And What It Doesn't)

BrassCoders scans run entirely on your machine by default. The Paid plan adds one network call to our gateway with already-redacted findings, never raw source code. Here is every byte that leaves your machine.

Copper Sun Brass Team · · 8 min read
privacyoss-coreengineering

BrassCoders scans run entirely on your machine by default. The OSS core makes zero outbound network calls during a scan. The Paid plan adds exactly one network step: a request to our hosted gateway carrying already-redacted scanner findings plus a short project signature derived from your README and manifest. Source code never travels. This post documents every byte that leaves your machine, where it goes, and how to verify it yourself.

What Leaves Your Machine in an OSS Scan

BrassCoders’s OSS core makes zero outbound network calls during a scan. The CLI reads files from your project root, runs 12 bundled scanners locally, and writes YAML output to .brass/ inside that project. BrassCoders is published on PyPI as brasscoders under Apache 2.0.

The 12 scanners are local processes that ship as Python dependencies alongside BrassCoders: Bandit and Pylint for Python static analysis, Pyre and Pysa (Meta’s taint analyzer) for taint propagation, Semgrep and ast-grep for pattern matching, and detect-secrets (Yelp’s entropy-based credential scanner) for secret discovery.

BrassCoders ships six additional custom scanners to cover AI-pattern detection, privacy and PII matching, secret patterns, performance hints, content moderation, and JavaScript/TypeScript analysis. None of these tools make network calls during analysis. The Bandit binary doesn’t phone home; you can verify this against PyCQA’s source. Pylint doesn’t either. BrassCoders doesn’t add a network layer on top.

One narrow exception exists: the optional package-hallucination check, enabled with --check-package-hallucination. When you opt in, BrassCoders issues HTTPS GETs to PyPI, npm, or pkg.go.dev to confirm that imports in your code reference packages that actually exist (catching AI-generated code that imports invented libraries). The package name is the only payload transmitted. The flag defaults to off, and passing --offline forces it off even if you’ve opted in elsewhere.

If you’re scanning in an air-gapped environment, an unlicensed install is the natural fit. No license validation. No gateway calls. No exceptions to manage.

What Leaves Your Machine in a Paid-Plan Scan

BrassCoders Paid sends two payloads to our hosted gateway per scan: redacted scanner findings and a short project signature of at most 7500 characters. Source code is never transmitted, not as snippets, not as embeddings, not as anything else.

The findings payload is the same YAML that gets written to .brass/ on disk, with one important property: it has already been redacted at the source before transmission. For any finding flagged as PRIVACY or hardcoded-credential, sensitive matched values are masked at the scanner (a credit card number becomes 4111****1111), the code_snippet field is cleared, and the YAML writer strips a known set of privacy-sensitive metadata keys before serialization. The redaction runs whether the YAML is being written locally or sent to the gateway. There is no separate “sanitized for transmission” path; the same redacted payload that goes to the gateway also lands on your disk.

The project signature is a short text string derived from four sources: your top-level README, your manifest file (pyproject.toml, package.json, Cargo.toml, or equivalent), your entrypoint file, and the names (not contents) of files in your project root. Combined, these reach at most 7500 characters. The signature lets the gateway rank findings by relevance to your project, so a SQL-injection finding in a CLI tool gets weighted differently than the same finding in a web service.

What the gateway does with the payloads: it runs an embedding-based semantic deduplication pass to collapse near-duplicate findings, reranks the survivors against the project signature, and returns the deduplicated list. Our embedding provider operates under a zero-data-retention agreement. Neither BrassCoders nor the embedding provider stores the payloads after the request completes.

How License Validation Works

BrassCoders license activation sends three fields to LemonSqueezy: your license key, a machine identifier, and your customer email. These calls never include scanner findings or source code. After the initial activation, the CLI re-validates with LemonSqueezy at most once per week per machine.

LemonSqueezy is the third-party that handles billing, license issuance, subscription management, and customer email delivery. They’re a PCI-compliant payment processor, and BrassCoders treats them as the source of truth for whether a given license is active. The validation cadence (weekly per machine) is a deliberate trade-off: more frequent validation would burn network traffic for no value, while less frequent validation would let cancellations linger too long before the gateway notices.

The activation flow runs once per machine when you first run brasscoders activate <license-key>. BrassCoders generates a machine identifier locally, sends {license_key, instance_name, machine_id, email} to LemonSqueezy’s License API, and stores the returned instance ID. From that point on, the per-week revalidation just sends {license_key, instance_id} and checks the response status.

If you cancel your subscription through the LemonSqueezy hosted portal (reachable via brasscoders portal), the gateway picks up the cancellation within seconds via webhook. The CLI will continue to function with a cached license state until the next weekly revalidation discovers the cancellation, after which scans fall back to heuristic-only enrichment without contacting the gateway.

The Two-Boundary Redaction Model

BrassCoders redacts sensitive data at two boundaries: once at the scanner that produces the finding, and again at the YAML writer that serializes it. If a scanner misses a sensitive value at the source, the writer’s allowlist strips suspect metadata keys before anything is written to disk or sent to the gateway.

The scanner boundary handles the obvious cases. The hardcoded-credential detector, for example, replaces literal values inside string quotes with <REDACTED> before persisting the line. The secret scanner records only the secret’s type and a short hash for deduplication; the secret value itself never makes it into a BrassCoders-owned object after the initial match. Credit card numbers get masked to the standard 4111****1111 pattern. Email addresses, IPs, and other PII matches get masked at the same point.

The YAML writer boundary is the belt-and-suspenders layer. For any finding whose type is PRIVACY or whose detector is on the secret-leak allowlist (currently auth_pattern_analyzer and bandit), the writer strips matched_text, code_snippet, context_line, raw_match, and context from the serialized output. This catches anything the scanner forgot.

The same code path runs whether the output is being written to .brass/ on disk or sent to the gateway. There is no “this version is for our servers, that version is for the local file” split. Whatever you can read in .brass/ai_instructions.yaml is exactly what the gateway saw.

What’s in .brass/ (And Why It’s Yours)

BrassCoders writes seven files to .brass/ inside your project root: ai_instructions.yaml, detailed_analysis.yaml, file_intelligence.yaml, security_report.yaml, statistics.yaml, privacy_analysis.yaml (only when PII findings exist), and brass.log. The directory is created with permissions 0700; YAML files are written with 0600. On Windows, BrassCoders relies on filesystem ACLs to achieve the same owner-only access.

These files contain redacted findings, the project signature, and aggregate statistics. They never leave your machine unless you copy them yourself; nothing in BrassCoders uploads or syncs them. If you check .brass/ into version control, that’s your choice (and you might want to think twice — the redaction is good but not bulletproof if your code review process treats finding text as low-sensitivity).

For convenience, BrassCoders also writes a .gitignore entry in .brass/.gitignore that excludes everything in the directory. If you want the files committed, override that explicitly.

The output is yours, locally, with no telemetry. BrassCoders doesn’t track which findings you read. BrassCoders doesn’t know when you ran the scan. BrassCoders doesn’t know whether you addressed the issues. The on-disk YAML is the only artifact of the scan, and it lives where you put your code.

How to Verify Yourself

BrassCoders’s network behavior is auditable three ways: read the source on PyPI, pass --offline to any scan, or run a scan under a network packet capture tool. All three produce evidence anyone can reproduce; the rest of this section walks through each.

The source-reading path is the cheapest. BrassCoders is published on PyPI as brasscoders; the package is unpackable with pip download brasscoders followed by tar tf brass_ai_coders-*.tar.gz to inspect the contents. The single file responsible for all outbound traffic is cli/src/brass/enrichment/client.py. Search it for requests.post, urlopen, or fetch and you’ll find exactly one outbound call, gated by license state.

The --offline flag is the runtime assertion. Pass it to any brasscoders scan invocation and BrassCoders will skip the gateway entirely, fall back to heuristic-only enrichment, and exit normally. Combined with a network monitor, this is the cleanest way to confirm the OSS-only behavior on your specific system.

The packet-capture path is the most rigorous. Start sudo tcpdump -i any -n host coppersun.dev or host api.lemonsqueezy.com in one terminal, run brasscoders --offline scan /path/to/your/project in another, and check the tcpdump output. You should see zero packets to either host. If you do see traffic, file an issue at brass@coppersuncreative.com — that would be a launch-blocking bug.


BrassCoders is free to install and run on any code you can read. Install with pipx install brasscoders, scan with brasscoders scan /path/to/project, and read the YAML in .brass/. The Paid plan at $12/month adds the AI-powered enrichment pipeline; cancel any time via brasscoders portal. See the full data-handling docs at coppersun.dev/legal/privacy.

For the broader context on AI code review failure modes, see AI Code Review: The Practical Guide for 2026.

Frequently Asked Questions

Does BrassCoders send my source code to your servers?

No. Source code never leaves your machine. The OSS core makes zero outbound calls during scans. The Paid plan sends already-redacted scanner findings and a short project signature derived from your README and manifest, but it never transmits raw source code in any form.

What does the Paid plan actually send through the gateway?

Two payloads per scan: redacted scanner findings (title, description, severity, file path, line number) and a project signature of at most 7500 characters built from your README, manifest, entrypoint, and top-level filenames. The gateway uses these to run semantic deduplication and reranking before returning results.

Can I run BrassCoders with the network fully blocked?

Yes. Pass --offline to any scan to force zero network calls regardless of license state. With an unlicensed OSS install, --offline is effectively the default behavior. With a Paid license, --offline skips the gateway and falls back to heuristic-only enrichment for that scan.

What does LemonSqueezy receive when I activate a license?

Three fields: your license key, a machine identifier, and your customer email. These activation and validation calls never include scanner findings or source code. After initial activation, the CLI re-validates with LemonSqueezy at most once per week per machine.

How can I prove BrassCoders is not phoning home?

Run brasscoders --offline scan with a network monitor like Little Snitch, mitmproxy, or tcpdump attached. You should see zero outbound connections from the brasscoders process. The OSS source is published on PyPI as brasscoders and is readable; the network client lives at cli/src/brass/enrichment/client.py.