Write a manifest
A YAML file naming the credentials the agent held: type and identifier, never the secret itself.
YAML manifestGhostGrant checks that a retired AI agent's GitHub, AWS and Slack credentials actually stopped working, then checks again at 24 hours, 7 days, 30 days and 90 days. If one comes back to life, it raises an alert.
When a team shuts down an AI agent, somebody clicks revoke on its tokens and keys and moves on. Nobody goes back to see whether the GitHub token still answers, whether the AWS key got cached into some other job, or whether the Slack bot quietly came back after a reinstall. GhostGrant does the going back.
A YAML file naming the credentials the agent held: type and identifier, never the secret itself.
YAML manifestEach provider's own introspection endpoint says whether the credential is dead, and the answer is recorded.
24h · 7d · 30d · 90dA credential that goes from dead to alive between checks raises an alert, then you get a dated report.
dead → aliveOnly GitHub is wired up so far. check --live asks GitHub; Slack and AWS answer unknown until their adapters land. See the roadmap.
ghostgrant decommission <manifest> records a decommission entry with a timestamp and the manifest's checksum.
Runs every check that's due against canned responses and prints alive or dead.
canned answersGitHub: asks GET /user with each token you pipe in. 200 is alive, 401 is dead, a timeout, 5xx or rate limit is unknown.
Each check runs the window that's due and skips records with nothing due. Dead-to-alive prints an ALERT and goes to alerts.log.
ghostgrant report <agent> prints a dated plain-text report of every check for one agent, or the same data with --json.
A bundled report for a fictional agent (GitHub credentials, every window, one flip), rendered with the CLI's own Report type.
Coming later: a Slack adapter (slice 2), then AWS IAM, Okta and Google Workspace (slices 5 to 7).
Node 22.18 or newer. Records live under $GHOSTGRANT_HOME (default ~/.ghostgrant); point it somewhere else to try things without touching your real records.
# 1. Install dependencies (Node 22.18 or newer) $ npm install # 2. Build the CLI into dist/ and the demo page into _site/site/ $ npm run build # 3. Record the example manifest and check it in mock mode $ node dist/cli.js decommission examples/manifest.yaml $ node dist/cli.js check --mock $ node dist/cli.js report billing-bot --mock # 4. Tests and lint $ npm test && npm run lint
Live checks. check --live needs the tokens themselves, which the manifest never holds. Pipe them in on stdin, one per line. Each token goes to the credential whose prefix it starts with, is sent to that provider once, and is dropped when the run ends.
$ printenv OLD_AGENT_GH_TOKEN | node dist/cli.js check --live
The last line of a report is a verdict. unknown never counts as dead, so it keeps a report from being clean. A report says what was checked and what each provider answered; it isn't signed and isn't compliance evidence.
| Verdict | Meaning |
|---|---|
| clean | Every credential answered dead at every window checked, through 90d |
| dead-so-far | Every answer so far is dead; the line says when the next check is due |
| unknown | A credential has no alive or dead answer for a window that was checked |
| alive | A credential's latest answer is alive |
| flipped | A credential went from dead back to alive |
| not-checked | No checks of that kind yet |
What it won't do, on purpose: revoke or rotate anything (it only asks read-only introspection endpoints), sign or attest its reports, run as a hosted service, or support providers beyond GitHub, Slack, AWS IAM, Okta and Google Workspace.