Skip to content

Using it

The pages, from a running instance with the sample account imported (captured by scripts/capture_screenshots.py, so these images are reproducible rather than asserted):

An identity's detail open beside the list: the derived facts, what it holds now and through which group, and the authorization form

A review campaign open over the whole inventory, ninety-nine items awaiting decisions

The risk report: identities ranked by the engine, every finding naming its reason

The page is one document with no build step (D-036, D-075, D-077). The views sit in a sidebar, the theme follows the system until a person chooses one from the bottom of that sidebar, every row that opens something can be reached and opened from the keyboard, and the palette is a set of tokens whose contrast a script checks in both themes. An identity opens beside the list it came from rather than on top of it, every list stands a skeleton while it loads and says what to do next when it is empty, and the delta's tiles narrow the table to one class of difference.

Four people, and the design answers their questions in their order.

  • The reviewer certifies identities and groups: what is this, whose is it, what can it do and where did that privilege come from, is it used, what changed since last time, what do you recommend. Everything on the decision screen exists to answer those without leaving the page, and when the answer is not there, "insufficient evidence, here is what was missing" is a recorded outcome that steers what gets built next.
  • The operator imports files, runs campaigns, and triages findings.
  • The auditor consumes proof: the population statement, coverage, each decision with its actor and time.
  • The administrator manages users and roles, and nothing else.

Three roles. A reviewer reads everything and records attestations and review decisions. An operator additionally imports files and sets governance: owners, purposes, flags. An administrator additionally manages the application's local users.

Imports. Nine file shapes are read natively, the two AWS export formats, one document each for GitHub, Kubernetes, Google Cloud, Azure and Entra, and Okta, and two for Active Directory, the cmdlet document and the SharpHound collection; a tenth, the table, carries any other provider through a mapping; the Imports view names the shape and a file of another shape is refused rather than guessed. Every parser works in memory, bounded on every axis, and never writes to disk. The file's content is authoritative and its name is not, because a filename is client input; a file claiming to cover one account, cluster, project, tenant, organization, or domain is verified to cover one rather than trusted; a timestamp with an unrecognized timezone is rejected rather than guessed, because capture times order the history and therefore decide what counts as current. Imports are append-only and duplicates are rejected, so a re-import is harmless and out-of-order syncs self-correct.

The imports view: the file, its kind chosen from the nine native shapes or the table, the capture time, and the record of every import so far

Inventory. The dashboard counts identities by their worst finding tier; the table filters by name, type, and tier, and each row shows its worst finding explaining itself, so the list teaches before a single click. Nothing here is a stored status: every figure is computed at read time from the observation history, because a stored security status that drifts from reality is worse than none, people trust it. The computation runs against the newest import's capture time, never the wall clock, so a month-old import shows month-old staleness rather than aging by itself, and the as-of line states what the page knows. Hiding an identity from this inventory requires tampering with the stored history again after every future sync, because each sync is a full picture and state is re-derived from all of it.

Identity detail. The observation timeline, the findings, and the governance section. Identities are keyed by the provider's immutable identifier, not by name, so a principal deleted and recreated under its old name is a new identity that inherits nothing, and the reuse of a governed name is itself surfaced, because inheriting a dead identity's standing is exactly how a recreated principal would be laundered. An identity is not flaggable as unused until it has been observed for fourteen days, because a two-week-old key that has not been used yet is new, not stale, and a false positive on day one costs the tool its credibility.

Findings explain themselves and name their sources: which policy, held directly or through which group. Privilege is judged by what a policy can do, not what it is called, so a policy named ReadOnly that grants the permission to attach arbitrary policies is reported as the administrator it is. Escalation detection covers the published single-permission and permission-pair paths a principal could use to raise its own privilege, and reports them for principals nobody calls an administrator, because the shadow admin is the finding that matters; the actual administrators are already on somebody's list.

The governance section holds the human record: a typed owner, a purpose, flags, and attestations, each attributed, each superseded or cleared rather than edited, so the history of who said what stands the way the machine history does. The owner is a team by default, because an individual owner is the orphan in waiting: the person leaves, nothing in the cloud account changes, and the identity keeps its keys with nobody accountable, which is the failure class this tool opens with. Assigning an owner answers the unowned finding; a disagreement between the assigned owner and the provider's tag is surfaced as its own notice naming both values, because a silent winner would hide exactly the staleness a governance tool exists to show.

Authorizations. Beneath the observed facts, the detail shows the second record: what a named person said this identity may hold, for how long, and who owns it, beside what it holds now and what it can obtain, each grant with the route it arrives by (directly, through a group, by assuming a role, or, in a directory, through a control right). The authorization form prefills from an observed grant, so authorizing what exists is one confirmation and not a transcription; the approver is taken from the session and never from a field, and an authorization is superseded or revoked with a reason rather than edited, so the record of who allowed what stands the way the machine history does. An organization that already keeps its approvals in a spreadsheet imports them through the file door, with a dry run that shows how each row was read before anything is written.

The delta. One view of every difference between the two records, in nine classes: held but not authorized, authorized but not held, expired and still held, eligible but not authorized, reached through a door nobody authorized, a definition that changed after it was authorized, a custom definition nobody authorized, a custom definition that changed, and an owner the tag and the record disagree about. Each row names both sides and when each side was last heard from, because a stale side makes a difference look like agreement. The tiles at the top count each class and narrow the list; the view is computed every time it opens and stored nowhere, so it cannot say yesterday's answer.

The delta: every difference between what identities hold and what a person authorized, counted by class and listed with both sides

Groups. Privilege sources with members, owners, and their own findings. An empty privileged group is reported before anyone joins it, because it is a standing grant waiting for its next member with nobody reviewing it. What changed since the previous import, who joined and who left, is computed and shown, because the delta is what a review actually reviews; re-reading the full list every quarter produces approval without attention.

Campaigns. A campaign is driven by one of three things: the calendar, which reviews a scope and stays because auditors ask for it; the delta, which puts every identity with a finding a person must answer in front of whoever can say whether the access should exist; or expiry, which puts every authorization ending within a window in front of the person who approved it, and raises an alert for each so an expiry nobody heard about cannot become one still held. Whatever drives it, a campaign freezes its population into items at creation, each item carrying the evidence as it stood and the engine's recommendation with its reasons, so the review covers a stated population rather than a moving one; the population statement in the evidence export describes that frozen set, which is what makes it a statement instead of a target. Decisions are one item, one person: certify, revoke recommended, insufficient evidence, or delegated. There is no bulk certification anywhere in the application, because a certification records that someone looked at that identity, and a button that certifies a hundred rows at once records that nobody did. Insufficient evidence must name what was missing and delegation must name who holds it now, because those answers are meaningless without their notes; the rollup collects the missing-evidence notes across campaigns, since one recurring note is a reviewer's problem and the same note across a column is the program's problem. A decision is final within its campaign, a changed mind being the next campaign's decision, and close refuses while any item is unanswered, because an access review with gaps is a false population statement. The evidence export carries the population statement, coverage, and every decision with actor and time, as JSON and as a CSV built from the same export, because the people who consume evidence live in spreadsheets.

Reports. The risk report is one self-contained file ranked by the engine, safe to open from disk years later. Because it opens from disk, no server header protects it, so it is rendered by an engine that escapes every value by default and contains no script element at all. The CSV prefixes every formula-leading cell, because a cell that begins with an equals sign executes in the reader's spreadsheet with the reader's permissions, and identity names are controlled by the observed account's users. The JSON export carries the same figures the page shows, raw, because JSON consumers parse rather than interpret. All three read from the one computation the page reads.

The page itself renders every API value through its text interface, never as markup, so a hostile identity name displays as a string instead of running as script; a parser-based scan of the page's code fails the build if a markup sink appears. The session token lives in a closure variable rather than browser storage, where any script that ever ran in the page could read it; the accepted cost is that a refresh signs you out. The content policy forbids inline script and style, and the page needs neither.