Skip to content

feat(dashboard): add an Identity section, with Codex and Antigravity accounts - #395

Merged
blitzcrieg1 merged 3 commits into
masterfrom
feature/identity-dashboard
Nov 7, 2026
Merged

blitzcrieg1 merged 3 commits into
masterfrom
feature/identity-dashboard

Conversation

@blitzcrieg1

@blitzcrieg1 blitzcrieg1 commented Nov 7, 2026 •

Copy link
Copy Markdown
Owner

#394 put the reported account on the trail, but nothing showed it. This adds an Identity section to the existing dashboard, and adds Codex account IDs and the Antigravity account domain so those two stop being blank.

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

What the section shows

  • Personal accounts and Corporate accounts, counted as domains because one row can hold several people, with agents, sessions, detections and last seen. Expanding a row shows how the account is known, organization IDs, OS accounts, hosts and dates, and a detection opens the existing investigation page.
  • Accounts with no domain, only when one exists. Codex reports IDs, so it is not guessed at.
  • Agent coverage, where an agent that reports nothing shows as Unknown with its own reason, so a blank does not read as clean.
  • Approved accounts, a list of domains or account IDs kept in that browser.

Decisions worth checking

  • Personal means a built-in provider list; corporate means anything else. core/audit/personal_domains.py cannot be complete, and somebody's own domain reads as corporate. The page says so. Look-alikes such as outlook.company.com and gmail.com.evil.test stay corporate. This label exists only on the dashboard, and events sent to a SIEM carry the domain alone.
  • One domain is one row. Only Claude reports an organization, which split the same domain in two. A row is a domain, not a person, so it does not prove two agents share an address.
  • Coverage counts from an agent's first report, so events from before this field existed do not read as an agent that stopped reporting.
  • Codex's IDs come from the first line of its session file. Only a .jsonl file under CODEX_HOME or ~/.codex is opened, and nothing past the first line is read. auth.json is never touched. The format is not a stable interface, and an ID says nothing about whether an account is personal.
  • Antigravity reads its last sign-in. Its hook input has no account field, so the domain comes from jetski.onboarding.lastLoginUsername in its undocumented app_storage.json, labelled local_config. It means the account last signed in on the machine, the key can change between versions, and only the Windows path has been run.
  • Coverage status follows the newest event. An agent reporting again reads as reporting, and the sentence keeps the earlier gap visible, for example "23 earlier events since the first report had none".
  • ZCode stays Unknown. No account setting was found in its folders on one machine, only addresses inside conversation content.
  • No built-in rule. The frozen detection files are untouched, and the fingerprint stays 15846a0915769d4a.

Checks

Run from this branch on Windows:

  • pytest -q: 2251 passed, 7 skipped
  • ruff check agentmetry tests: clean
  • agentmetry benchmark: 0 missed, 0 false positives
  • Dashboard: 200 tests pass, type check clean, lint has no errors, build passes
  • In a browser on the demo data: the sections, row details, approved list, detection link, phone width and light mode. Injected test data covered a very long domain, five agents on a row and a page with no accounts
  • On a read-only snapshot of the maintainer's real trail: personal and corporate sections and agent coverage. 30 days loaded in under a second
  • The Codex read on 3 real session files: both IDs found in about 1 ms each, values not shown
  • The Antigravity read on one real machine: a domain found in about 1 ms, value not shown
  • A scan of the diff and new files for the maintainer's real account values: 0 matches

Not proven

  • No real Codex or Antigravity session has produced these values through a live hook. That test comes after merge.
  • The copy buttons showed no confirmation in the test browser, which may block the clipboard, so they need a manual check.
  • The phone layout is unchanged, and tables scroll sideways below about 760 px.
  • No real detection has been linked to an account on live data. The link is tested on demo data and through the real ingest path in tests.
  • The dashboard covers one machine. A fleet view belongs in Enterprise.
  • Copilot and the other agents have not been checked beyond their documented hooks, where looked at.
  • It does nothing for an agent with no hook, or for 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

blitzcrieg1 and others added 2 commits November 7, 2026 20:37
The account field reached the trail, but nothing showed it. Reading a domain
out of raw events is not something a security team does during a case, so the
dashboard now has an Identity section. It lists reported accounts as personal or
corporate, with their agents, hosts, OS accounts, sessions and detections, backed
by GET /api/v1/audit/identity. A detection opens the existing investigation page.

An agent that reports nothing is shown as unknown, each with its own reason, so
a blank does not read as clean. Coverage counts from an agent's first report,
because events recorded before this field existed would otherwise make every
agent look partly broken. One domain is one row: only Claude reports an
organization, which split the same domain in two.

Personal means the domain is on a built-in list of public email providers.
Corporate means only that it is not on that list, so somebody's own domain reads
as corporate, and the page says so. Look-alikes such as outlook.company.com stay
corporate.

Codex hook input has no account field, but it names the session file, whose first
line carries creator_account_id and creator_user_id. Those arrive as account_id
and user_id with source session_meta. They name no domain or address, so Codex
accounts sit in their own section rather than being guessed at, and can be matched
against an approved list. Only a file under Codex's home is opened, and auth.json
is never read. Antigravity stays unknown, because nothing safe to read names it.

The schema stays additive. The fingerprint is unchanged at 15846a0915769d4a. No
real Codex session has produced these IDs through a live hook yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the newest event

Antigravity showed as unknown because its hook input has no account field and an
earlier search missed `app_storage.json`. Its
`jetski.onboarding.lastLoginUsername` key is not a credential, so the domain now
comes from there, labelled `local_config`. Like Claude's file, it means the account
last signed in on the machine, and the key can change between versions. Only the
Windows path has been run.

Claude read as "Partly reporting" because of 23 events from a few minutes when the
live hooks sat on another branch, and it would have stayed that way until they aged
out of the window. Status now follows the newest event, and the sentence keeps the
gap visible rather than hiding it.

ZCode stays unknown: no account setting was found, only addresses in conversation
content. Its explanation now says that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@blitzcrieg1 blitzcrieg1 changed the title feat(dashboard): add an Identity section, and Codex account IDs feat(dashboard): add an Identity section, with Codex and Antigravity accounts Nov 7, 2026
"Personal accounts: 1" read as one person when the row is one domain, and "outside
any company tenant" was simply false: a Gmail login can belong to an organization,
and the maintainer's own row shows one. The totals now say domains, and the note
says several people can share one and to review it against the approved list.

The top of the page gave five equal cards and a long notice before any evidence. It
now leads with personal domains and the approved list, keeps the unknown counts in
one visible line, and moves the lesser limits under an expander. "Not verified
identity" and the last-sign-in warning stay visible.

Columns moved between sections because each table sized itself, which slowed
comparison, so they now share fixed widths. A long agent list shows two names and a
count, with the rest in the details. Timestamps name their timezone, full IDs have
copy buttons, and editing the approved list now confirms that it applied.

From an outside design review. Phone layout, search and filters were left out: the
page is read on a desktop and holds a handful of rows on one machine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@blitzcrieg1
blitzcrieg1 merged commit 4a74f6c 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