Shadow AI

Every other view in Prismor answers what did my agents do. This one answers the inverse: what is running that Prismor never saw.

An agent installed without hooks, an MCP server wired straight into Cursor, an API key sitting in a .env — none of them appear in Sessions or Activity, because nothing about them ever reaches Prismor. Shadow AI finds them by inspecting the machine rather than by observing behaviour, then tells you which ones a developer can fix in one command.


The three surfaces

SurfaceGoverned when
Coding agentsPrismor's dispatcher is wired into the agent's hook config
MCP serversthe server is routed through prismor mcp-gateway
Provider keysthe credential is registered with Cloak

Anything present but not governed is shadow. Each device reports its own inventory; the console rolls them into one fleet view.


Finding it

On any machine:

prismor discover                 # agents, MCP servers and keys, plus a coverage score
prismor discover mcp             # one section at a time
prismor discover --json          # machine-readable
prismor discover --fail-on-shadow   # exit 1 in CI
  AGENTS
    governed               Claude Code  (enforce)
    SHADOW                 Gemini CLI (Google)
    no coverage            Warp (Agent Mode)  (Prismor has no hook for this agent)

  MCP SERVERS
    SHADOW                 context7  [cursor]  medium
      · hardcoded secret in headers ('CONTEXT7_API_KEY')

  PROVIDER KEYS
    SHADOW                 anthropic  ANTHROPIC_API_KEY

  Coverage:  42%  of discovered AI surface is governed

The scan is read-only and host-local. It reads config files already on disk and never touches the network.

Credential-shaped values are redacted before they reach output. An MCP endpoint often carries the caller's key in a path segment, so the raw URL is itself a secret; those are masked at the point the record is built, which means the JSON output is exactly as safe as the terminal one. Key findings record the provider and location only — never a value.


Fixing it

prismor discover --fix           # asks first
prismor discover --fix --yes     # no prompt (CI)
prismor discover mcp --fix       # one section
prismor discover --fix --fix-mode enforce   # hook agents in enforce, not observe

--fix installs the global hook for unmanaged agents, moves MCP servers behind the gateway, and imports dotenv keys into Cloak. It prints the plan before touching anything, and states plainly what it cannot fix and why:

  WILL FIX
    will fix   Cursor       install the global hook for cursor
    will fix   weather      move behind the Prismor MCP gateway (.mcp.json)

  CANNOT FIX AUTOMATICALLY
    skipped    Warp         Prismor has no hook for this agent
    skipped    codexsrv     declared in ~/.codex/config.toml — a TOML config the
                            gateway migration cannot rewrite; move it by hand

  This writes to your agent config files.
  Apply 2 fix(es)? [y/N]

Piping the command without --yes declines rather than proceeding unattended.

What "governing" can and cannot mean

Worth being direct, because it bounds the feature: Prismor cannot constrain an agent it has not hooked. Egress screening, the sandbox, tool denies and kill switches all act on a hook payload, and an unhooked agent never produces one.

So --fix governs by eliminating the shadow — installing the hook, moving the server, vaulting the key — rather than policing it in place. An agent with no hook surface at all (Warp, Trae, Antigravity) is reported as unfixable instead of quietly skipped, because a dashboard row is not a control.


The fleet view

Enrolled devices report their inventory automatically: prismor enroll seeds it, and the runtime refreshes it daily. Admin → Shadow AI shows the result.

Two numbers there are deliberate:

"Never scanned" is the headline, not the finding count. Zero findings from a machine nobody looked at is an absence of evidence, not evidence of absence, and a page that renders those two states identically is confidently wrong in the one direction that matters. Devices that have never reported are counted separately and excluded from the totals.

"Fixable now" tells you what is actionable today. Thirty ungoverned findings is a number to despair at; "thirty ungoverned, twenty-four fixable in one command" is a number to act on. Each fixable finding carries the exact command, click-to-copy. Each unfixable one carries the reason — that is what distinguishes a five-minute change from a procurement decision.

Fleet coverage is weighted by surface, not averaged across devices: a laptop with one ungoverned agent and a CI box with forty are not "50% covered" together.

Per-device coverage reads — rather than 100% when a machine has nothing governable, so an empty machine never masquerades as a fully covered one.


Reporting controls

SettingEffect
PRISMOR_DISCOVER_INTERVALseconds between automatic refreshes (default 86400)
prismor discover --reportpush an inventory to the console immediately

Reporting is gated on the managed workspace, exactly like telemetry: a personal repo never reports what is installed on the developer's machine, and an unenrolled or revoked device reports nothing at all. The refresh runs as a detached background scan so it never delays a tool call.


Related

  • Cloak — vaulting the provider keys this finds
  • MCP Gateway — what MCP servers are migrated into
  • Live Telemetry — enrolling a device so it reports