Activity & troubleshooting
The activity feed, the per-credential usage drawer, the denial explainer, the identity ledger — and what each failure code means.
Machine credentials fail at inconvenient hours, and the useful question is never "did something go wrong" but "was it refused, and by which rule". Four surfaces answer that, each at a different grain.
Activity tab — what this machine did#
A reverse-chronological projection over the tamper-evident audit log, filtered to this identity. The data was always written with the machine as the actor; this is where its owner can read it.
Denials lead the design. An allow is routine; a refusal is either a broken pipeline or a credential probing paths it cannot reach. So denials are visually distinct and filterable in one click, rather than one grey row among hundreds. You can also filter to a single credential, which is how you tell "this machine is fine, that one key is not" from "the whole identity is broken".
Explain a denial#
Any denied row offers why was this refused?. It answers against this machine's real permissions — not against a generic pattern checker — and names the rule that refused it, including a deny that overrode an allow granted elsewhere.
Denial → why → fix, without retyping the path. If the answer is "nothing matches", the fix is on the Permissions tab; if the answer names a deny, the fix is in the library.
Credential drawer — everything about one key#
Click a key on the Credentials tab. The drawer is ordered the way an operator's questions actually arrive:
| Band | Answers |
|---|---|
| Identity | what is this key, when does it expire |
| Health | is it working — allowed vs denied calls, first and last use |
| Reach | what it may touch versus what it has touched, and the source addresses it came from |
| Timeline | daily allowed/denied counts, and its busiest paths |
The Reach band is the point. Anyone can render a log; "granted prod/**, has only ever read
prod/db/*" turns the drawer into a least-privilege tool — and the fix is one
re-scope away, on the same screen,
with no re-issue and no CI redeploy.
The source-address list is the other quiet win: if every call in the window came from three addresses, an IP allowlist costs you nothing and removes most of the value of a stolen token.
The window is not forever#
Every band in the drawer covers the same window, and the drawer says which one — "Health (last 30 days)". That number is not fixed: it is how long your space keeps machine activity, and the drawer will never show you more than that, because empty days and days we no longer hold look identical on a chart.
You choose it, within what your plan allows, under DevOps → Retention. Shortening it deletes older activity on the next nightly cleanup, so the screen tells you how much you are about to discard before you save.
What it does not shorten: refusals, writes, deletions, key issue and revocation, and permission changes. Those are the evidence an audit asks for, and they are kept in the tamper-evident audit log on your plan's full audit retention regardless of this setting. This window covers the high-volume read history — which pod read which config value, and when.
Reach is not bounded by it either. "Has not touched 12 of 15 paths it can reach" is a statement about the credential's whole life, not about the window, because the paths a key has touched are tracked separately and kept for as long as the key exists. A job that runs quarterly will not read as unused just because you keep 30 days of history — so tightening a key stays safe however short you set this.
Ledger — who changed what#
Machine identities → Ledger is the lifecycle record for the whole project: identities created, renamed, disabled, archived; keys issued, re-scoped, settings changed, revoked, deleted; permissions attached and detached. Routine machine reads and writes are deliberately excluded — this screen answers "who could read prod in March?", and a million data-plane rows would bury that.
It also carries archived identities, which is why it is not a trash can: a screen that lists only tombstones gets opened twice a year, and this one is where an audit actually starts.
What each failure means#
Machine-plane calls fail in exactly three ways, and telling them apart is most of the debugging:
| Code | Meaning | Where to look |
|---|---|---|
401 |
the credential was not accepted | the key itself — see below |
403 |
authenticated, but no permission matched (access denied by policy) | Activity tab → explain the denial |
429 |
this key's rate limit | X-RateLimit-* headers on the response |
401 is deliberately uniform: the same reply whether the token is malformed, unknown, revoked,
expired, missing an expiry, used from a disallowed address, or belongs to a disabled identity. The
response cannot tell an attacker which of those it is — but the audit row can tell you, and it
names the exact cause, down to the address that was refused and the allowlist it missed. That is the
one place the distinction exists, which is why the Activity tab is the first screen to open.
The identity page shows the same thing at a glance:
| What you see there | Cause |
|---|---|
| Key status Revoked | it was revoked, or the identity was archived |
| Expires column red / in the past | it lapsed — issue a replacement |
| No expiry — unusable | issued before lifetimes were mandatory; it will never authenticate |
| Identity badge Disabled or Archived | every key of the identity is refused |
| An IP allowlist that excludes your runner | the call arrived from an address outside it |
| Last used never updates | the request never reached the platform — check the host and the Authorization header |
A machine call goes to the API host, not the console's. Pointing curl at the console returns
the web application's HTML with a 200, which looks nothing like an auth failure and wastes an hour.
The Setup tab's snippets always use the right host.
Related#
- Access policies — how a match is decided
- Re-scope & settings
- Reading secrets with the CLI · Using in CI