Overview

mallcop init scaffolds mallcop.yaml — the one file mallcop reads. Run mallcop scan with no flags and it discovers that file: connector, store path, inference mode, detector config, learning autonomy, sovereignty, and budgets all resolve from it. Flags on mallcop scan and a handful of environment variables (inference endpoint + key, GitHub App auth) still work and take precedence over the file for any value you set explicitly — the flag-only path from earlier mallcop versions hasn't gone away, it's just no longer the primary one. mallcop config prints the effective merged configuration a scan would pick up right now. Before mallcop init has run (no mallcop.yaml discovered), it falls back to built-in defaults:

$ mallcop config
mallcop effective config

  Config file:          (none found — built-in defaults; run `mallcop init` to create mallcop.yaml)
  Version:              1

  Inference mode:       offline
  Inference URL (MALLCOP_INFERENCE_URL):   (unset — scans force-escalate every finding, the fail-safe)
  Inference key (MALLCOP_API_KEY):       (unset)
  Model (MALLCOP_MODEL):                 mallcop-default

  Store path:           ./store
  Connectors:           1 configured
    - local-events     kind=file
  Builtin detectors:    enabled=true disabled=[]
  Sidecar detectors:    dir=./detectors/bin
  Learning dir:         detectors (autonomy=non, enforce_pin=false)
  Sovereignty:          tier=open contribute_back=false
  Budgets:              max_findings=25 scan_timeout=10m selfext_spend_cap_usd=25

After mallcop init, the same command reports Config file: <dir>/mallcop.yaml instead — the rest of the output reflects whatever that file (plus any environment overrides) resolves to. See Quick Start § Initialize for the full mallcop init transcript.

The full generated file

Here is the exact mallcop.yaml that mallcop init writes on a fresh machine — every top-level key mallcop reads, at its default value. Nothing is hidden or summarized: add a connector or flip the autonomy dial and only the value you touched changes.

# mallcop.yaml — the one file mallcop reads. Generated by `mallcop init`.
# Run the scan with no flags:  mallcop scan
# Add a source by editing connectors: below.
version: 1
inference:
  mode: offline
  endpoint: ""
  key_env: MALLCOP_API_KEY
  model: mallcop-default
store:
  path: ./store
  baseline: ""
connectors:
  - kind: file
    id: local-events
    path: ./events.jsonl
    org: ""
    source: ""
    args: []
    since: ""
    env: []
    binary: ""
detectors:
  builtin:
    enabled: true
    disable: []
  sidecars:
    dir: ./detectors/bin
learning:
  dir: detectors
  autonomy: non
  enforce_pin: false
sovereignty:
  tier: open
  contribute_back: false
budgets:
  max_findings: 25
  scan_timeout: 10m
  selfext_spend_cap_usd: 25

This exact file is checked byte-for-byte against the real mallcop init output in CI (test/docdemo) — it cannot silently drift from what the binary actually writes.

Writing config: config set

mallcop config set connector and mallcop config set autonomy write strict-validated changes directly into mallcop.yaml — atomic writes, no inline secrets, no duplicate ids. These are the shared primitives any driver of a config change (this CLI, a chat surface) calls; they're what makes the reconfiguration dial in self-improvement a config-file operation rather than hand-editing YAML.

$ mallcop config set connector --kind=github --id=my-org --org=my-org
mallcop config set connector: added "my-org" (kind=github) to <dir>/mallcop.yaml — takes effect on the next `mallcop scan`

$ mallcop config set autonomy semi
mallcop config set autonomy: learning.autonomy=semi in <dir>/mallcop.yaml

config set connector rejects a duplicate id, an unknown --kind, or an inline secret passed via --env. config set autonomy accepts exactly non, semi, or fully — the self-extension autonomy dial (see Self-Improvement).

Inference (environment variables)

mallcop's AI cascade runs against whatever inference endpoint your key points at. Bring your own key and mallcop costs you nothing — you pay only your model provider. Point it at Anthropic, OpenAI, Bedrock, any OpenAI-compatible endpoint, or the mallcop managed endpoint.

export MALLCOP_INFERENCE_URL=https://api.anthropic.com   # or https://api.mallcop.app for managed
export MALLCOP_API_KEY=sk-...                          # your vendor key, or a mallcop-sk-* key
export MALLCOP_MODEL=mallcop-default                    # optional model override

The --base-url flag on mallcop scan overrides $MALLCOP_INFERENCE_URL per run. With no inference URL set at all, mallcop still runs and every finding force-escalates to you — the documented fail-safe, no key required.

GitHub App auth

The built-in GitHub connector reads org events through a GitHub App. Install the app on your org with read access to organization administration and audit events, then export its credentials:

export GITHUB_APP_ID=...
export GITHUB_APP_PRIVATE_KEY=...       # the app's private key (PEM)
export GITHUB_INSTALLATION_ID=...

Choosing a connector

Two connectors are built in. Add one to mallcop.yaml with mallcop config set connector (shown above) so a bare mallcop scan picks it up, or pass it as a flag for a one-off run:

# file connector (default): read normalized events from a JSONL file or stdin
mallcop scan --events events.jsonl --store ./store

# github connector: pull org events via the GitHub App above
mallcop scan --connector github --github-org my-org --store ./store

Cloud sources (AWS, Azure, GCP, M365, Okta) are separate binaries that emit JSONL you feed into the file connector — see the Connector Catalog for the two-step flow and the roadmap.

The git store

Every scan writes findings, resolutions, events, and directives to the git-native store you pass with --store (required). It is a plain git repo — git log is your audit trail. mallcop scan runs git init on the store directory the first time if it isn't already a repo.

mallcop scan --connector github --github-org my-org --store ./store
mallcop status --store ./store     # counts by source and status

Detector tuning

Detection is baseline-relative and learns from your activity automatically — no manual configuration required. You can optionally widen coverage with a widen-only tuning overlay passed via --tuning. The overlay can only make mallcop more watchful; narrowing is inexpressible (see Self-Improvement).

# detectors/tuning.yaml — widen-only extra_* knobs
priv_escalation:
  extra_elevated_keywords:
    - poweruser

mallcop scan --events events.jsonl --store ./store --tuning detectors/tuning.yaml

Your rulings on findings (mallcop feedback) are written back to the store as suppress directives the next scan honors — there is no separate self-improvement config block to manage.

Circuit breaker

A volume circuit breaker protects you from runaway spend during a finding storm. Pass --max-findings: a scan that produces more findings than the ceiling force-escalates a single critical meta-finding to a human instead of running the AI cascade on every one.

mallcop scan --connector github --github-org my-org --store ./store --max-findings 25

Default ceiling is 25. Events are always collected and stored; only the AI investigation is short-circuited when the breaker trips.