Collection policy
AI usage and application time are always collected, and so is the inventory of installed AI software unless you turn it off. Everything more sensitive is off by default and is switched on per environment under Settings → Collection & deployment → Collection policy. Collectors fetch the policy from the platform on every cycle, so a change reaches the whole fleet within one reporting interval with no redeploy.
Settings
Section titled “Settings”| Setting | Values | Default |
|---|---|---|
| Browsing | off, domains, full | off |
| Network observation | on / off | off |
| Key fingerprints | on / off (requires network observation) | off |
| Coding-agent content | minimal, metadata_only, full | full (as far as Data retention allows) |
| Installed AI software | on / off | on |
Set in the enforcement policy instead:
| Setting | Where |
|---|---|
| Sensitive data in coding agents (per kind) | Protect → Policies → Sensitive data, Coding agents column |
| Block when the policy is unavailable | Protect → Policies → Sensitive data |
| Also check files the agent reads, What coding agents may change, Prompt injection | Protect → Policies → Coding agents |
Browsing
Section titled “Browsing”| Level | What is collected |
|---|---|
off | Nothing from browsers. |
domains | Time on AI websites, by service (for example chatgpt, claude, gemini). |
full | Individual visits to AI sites with full URLs, browser and timestamp. |
Browsing feeds the Websites view and the web evidence in Shadow AI.
Network observation
Section titled “Network observation”Reports which local process is talking to which AI provider: executable name and path, owning OS user, and provider. It’s read from the socket tables the operating system already maintains.
It exists to catch AI use that leaves nothing on disk: an internal app, a batch job, a script. The provider’s own usage report sees that spend but labels it with an opaque key ID and never a machine. Network observation supplies the process, machine and person.
It deliberately produces no byte or token counts. Tokens live inside the TLS body, and anything that claims to count them without terminating the connection is either guessing or intercepting.
AI hostnames
Section titled “AI hostnames”Where the operating system records which hostname a process looked up, the collector also reports the AI hostnames each process contacted, such as contoso.openai.azure.com or bedrock-runtime.us-east-1.amazonaws.com. This covers AI services on shared CDN addresses and per-resource cloud endpoints that address matching can’t. Nothing is decrypted.
Only hostnames in the AI catalog are ever reported. That covers the catalog’s hosts, your own AI endpoints, and wildcard hosts such as *.openai.azure.com. No other hostname leaves the machine, so this is not browsing data.
| Platform | Source | Privilege |
|---|---|---|
| macOS | mDNSResponder’s query log. It gives the process and a hash of the name. The collector compares that hash with the catalog’s exact hosts and never learns any other name. | The user running the collector must be an administrator, or the collector runs as a system service. |
| Windows | The DNS client cache, the same data ipconfig /displaydns shows, joined to the TCP tables by address. | Any user. |
| Linux | systemd-resolved’s cache (resolvectl show-cache), joined to the socket table by address. | Root, and only where systemd-resolved is the resolver. |
These sources have limits:
- Without the privilege above, the collector matches by address only, as before.
- Encrypted DNS (DoH or DoT) inside an application bypasses the system resolver and its cache.
- On macOS the name hash is only 32 bits, so a lookup is reported only when the same process also held a connection, within the last five minutes, to an address that host resolves to. A short-lived process that disconnects between scans is therefore not attributed by hostname.
- On macOS, wildcard hosts can’t be matched. Neither can native apps that resolve through Network.framework. Those apps are recognised by their bundle instead.
- An address that a non-AI name also resolved to is never attributed by hostname.
Key fingerprints
Section titled “Key fingerprints”With network observation on, the collector can also identify which API key a process is using, by reading provider key variables (such as ANTHROPIC_API_KEY or OPENAI_API_KEY) from that process’s environment and reducing each key to a fingerprint.
The fingerprint carries no character of the key. Providers list each key with a redacted hint (Anthropic shows sk-ant-api03-R2D...igAA); what every hint has in common is the provider and the key’s last four characters, and most start with the key’s public family prefix (sk-ant-api03-, sk-proj-). The collector hashes exactly those parts with your environment’s guard fingerprint key, the key the device policy already carries:
hintHash = HMAC-SHA256(key, "<provider>:...<last four>")shapeHash = HMAC-SHA256(key, "<provider>:<family prefix>...<last four>")It sends only the provider, the variable name and the two hashes (the first 16 hexadecimal characters of each). When a spend connector syncs, Shield360 computes the same hashes over the hint the provider lists and stores only those, beside the provider’s own key id and name. Billed usage is matched to the application, machine and person by hash; the shape hash only separates two keys whose last four characters happen to agree.
Without the guard fingerprint key the collector sends no key fingerprints at all: an unkeyed hash of four characters could be reversed by trying every combination. The full key exists only in memory, only long enough to hash those parts, and is overwritten before the function returns. No data type in the collector can hold any of a key’s characters.
Collectors from before this change still send the provider’s hint and last four characters. Ingest hashes them with your environment’s key and stores only the hashes.
Key fingerprinting can’t be on while network observation is off. The platform normalises such a policy to off.
Installed AI software
Section titled “Installed AI software”Finds the AI software on each machine whether or not it is in use: desktop apps and command-line agents, local model runtimes such as Ollama and LM Studio (with the names of the models they serve), AI extensions in VS Code, Cursor, Windsurf, VSCodium and JetBrains IDEs, and the MCP servers AI clients are configured with. It reports names, versions, install paths (the home directory written as ~) and, for an MCP server, the transport and the command’s file name or the URL’s host. An MCP server’s arguments, environment variables, headers and keys never leave the machine.
It also lists every installed application, with the IDs of the AI libraries, SDKs or model files found inside it, so the platform can classify each one as an AI tool, an app with AI built in, or not AI. An app’s files and their contents never leave the machine.
It is on by default, because an inventory of installed software is not personal content. See Installed AI software for what is found on each operating system, how often, and how to stop it on one machine.
Sensitive data in coding agents
Section titled “Sensitive data in coding agents”The coding-agent hooks check prompts and “about to” tool calls on each machine, before they reach the model, for secrets, API keys, private keys, passwords, social security and card numbers. What they do for each kind of data is the Coding agents column of the enforcement policy; by default email addresses, phone numbers, IBANs and IP addresses are off for coding agents, because they are usually noise in code.
| Mode | What happens |
|---|---|
off | Nothing is checked. |
warn | The prompt or tool call goes through, the developer sees a notice, and the finding is recorded. |
block | The prompt or tool call is stopped, the developer is told why, and the finding is recorded. |
Only the kind of data is reported, never the value.
The hooks also look for your own kinds of data, with the action the Coding agents column gives each one. A notice or refusal names such a kind by the name you gave it. Secret files remain a built-in check: a custom kind on its own does not block reading .env or a key file.
The hooks act on the copy of the policy the collector last saved on the machine. The resident collector fetches the policy every five minutes, so a change you save applies on each machine within five minutes, and shield360 collector status fetches and saves it at once. Until then a prompt is handled under the previous policy: a kind you just moved to block is still only warned about.
In warn, Claude Code and Codex show the developer a warning such as:
Shield360: this prompt contains a payment card number. Your organisation allows it but records it — remove it if it isn't needed.The model is also told what kind of data was flagged, so it doesn’t repeat the value. Cursor’s hooks can show a message only when they stop something, so in Cursor a warned prompt goes through without a notice.
Block when the policy is unavailable
Section titled “Block when the policy is unavailable”The hooks run on every prompt and can’t wait on the network, so they read the policy the collector last cached on the machine. If that copy is missing, unreadable or corrupt, the guard allows prompts by default: a broken setup never stops a developer.
With a kind on block, Block when the policy is unavailable (in the enforcement policy) reverses that. When blocking is on and a machine cannot read its policy, block prompts instead of allowing them. The developer sees:
Shield360 blocked this because your organisation's Shield360 policy is unavailable on this machine, and your organisation requires AI requests to be blocked until it can be read. Contact your administrator to restore the Shield360 policy.The hook refuses every event the guard acts on (prompts and tool calls) and exits with code 2. GitHub Copilot CLI doesn’t let a hook stop a prompt, so there only its tool calls are refused.
The machine keeps a marker beside its cached policy while the setting is on, so deleting or corrupting the policy file doesn’t turn the guard off. As a system service the collector keeps both in a directory only an administrator can change.
| Last policy cached on the machine | Policy now | Result |
|---|---|---|
| None (fresh install) | Missing or corrupt | Allowed |
off or warn, any setting | Missing, unreadable or corrupt | Allowed |
block, fail closed off | Missing, unreadable or corrupt | Allowed |
block, fail closed on | Readable (for example, the platform is unreachable and the cached copy is kept) | The cached policy applies: sensitive data is blocked, clean prompts are allowed |
block, fail closed on | Missing, unreadable or corrupt | Blocked, exit code 2 |
The SHIELD360_AGENT_GUARD environment variable still overrides the cached policy on a machine.
Coding-agent content
Section titled “Coding-agent content”How much of a coding-agent session the hooks send. The levels, from least to most:
| Level | What is sent |
|---|---|
minimal | Session totals only: tokens, cost, lines changed. No per-event spans. |
metadata_only | Every event (tool calls, model turns, edits) with names, paths and timings, but no prompt, response, tool input or tool output. |
full | Everything metadata_only sends, plus prompt, response and tool bodies, after secret scrubbing. |
This level is a ceiling under Data retention: machines are told metadata_only unless the organization stores prompt or tool content, so with the defaults no content leaves a machine. full here means “as much as data retention allows”. The organization’s administrator decides; a machine’s own setting (shield360 configure --content-capture or SHIELD360_CODING_CONTENT_CAPTURE) can only narrow it. The hooks use the strictest of the organization’s level, the person’s narrowing (below) and the local setting. If Block when the policy is unavailable is on and the machine can’t read its policy, the organization’s level is unknown, so the hooks send minimal. See Privacy & Governance.
Narrow collection for one person
Section titled “Narrow collection for one person”Under Govern → AI usage → People → a person → Collection for this person, an administrator can lower what is kept for that person:
| Setting | Levels, least first |
|---|---|
| Browsing | off, domains, full |
| Shadow-AI discovery | off, connections, fingerprints (connections plus key hints) |
| Coding-agent content | minimal, metadata_only, full |
| Installed AI software | off, on |
A per-person setting can lower what is kept for that person. It cannot raise it above the organization’s level:
- The control only offers the organization’s current level and the levels below it.
- The server refuses (
400) any request that sets a person above the organization’s current level. - What applies to a person is always the lower of the organization’s level and theirs, worked out each time it is read. If the organization later lowers its policy, everyone narrowed is lowered with it. If the organization raises its policy, a person set to a lower level stays at that level. Choose Same as organization to follow the organization again.
There is no setting that turns collection onto one named person. Browsing is off until an administrator enables it for the whole organization, and nothing per person can switch it on for one individual.
People are matched by the account name their computers report (the name under Govern → AI usage → People), ignoring case and any Windows domain prefix. Coding-agent telemetry is matched by the same account name, which the hooks stamp as coding_agent.os_user, and by the hook’s gen_ai.user.name when it is set. A record that names more than one identity gets the strictest level among them.
Changing a person’s collection needs the same permission as changing the organization’s policy (Change what is collected). Each change is written to the audit log with who made it, the person, the organization’s levels, and the person’s override and effective levels before and after (collector.person_collection.update and collector.person_collection.delete). Changes to the organization’s levels are recorded the same way (collector.policy.collection_levels.update).
How it reaches machines
Section titled “How it reaches machines”When a collector fetches its policy it names the accounts it collects for. The platform answers with each person’s levels and, at the top level, the strictest across them:
- Browsing is read per person, so narrowing one person on a shared machine doesn’t affect another.
- Shadow-AI discovery reads the machine’s socket table, which isn’t one person’s, so it uses the strictest level of anyone on the machine.
- Coding-agent content is left for the hooks in the machine’s policy file. A collector running as a system service writes one file for every account, so it holds the strictest level of anyone on the machine.
Enforced again on ingest
Section titled “Enforced again on ingest”A current collector stops at the right level before anything leaves the machine. The platform checks again on the way in, so an older collector, a machine that hasn’t fetched the policy since a change, or a modified one can’t store more than is allowed:
- Browsing visits for a person at
offare dropped. Atdomains, full URLs are cut to the site. - Network observations for a person at
offare dropped. Atconnections, key hints are removed. - Coding-agent spans for a person below
fulllose prompt, response and tool bodies. Atminimal, only session spans and guard decisions are kept. - Installed AI software for a person at
offis dropped. Aton, an MCP server’s target is cut to the URL’s host or the command’s file name, an install path in a home directory is written as~, and fields that do not belong to an item’s kind are removed.
Ingest uses the same rule as the policy the machines fetch. A change takes effect on ingest within 30 seconds, and straight away on the server that saved it. If the platform can’t read the policy, it stores nothing and the machine retries.
GET /api/collector/policyPUT /api/collector/policy{ "browsing": "domains", "netObserve": true, "fingerprintKeys": false, "agentGuard": "block", "agentGuardFiles": true, "guardFailClosed": true, "contentCapture": "metadata_only"}contentCapture is optional; a request without it keeps the current setting (full if none has been saved). So is aiSoftware (true or false); a request without it keeps the current setting (true if none has been saved).
GET also returns the enforcement fields (agentGuard, agentGuardFiles, guardFailClosed, editPolicy, injectionMode, gatewayInjectionMode, injectionLlmCheck, aiGuard, aiGuardKinds, aiTools, aiWarnOverride, and proxyGuard, proxyGuardKinds, agentGuardKinds) as they apply to this environment. A PUT that leaves them out keeps them. A PUT that changes one changes this environment’s override of it in the enforcement policy, which also needs Manage agent policies. New integrations should use the enforcement policy API.
One person’s narrowing:
GET /api/collector/people/collection?user=alicePUT /api/collector/people/collectionDELETE /api/collector/people/collection?user=alice{ "user": "alice", "browsing": "off", "network": null, "contentCapture": "metadata_only"}A dimension that is null or absent follows the organization. GET returns the organization’s levels, the person’s override, the levels that apply now, and the levels the person may be set to.
Machines read their own policy with their collector token at GET /api/collector/device/policy?users=alice,bob. The response carries the machine-wide policy and a people object with each named person’s levels.