Skip to content

feat(account): record which account domain a coding agent reports - #394

Merged
blitzcrieg1 merged 1 commit into
masterfrom
feature/account-domain
Nov 7, 2026
Merged

blitzcrieg1 merged 1 commit into
masterfrom
feature/account-domain

Conversation

@blitzcrieg1

Copy link
Copy Markdown
Owner

EDR and SIEM logs show that an agent's process ran, not which account was signed in inside it. A developer using a personal account on a company machine is outside the company's tenant, so vendor admin logs do not show it either. This records the account the agent reports.

The maintainer decided on 2026-11-07 to build this as an exception to the feature stop.

What an event gets

"account": {
  "domain": "example.com",
  "source": "hook_payload",
  "organization_id": "org-123"
}

The domain is stored by default. The full address needs AGENTMETRY_LOG_ACCOUNT_EMAIL=1, set on both the hook and the recorder.

Agent Source Tested with
Cursor documented user_email in hook input synthetic payloads
Claude Code documented user.email and organization.id in OpenTelemetry synthetic payloads
Claude Code hooks undocumented ~/.claude.json, labelled local_config synthetic files, plus one real read on the maintainer's machine, values not shown
Codex, Antigravity, Copilot no documented account field, so the block is absent synthetic payloads

Decisions worth checking

  • local_config is weak on purpose. It means the account last signed in on the machine. It is skipped when ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, a non-Anthropic ANTHROPIC_BASE_URL, Bedrock, Vertex, Foundry, CLAUDE_CODE_OAUTH_TOKEN, ANTHROPIC_PROFILE or the federation pair selects another credential. apiKeyHelper, an active profile and a gateway sign-in cannot be seen from the environment, and the docs say so.
  • The official base URL is not an override. Claude's desktop app can set ANTHROPIC_BASE_URL=https://api.anthropic.com, so only a different host skips the fallback.
  • Detection events keep the block, so the alert carries the domain. live.py is not frozen.
  • ECS uses agentmetry.account.*, not user.*. A reported claim is not the OS user's identity.
  • No list of personal domains ships in the sensor. The company keeps its own approved list in its SIEM.
  • No built-in rule. The frozen detection files are untouched, and the fingerprint stays 15846a0915769d4a.

Checks

Run from this branch on Windows, in an environment with ANTHROPIC_BASE_URL set to the official host:

  • pytest -q: 2150 passed, 7 skipped
  • ruff check agentmetry tests: clean
  • agentmetry benchmark: 0 missed, 0 false positives
  • A scan of the diff and new files for the maintainer's real account values: 0 matches

The worktree also ran a scratch recorder with a temp data dir: account domain retained on the detection event, no full synthetic email in the output, and verify --trail passing.

Not proven

  • No real Cursor session has produced this field. That test comes after merge and before any release.
  • No live OpenTelemetry session from Claude Code.
  • The Google SecOps and Splunk examples have never run on a live tenant, and the docs say so.
  • A session and its hook can have different environments, so the override check is not perfect.
  • It does nothing for an agent with no hook, including one a developer installs alone, and it does not see browser use.
  • Email is personal data. EU employee-monitoring and GDPR questions need a lawyer before this is pitched that way.

🤖 Generated with Claude Code

EDR and SIEM logs show that an agent's process ran, not which account was
signed in inside it. A developer using a personal account on a company machine
was invisible, because that account is outside the company's tenant and its
vendor admin logs.

Events now carry an optional `account` block: domain, source, and organization
ID where one is reported. The full address needs AGENTMETRY_LOG_ACCOUNT_EMAIL=1.
Cursor reports it through `user_email`. Claude Code reports it through
OpenTelemetry, and hooks fall back to the undocumented `~/.claude.json`. That
fallback means the account last signed in on the machine, so it is labelled
`local_config` and skipped when an API key, token, cloud provider, profile or
another endpoint selects a different credential. Codex, Antigravity and Copilot
document no account field and stay absent.

Detection events keep the block, so the alert a SOC reads carries the domain.
ECS keeps it under `agentmetry.account` rather than `user.*`, because a reported
claim is not the OS user's identity. The HTTP model declares it and external.py
bounds it, so the spool replay behaves the same way.

The schema stays additive. The fingerprint is unchanged at 15846a0915769d4a. No
real Cursor session and no live SIEM has run this yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@blitzcrieg1
blitzcrieg1 merged commit 83b4033 into master Nov 7, 2026
11 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Nov 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant