Most findings resolve themselves

Mallcop generated 15 findings this week. 12 were resolved by AI triage. Routine activity by known actors doing known things. 2 were investigated deeper and resolved with cited evidence. 1 was escalated to you with a full investigation report written to the git store.

Every security tool finds things. What you actually want is to not spend your afternoon chasing them. The AI runs each one down, cites its evidence, and either closes the case or hands you a briefing you can act on in under a minute.

What mallcop watches

No AI involved at this stage. Just rules compared against what's normal.

Suspicious behavior

Is someone doing something they shouldn't?

Someone new shows up
A user, bot, or API key that's never been seen before. Most are expected onboarding. Triage resolves those automatically. The ones that aren't get escalated.
new-actor
Somebody got promoted
New admin role, new repo owner, new org-level permissions. High signal. Almost always worth looking at, even when it's legit.
priv-escalation
Someone's hammering the door
A burst of failed logins in a short window. Mallcop checks the pattern and tells you if it's fat fingers or something worse.
auth-failure-burst
Activity at a weird hour
A 9-to-5 account doing things at 3am. The baseline knows whether this person works late sometimes or never has before.
unusual-timing
Way more traffic than normal
Event volume spiked 10x for a source. If there was a deploy, that's probably why. If there wasn't, that's a problem.
volume-anomaly
Someone's poking around new places
A known account touching resources it's never touched before. A developer who's never been near the secrets store suddenly querying it.
unusual-resource-access

Your own mistakes

Did your team leave a door open?

Something got shared with the outside
An external account was granted access to an internal resource for the first time. Cross-account grants, new service accounts, unexpected access expansions.
new-external-access
Logs changed shape
A service updated and its log format shifted. The parser needs to know either way, but tampering looks different from an upgrade.
log-format-drift

Watching the perimeter

Is something messing with mallcop itself, or showing up outside your walls?

Something's trying to mess with the AI
Prompt injection patterns in usernames, commit messages, or resource names. Caught at ingest, flagged, never reaches the LLM.
injection-probe

The pipeline

One command runs the whole thing. mallcop scan connects, detects, triages, investigates, and runs committee consensus end to end, then writes findings and resolutions to your git store. Detection is rule-based and free; the AI cascade runs only on what the detectors flag. mallcop init sets up config first; mallcop detect is available to run the detection phase on its own.

Init

mallcop init
  • Detect connectors
  • Write config
  • One-time setup

Connect

mallcop scan
  • Pull events (GitHub / file)
  • Normalize
  • Store in git

Detect

(within scan)
  • Compare to baseline
  • 17 built-in detectors
  • Generate findings

Investigate

(within scan)
  • AI triage
  • AI investigation
  • Deep panel

Consensus

(within scan)
  • Independent re-votes
  • Any escalate wins
  • Write to git store

Follow a finding through the system

Someone new shows up in your GitHub org.

Detection (no AI)

The new-actor detector fires because this principal isn't in the baseline. Rule-based comparison, no LLM, no cost. Finding generated with severity warn.

Triage agent starts

The triage agent runs the structured triage playbook. The finding, triggering events, and baseline data are pre-loaded into context. The agent enters a reasoning loop with a bounded iteration budget:

# Triage agent reasoning loop

1. Calls check-baseline → actor not known, 0 prior events
2. Calls search-events → finds org invite sent by repo owner 3 hours ago
3. Applies the 4-question test:
   A. Routine? No — first appearance.
   B. Legitimate trigger? Yes — org invite by admin at 14:22 UTC.
   C. Credential theft? No — invite chain is intact, timing matches.
   D. Access expansion? No — read-only collaborator.
4. Resolves with confidence 4, citing invite event ID and timestamp.

Result: resolved — "New collaborator added via org invite
  (event evt-2026-03-11-0042) by admin@org 3h prior. Read-only access."

What if triage can't resolve?

Same finding, but no invite found. Activity at 3am. Unusual IP range. Triage confidence is 2. A hard runtime rule says confidence ≤ 2 means escalate. The agent doesn't guess. It says "I'm not sure" and passes to investigation.

Investigate agent

The investigation agent has more tools (search-findings, baseline-stats, annotate-finding) and a larger iteration budget than triage. It digs into the full event history before deciding.

# Investigation agent deep dive

1. Calls search-events → no invite, no PR, no onboarding workflow
2. Calls check-baseline → actor IP not in any known range
3. Calls search-findings → no prior findings for this actor
4. Applies credential theft test: "If credentials were stolen,
   would this look identical?" → Cannot distinguish.
5. Annotates finding with full reasoning chain
6. Escalates with confidence 2, citing missing provenance

Result: escalated — "No upstream trigger found. 3am activity,
  unknown IP range, no org invite or PR. Recommend: verify identity
  through independent channel."

Output

Findings and resolutions are written to the git-native store you control. One auditable history of every event, finding, and ruling. Escalated findings carry their full investigation notes, so you read reasoning, not just an alert. Wire your own notifier on top of the git store if you want a push to chat or email.

When you rule on a finding (mallcop feedback <id> dismiss --store ./store, or "Silence this" from the hosted mallcop.app chat), that ruling is written back to the store as a suppress directive. The next mallcop scan reads it and stops re-flagging that class of finding. Same loop, whether you run it from the CLI or the browser.

The airlock

Notice the AI never saw the raw GitHub API response. Events are sanitized at ingest: control characters stripped, strings length-capped. User-controlled fields (commit messages, branch names, PR titles) are wrapped in [USER_DATA_BEGIN]...[USER_DATA_END] markers. Tool results are re-sanitized before reaching the LLM.

If someone put "IGNORE PREVIOUS INSTRUCTIONS" in a commit message, it arrives wrapped in markers. The AI sees it as data to analyze, not instructions to follow.

Full Security Model →

What agents can see and do

Agents interact with your data through a controlled set of tools. Each tool has explicit permissions. Triage is read-only by design.

Tool What it tells the agent Available to Permission
check-baseline Is this actor known? Frequency, relationships, typical hours triage, investigate read
read-events Events for this finding, enriched with local time context triage, investigate read
search-events Full-text search across event history — find upstream triggers triage, investigate read
search-findings Historical findings — has this pattern appeared before? investigate read
baseline-stats Statistical summary of baseline for an actor investigate read
annotate-finding Document reasoning before resolving investigate write
resolve-finding Mark finding resolved/escalated with confidence score triage, investigate write*

* Triage's resolve-finding is constrained by policy: it cannot resolve privilege escalation or access boundary findings regardless of model output. Enforced at runtime, not by prompting.

Ask it directly

Beyond the scan, mallcop ships a tool-calling analyst you can question in plain English. mallcop investigate reads your events, findings, baseline, and rules to answer things like "who was added to the org this week?" or "explain this finding," and it cites the evidence behind every answer. On the managed rail the same analyst runs from a web chat console, inside your own GitHub Actions, so your data and inference key never leave your repo.

Playbooks: the decision protocol

Triage is not a vibe check. It's a structured protocol with hard constraints the AI can't override. These are real excerpts from the playbooks that run in production.

Triage: the 4-question test

From the triage agent's playbook:

## Step 3: Analyze

Answer these questions using the data from steps 1-2:

A. Is this action routine for this actor?
"[Actor] has done [action] [N] times. This is [routine/new]."

B. Is there a legitimate trigger?
"Events show [trigger/no trigger]: [detail]."

C. Could a stolen credential produce this exact pattern?
"[Yes/No] because [specific factor — IP/location, timing, user-agent]."

D. Does this expand access or privileges?
"[Yes/No]."

## Step 4: Decide

- If A=routine AND B=trigger AND C=distinguishable AND D=no → RESOLVE
- Privilege changes → always ESCALATE (non-negotiable)
- Log format drift → always ESCALATE
- Resolution requires positive evidence — "actor is known" alone is not enough
- Otherwise → ESCALATE

Investigation: pre-resolution checklist

From the investigation agent's playbook:

## Pre-Resolution Checklist

Before calling resolve-finding — whether resolving OR escalating —
run these 5 checks. They apply in both directions.

1. EVIDENCE — Am I citing specific fields, timestamps, or baseline
   entries? If I can't point to it, I'm guessing. This applies to
   escalations too: cite what's anomalous, not just "it looks wrong."

2. ADVERSARY — Could an attacker produce this exact pattern? What
   would distinguish legitimate from compromised? Automation names,
   user-agent strings, and correlation IDs can all be spoofed.

3. DISCONFIRM — What evidence would contradict my conclusion? Did I
   check for it, or just not look? If resolving, did I check for
   anomalous signals I might be overlooking? If escalating, did I
   check whether the baseline explains the activity?

4. BOUNDARY — Does this action expand who or what has access to the
   environment? If yes, treat as privilege-level.

5. BLAST RADIUS — If I'm wrong, what's the worst case? A false
   escalation wastes analyst time. A missed breach loses the org.

How the investigation agent reasons

The investigation agent works from structured domain reasoning baked into its playbook. Here's the kind of analysis it applies.

Privilege analysis: Grant vs. Use vs. Escalation

These are three distinct events that often get conflated. A grant is
when an actor receives a permission (role attachment, policy change, group
membership). A use is when the actor exercises that permission.
Escalation is when the result exceeds what was explicitly intended — an
actor converts a limited permission into broader access, often by chaining
grants across multiple resources or principals.

Seeing a grant event in isolation tells you almost nothing. The question is
whether the use that followed was proportionate to the grant, and whether
the grant itself was within the actor's pre-existing authority.

Key check: did the actor who issued the grant have the authority to do so?
An actor who can grant a permission they do not themselves hold is a classic
privilege escalation pattern.

GitHub security: the known actor trap

The "known actor" trap: GitHub admins are known actors by definition —
they appear frequently in the audit log. The credential theft test must
focus on behavioral deviation (new action types, new targets, new timing),
not on whether the actor is known.

This domain reasoning is built into the cascade. There is no plugin to install or configure. The same playbooks run on every scan, and any user-controlled text the agent reads is sanitized at the airlock first (see the security model).

CLI reference · Security model

Committee consensus: the accuracy gate

The headline differentiator: mallcop doesn't trust a single AI verdict to close a case.

How a finding gets resolved

Layer What it is AI involved?
Hard constraints Code rules — priv-escalation, log drift, injection always escalate No
Learned rules Your confirmed rulings become deterministic rules the next scan reads No
AI triage Quick investigation, resolve the obvious by the 4-question test Yes
AI investigation Deeper dig with more tools and iterations when triage can't be sure Yes
Committee consensus Independent re-votes on anything it would resolve benign Yes
Escalated to human Any single escalate vote wins — the finding comes to you

Any single escalate vote wins

When the cascade would resolve a finding as benign, mallcop doesn't just accept the answer. It runs independent re-votes on that finding. If any one of them votes to escalate, the finding goes to a human. Closing a case takes unanimity, so a single dissent is enough to keep it open. This is the mechanism that catches the cases a single AI pass would get wrong.

Proven live

mallcop scan has run against a real GitHub org, pulled real events, and flagged a new actor (collaborator_added). The consensus cascade resolved it benign with cited provenance: "established relationship via onboarding contributions; no role grants." A real connector, real events, and the reasoning behind the verdict, all written to the git store.

It closes its own gaps

The consensus cascade decides what to do with the findings it has. A second loop safely grows what mallcop can find in the first place.

Every scan that force-escalates an action it couldn't map, or misses something it should have caught, becomes feedstock. Mallcop mines it (mallcop collect), proposes a narrowly-scoped fix, and puts that fix through a $0, no-inference gate (mallcop validate-proposal) before it lands. Safe config widens apply automatically; a brand-new authored detector only ever lands after a human reviews it. The gate freezes the committee itself. The loop can widen what mallcop sees, but it can never edit how mallcop votes.

Mallcop fixes its own gaps two ways, and both leave proof behind:

A rule change: safe by default
A small rule tweak that can only make mallcop more watchful, never less. It needs no AI, so it is free and you can prove it worked offline, like the example below.
A new detector: the escape hatch
When a rule tweak isn't enough, mallcop writes a brand-new detector in a locked sandbox on a spend-capped key. It still only hands a human a patch to review. Authored code is never merged on its own.
Both are gated the same way
Before either lands, the change must not touch protected code, must still build, must break nothing on the test set, and must close at least one real gap. Run the check for free, on donuts, or with your own key.

A real example, for free. Mallcop grades its own detection against a set of real scenarios, and one kept slipping past: a genuine AWS privilege grant (PowerUserAccess) the rules missed, because the name has no obvious word like "admin." One added rule fixes it, and mallcop checks on its own that the near-identical but harmless read-only version still doesn't cry wolf.

$ mallcop exam-detect   # grade detection against real scenarios
FAIL PE-08-aws-poweruser-grant    must_fire: priv-escalation    emitted: (none)   # the blind spot: missed it
exam-detect: 17 labeled (13 passed, 4 failed), 41 unlabeled (skipped)

$ mallcop exam-detect --tuning detectors/tuning.yaml   # after one added rule
PASS PE-08-aws-poweruser-grant    must_fire: priv-escalation    emitted: priv-escalation   # now caught
exam-detect: 17 labeled (14 passed, 3 failed), 41 unlabeled (skipped)

This is not a diagram. Mallcop recently wrote a brand-new detector end to end: opencode (GLM-5.2) authored it in a sandbox, it passed every check (it caught one more real case and broke nothing), and a human opened it as pull request #157. Never merged on its own.

Scan

force-escalate
  • Record the unmapped

Collect

surface the gap
  • Mine the git store

Propose

add-only
  • Widen, or author a detector

Gate

validate-proposal
  • No regression, $0

Route

overlay / review
  • Code is human-reviewed
How mallcop learns →

Where it watches

File and GitHub are built into the core binary. Six cloud sources ship today as standalone binaries in the mallcop-connectors repo. They emit event JSONL you pipe into mallcop scan --events. The list below is a starting point, not a fixed catalog: a connector is AI-authored code, so a source that isn't here yet is a request away, not a roadmap item you wait on.

GitHub · live
Org events via GitHub App auth. --connector github --github-org <org>
  • Org membership changes
  • Collaborator added / removed
  • Repo permission changes
  • Deploy key management
Event files · live
Read normalized events from a JSONL file or stdin. --events <file.jsonl>
  • Bring your own exported logs
  • Pipe events from any source
  • Replay history for testing

Cloud connectors (mallcop-connectors repo)

Shipped standalone binaries. Each emits event JSONL for mallcop scan --events.

AWS · shipped
CloudTrail audit log: IAM, S3, Lambda, security groups.
Azure · shipped
Activity Log: role assignments, Key Vault access, resource changes.
GCP · shipped
Cloud Logging: admin activity, IAM, data access.
Microsoft 365 · shipped
Management Activity API: sign-ins, admin actions, mail rules.
GitHub Audit Log · shipped
Org audit log stream: the standalone connector counterpart to the built-in GitHub source.
Okta · shipped
System Log: authentications, MFA, admin and policy changes.

On the roadmap

Not yet ported. Planned standalone connectors.

Container logs · not yet ported
Parsed container/application log streams.
Vercel · not yet ported
Deployment and access events.
Supabase · not yet ported
Auth and database access events.

BYOK or managed: both live

Bring your own inference key and mallcop costs you $0. You pay only your model provider. Or skip key management: managed inference on the donut rail is live today, pay-as-you-go or by monthly subscription.

BYOK — live Managed — live
Price$0 + your API costsDonut packs from $5, or $4.99/mo+
ModelsYour choice (any API key)Optimized routing by sovereignty tier
Key managementYou manage API keysWe handle it
ConnectorsFile + GitHub built in; AWS, Azure, GCP, M365, Okta via mallcop-connectorsSame
EventsUnlimitedUnlimited
StatusAvailable nowCheckout + metering live now (Polar)
See Pricing →
Get Started →    Security Model