Inspiration
In 2018, a malicious actor gained control of event-stream, a npm package downloaded 8 million times, and injected a Bitcoin-stealing backdoor that sat undetected for two months. The developers whose apps were compromised never directly installed that package — it was a transitive dependency, three levels deep, invisible to them.
That's the problem. Modern codebases are icebergs. What you wrote is the tip. Everything underneath — the packages you depend on, the secrets you accidentally committed, the Dockerfile someone copy-pasted from Stack Overflow, the API route nobody put auth on — is the part that gets you breached.
Enterprise tools exist for this (Snyk, GitGuardian, Trivy). They're excellent. They're also built for SOC teams with budgets, onboarding time, and security engineers to read the output. The individual developer or student team shipping a hackathon project at 3am has none of those things.
Specter is the version of that for everyone else.
What it does
Paste a public GitHub repo URL. In under two minutes, Specter runs five automated security scanners in parallel and maps everything it finds into a live 3D threat visualization:
- DepChain — recursively walks your
package.jsondependency tree (3 levels deep), queries OSV.dev's vulnerability database for every package, and renders the full tree in 3D with compromised nodes highlighted in red. - GhostCommit — scans the last 30 commits' diffs for leaked secrets using both vendor-specific regex patterns (AWS keys, GitHub tokens, Stripe keys, OpenAI keys) and Shannon entropy analysis to catch unknown token formats no regex would match.
- LayerScan — parses your Dockerfile instruction by instruction, flagging anti-patterns like
curl | sh, secrets inENVorRUNlayers, missingUSERdirectives (container runs as root), and unpinned base images. - APIBleed — detects unauthenticated API endpoints across Express, FastAPI, Flask, and Next.js App Router by matching route declarations and checking for auth-hint keywords within a 400-character window of each handler.
- EnvTrace — checks committed
.envfiles for real (non-placeholder) values, validates.gitignorecovers.env, scans.env.examplefor accidentally included credentials, and searches source files for hardcoded credential patterns.
All findings feed a weighted threat score:
$$\text{score} = \min\Bigl(\min(d \times 15,\ 40) + \min(v \times 8,\ 30) + \min(s \times 10,\ 20) + \min(a \times 5,\ 10),\ 100\Bigr)$$
where \(d\) = critical Docker/Env findings, \(v\) = vulnerable dependencies, \(s\) = leaked secrets, \(a\) = unsecured API endpoints. Each category is capped before summing so no single finding type dominates the score.
The AI layer (Gemini 2.0 Flash via OpenRouter) receives only the top 6 critical/high findings and returns plain-English explanations — why it's dangerous, how to fix it, and a real breach where this exact class of vulnerability was exploited. Critically, the AI never makes detection decisions. Every finding comes from deterministic code. The LLM writes the explanation after the fact. If the AI call fails, the scan result is completely unaffected.
How we built it
Stack: Next.js 15 (App Router) + TypeScript for both frontend and API routes in one deploy. Three.js via @react-three/fiber and @react-three/postprocessing for the 3D scene. Supabase (Postgres) for scan persistence and a 6-hour result cache keyed by repo URL. Zustand for client state. Octokit for GitHub API access. jsPDF for report export. n8n Cloud for a push-event webhook that auto-triggers scans and emails results.
Architecture decision that mattered most: The scan API route (/api/scan/start) returns a scanId immediately — fire-and-forget — and all five scanners run via Promise.allSettled in the background. The client polls /api/scan/[scanId]/status. This means one failing scanner never kills the other four, and the user sees live per-scanner progress instead of a blank spinner for 90 seconds.
3D scene design: Every visual encoding maps to real data — dependency depth maps to distance from the center sphere, CVE presence maps to red node color, auth status maps to spoke color, threat score maps to the core sphere's color (blue → amber → red). Bloom post-processing is applied with a luminance threshold so only genuinely critical-severity emissive elements glow. The goal was a NORAD situation room, not a dark-mode SaaS dashboard.
Challenges we ran into
- Next.js 15 breaking changes — the async
paramsAPI in App Router required rewriting every dynamic route handler. - Serverless function timeouts — a scan hitting GitHub, npm registry, and OSV.dev across 5 parallel scanners could easily exceed Vercel's function timeout. Solved with the fire-and-forget architecture above.
- False positive tuning — the Shannon entropy threshold (4.5 bits/character) had to be calibrated to avoid flagging every long hash or UUID as a secret while still catching real tokens that don't match known vendor patterns.
- Supabase error handling —
.single()throws on no-row instead of returning null in the JS client, requiring explicitmaybeSingle()usage throughout. - 3D performance — rendering hundreds of dependency nodes with bloom at 60fps required moving per-frame calculations into
useFramehooks with selective Zustand subscriptions so the 3D scene doesn't re-render on every poll tick.
What we learned
That the line between "this is a demo tool" and "this is something people would actually use" is mostly about what you don't build. Every scanner in Specter has explicit, documented limitations — DepChain only handles npm, GhostCommit only covers the last 30 commits, LayerScan reads Dockerfile text not actual image layers. Knowing where your tool's signal ends is what makes the signal it does produce trustworthy.
We also learned that separating deterministic detection from AI explanation isn't just an architectural nicety — it's what lets you defend every single finding under questioning without saying "the model said so."
What's next for Specter
Multi-ecosystem dependency scanning (PyPI, crates.io, Go modules). Private repo support via OAuth. Replacing the in-memory rate limiter with a Redis-backed distributed one. And the longer-term roadmap this prototype feeds into: a CI/CD Pipeline Security Posture Management SaaS — an open-source CLI (pipeaudit) as the community entry point, with a paid continuous-monitoring dashboard built on top. The five-scanner architecture, threat-scoring model, and deterministic-detection pattern built here are the direct foundation for that.
Built With
- jspdf
- n8n
- next.js
- node.js
- octokit
- openrouter
- osv-dev
- postgresql
- react-three-fiber
- shannon-entropy
- supabase
- tailwind-css
- three.js
- typescript
- vercel
- webgl
- zustand
Log in or sign up for Devpost to join the conversation.