Severity-scaled advance notice (critical 2d, high 5d, medium 7d, low 10d against the 7/30/60/90d windows) on the configured alert channel. New lowest-precedence standing condition in incidentFor; sla_crossed supersedes it silently, rotating inside the window sends an honest 'rotated ahead of its deadline' recovery. Reminders wear amber, not the incident red dot. Also un-breaks the strip-types test runner for alerting and blast-radius: path-alias imports had crept into lib modules the tests traverse. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .claude | ||
| .github/workflows | ||
| app | ||
| components | ||
| docs | ||
| lib | ||
| openspec | ||
| prisma | ||
| public | ||
| scripts | ||
| types | ||
| .dockerignore | ||
| .env.example | ||
| .gitguardian.yaml | ||
| .gitignore | ||
| .npmrc | ||
| ai-declaration.md | ||
| CLAUDE.md | ||
| DEPLOYMENT.md | ||
| docker-compose.yml | ||
| docker-entrypoint.sh | ||
| Dockerfile | ||
| eslint.config.mjs | ||
| LICENSE | ||
| middleware.ts | ||
| next.config.ts | ||
| package-lock.json | ||
| package.json | ||
| postcss.config.mjs | ||
| README.md | ||
| tailwind.config.ts | ||
| tsconfig.json | ||
| vercel.json | ||
Keystrok
Find exposed API keys in your code, track them, and rotate them safely. Keystrok is a self-hostable security tool for developers who want to get on top of leaked secrets without handing their keys to a third party.
Docs: keystrok.dev/docs (also readable in-repo at docs/guide/).
Advisory, never automated. Keystrok ranks what actually needs attention and walks you through each rotation step, but it never rotates or revokes a key on its own. The irreversible actions stay in your hands. It also never claims to know a key's real age: every deadline is anchored to when a key was discovered, not a guessed creation date.
What it does
- Discovery: scan a GitHub repo (via a GitHub App) or a local folder for exposed secrets (AWS, Stripe, GitHub, observability platforms, and more). Findings are stored hashed/masked, never as plaintext.
- Ledger: promote real findings to a tracked inventory. Each key gets a rotation deadline from its severity, counted from discovery.
- Guided rotation: step-by-step, operator-gated walkthroughs. The revoke step is irreversible by confirmation.
- Platform validation: connect a platform (Datadog, Grafana, Stripe, GitHub, …) to check whether a leaked key is still live. A dead key is far less urgent than a working one.
- Bring-your-own AI assistant (optional): a chat that reasons over your key metadata (never the secret values) to answer "what should I rotate first?". Works with Anthropic, any OpenAI-compatible endpoint, or a local model via Ollama.
- Teams: self-hosted, so one instance is one team. Invite members, gate the irreversible actions to admins, and keep a shared, attributed workspace.
Screenshots
Stack
- Next.js 15 (App Router) + React 19, TypeScript
- NextAuth v5: magic-link (email) authentication
- Prisma ORM on PostgreSQL
- Tailwind CSS + a square, terminal-inspired design system (
docs/DESIGN_SYSTEM.md) - Secrets encrypted at rest (AES-256-GCM); SSRF-guarded outbound calls
Quick start (self-host)
The fastest path is the bundled Docker stack, with app + Postgres + a local mail catcher, no external services:
cp .env.example .env
echo "NEXTAUTH_SECRET=$(openssl rand -base64 32)" >> .env
echo "ENCRYPTION_KEY=$(openssl rand -base64 32)" >> .env
echo "ALLOWED_EMAILS=you@example.com" >> .env # invite-only
docker compose up --build
# App: http://localhost:3001 Magic-link inbox: http://localhost:8025
Prefer a prebuilt image? docker compose pull && docker compose up uses the published ghcr.io/rootzreggae/keystrok instead of building. See DEPLOYMENT.md for the bring-your-own-infra path (managed Postgres + SMTP).
Note on email: the bundled mail catcher traps everything locally (that's the inbox at
:8025), so solo use works with zero email setup, but team invites will never reach a real inbox. To invite teammates, setEMAIL_SERVER_*in.envto a real SMTP server (orRESEND_API_KEY); the Team page shows where invites are currently being delivered.
Local development
npm install
cp .env.example .env.local
npx prisma generate
npx prisma db push
npm run dev -- -p 3001 # http://localhost:3001
Key environment variables
| Variable | Purpose |
|---|---|
DATABASE_URL |
PostgreSQL connection string |
NEXTAUTH_URL / NEXTAUTH_SECRET |
App URL + session signing secret |
ENCRYPTION_KEY |
AES-256-GCM key for encrypting stored credentials (openssl rand -base64 32) |
EMAIL_SERVER_* / EMAIL_FROM |
SMTP for magic-link and invite email (a real server, or the bundled catcher) |
ALLOWED_EMAILS |
Invite-only sign-in allowlist |
CRON_SECRET |
Secret for the scheduled-job endpoints (repo re-scans, liveness checks) |
Project layout
app/: Next.js App Router ((authenticated)/routes,api/handlers,auth/pages)lib/: auth, crypto, the scanner, rotation policy, integrationscomponents/: the UI component systemprisma/schema.prisma: database schema (source of truth)docs/: design & API references
On how this is made
I'm a product designer who has spent years on developer and observability tools (among them Grafana's Frontend Observability and APM). I build Keystrok with AI as my engineering partner. The product, what it does, what it deliberately won't do (it never rotates a key on its own), and how it should feel to use, is mine. The implementation is a collaboration, written and reviewed with AI in the loop.
I mention the AI part plainly, not defensively. Judge the work, not the method: the self-host path is verified end to end, secrets are encrypted at rest, rotation is advisory and operator-gated, and the AI assistant only ever sees key metadata, never the secret values. Find a bug or a bad call? Open an issue. I'd rather fix it than defend it.
License
MIT: see LICENSE.





