Skip to main content
Glama

PQC Triage

Find the cryptography a quantum computer breaks first — and the exact year you must migrate by.

Live app Next.js 16 TypeScript strict License MIT Live feeds MCP agent tools Engine hndl@1.0.0 Integrity SHA-384

Live app · GitHub · API · Agent · Issues

Post-quantum migration is a calendar problem wearing a technical costume. Nobody argues about whether RSA-2048 falls to Shor's algorithm; everybody argues about when, and the honest answer is "whatever year a cryptographically relevant quantum computer arrives". Most inventories never get that far, because the first hard question is where the cryptography is.

PQC Triage imports a real dependency manifest and a real source excerpt, resolves every cryptographic surface to an algorithm, scores each one against published quantum resource estimates and the NIST transition dates, and commits a sealed migration plan with a start-by year for every surface.

Zero API keys. Runs offline on an embedded Postgres. Every number carries its citation.

The workbench: a diffraction plate of exposure fringes, the factor breakdown, and the decision panel


✨ Features

  • Real inventory. Parses package-lock.json, package.json, requirements.txt, go.mod, Gemfile.lock, composer.lock, Cargo.lock and pom.xml, then scans a source excerpt line by line for cryptographic call sites. Every surface carries the line it matched on.

  • A deadline, not a score. Mosca's rule: work must start by H − (X + Y) — the quantum horizon, minus the years the data must stay secret, minus the migration lead time. You get a year, not a percentage.

  • Explainable by construction. Six weighted factors per surface, each with its weight, its severity, its point contribution and the sentence that produced it.

  • Published quantum costs. Logical qubits and Toffoli counts for a Shor attack from the Gidney–Ekerå closed form for RSA, and the Häner et al. anchor scaled linearly for elliptic-curve discrete logs. Labelled as literature estimates, not predictions.

  • Honest live data. deps.dev release metadata, OSV advisories, arXiv preprints and the NIST news feed, each reporting live or fallback with a sealed dated sample that is never presented as current.

  • An agent interface that cannot cheat. MCP JSON-RPC 2.0 with eleven typed tools. The mutating tools call the same service functions the buttons do, scoped to one owner, with idempotency keys.

  • An audit chain you can replay. Every create, decision and retire appends a SHA-384 seal: seal_n = SHA-384(UTF-8(prevSeal) || canonicalJson(event_n)). Deletes leave tombstones so the chain never breaks.

  • An in-repo model, no API key. A multinomial Naive Bayes classifier trains from a seed corpus of realistic call sites plus every correction you teach it, and shows you the tokens that moved its answer.

  • A takeaway artifact. Markdown, JSON or CSV migration plan with every factor, every citation, the provenance table and the seals. Plus a revocable public report link.


Related MCP server: quantakrypto pqc-tools

🚀 Quickstart

git clone https://github.com/aniruddhaadak80/pqc-triage
cd pqc-triage
npm ci
npm run dev

Open http://localhost:3000. No environment variables are required. Local development, npm test and the browser suite all use an embedded PGlite Postgres at .pgdata/, which is a real Postgres compiled to WebAssembly — the same schema, the same SQL, the same indexes.

A first-time visitor is seeded with one survey imported from the sample manifest that ships in the repository, so the workbench has something real in it immediately.

Quality commands

npm run typecheck    # tsc --noEmit, strict
npm run lint         # eslint, zero warnings tolerated
npm run test         # vitest, 125 deterministic unit tests
npm run build        # next build
npm run test:browser # Playwright: the full journey on desktop and mobile (needs a build first)
npm run verify:live  # end-to-end proof against a deployment

Production environment variables

Documented in .env.example; all of them optional.

Variable

Required

What it does

DATABASE_URL

in production

Pooled Postgres connection string. Injected automatically by the Neon integration on Vercel.

NEXT_PUBLIC_SITE_URL

no

Overrides the public base URL used for canonical URLs, the sitemap, OpenGraph metadata and mcp.json.

PGLITE_DATA_DIR

no

Where the embedded database lives during local development. Defaults to .pgdata.

On a Vercel runtime without DATABASE_URL the app refuses to open a connection rather than quietly falling back to an ephemeral store. See assertStoreUsable in src/lib/db/client.ts.


🔌 API

Everything is owned by an anonymous HttpOnly session cookie, so there is no account to create. The first response from the edge proxy sets pqc_sid.

BASE=https://pqc-triage.vercel.app

# Health: proves the store with a real insert, read-back and delete
curl -s $BASE/api/health | jq '.store, .writeProbe, .engineVersion'

# Create a survey from a manifest and a source excerpt
SURVEY=$(curl -s -c jar -X POST $BASE/api/surveys \
  -H 'content-type: application/json' \
  -d '{"name":"billing-gateway","repoHint":"acme/gateway",
       "manifest":"{\"dependencies\":{\"jsonwebtoken\":\"^9.0.2\",\"node-forge\":\"^1.3.1\"}}",
       "source":"jwt.sign(p, s, { algorithm: \"HS256\" });\nconst legacy = crypto.createCipheriv(\"des-ede3\", key, iv);"}' \
  | jq -r '.survey.id')

# Read it back, with the analysis the UI renders
curl -s -b jar $BASE/api/surveys/$SURVEY | jq '.analysis.score, .analysis.band, .analysis.engineVersion'

# Decide on the worst surface
SURFACE=$(curl -s -b jar $BASE/api/surveys/$SURVEY | jq -r '.analysis.surfaces[0].surfaceId')
curl -s -b jar -X PATCH $BASE/api/surfaces/$SURFACE \
  -H 'content-type: application/json' \
  -d '{"decision":"migrate-now","decisionNote":"Hybrid KEM in the next release.","shelfLifeYears":27}'

# Read the persisted state back
curl -s -b jar $BASE/api/surveys/$SURVEY \
  | jq --arg s "$SURFACE" '.surfaces[] | select(.id==$s) | {decision, shelfLifeYears}'

# Run the engine over any primitive, with nothing stored
curl -s -X POST $BASE/api/mcp -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"assess_primitive","arguments":{"primitive":"rsa-2048","shelfLifeYears":25}}}'

# Replay the seal chain
curl -s -b jar "$BASE/api/integrity?survey=$SURVEY" | jq '.valid, .eventsChecked, .algorithm'

# Download the plan
curl -s -b jar -D- -o plan.md "$BASE/api/export?survey=$SURVEY&format=md"

# Retire it; the rows stay as tombstones and replay still verifies
curl -s -b jar -X DELETE $BASE/api/surveys/$SURVEY | jq '.tombstone, .seal'

Errors

One envelope for every endpoint and every tool call.

{ "error": { "code": "validation_error", "message": "The request body did not match the schema.", "details": [{ "path": "name", "message": "Too small: expected string to have >=1 characters" }] } }

Code

Status

When

validation_error

422

Schema or bounds failure

not_found

404

Missing, or owned by another session

conflict

409

State conflict

rate_limited

429

Best-effort write limiter tripped

unsupported_media_type

415

Wrong content type

internal_error

500

Anything unexpected, with no stack trace and no environment values


🤖 Agent console

Eleven typed tools over MCP-style JSON-RPC 2.0 at /api/mcp. The mutating tools call the same service functions the buttons on the site call.

{
  "mcpServers": {
    "pqc-triage": {
      "type": "http",
      "url": "https://pqc-triage.vercel.app/api/mcp"
    }
  }
}

The same configuration is served from public/mcp.json.

flowchart LR
  classDef data fill:#22d3ee,stroke:#0e7490,color:#083344
  classDef engine fill:#a78bfa,stroke:#6d28d9,color:#2e1065
  classDef agent fill:#34d399,stroke:#047857,color:#022c22
  classDef infra fill:#94a3b8,stroke:#475569,color:#0f172a
  classDef risk fill:#fb7185,stroke:#be123c,color:#4c0519
  Client["Any MCP client"]:::infra -->|POST jsonrpc| Endpoint["/api/mcp"]:::infra
  Endpoint --> Init["initialize + ownerToken"]:::agent
  Endpoint --> List["tools/list with schemas"]:::agent
  Endpoint --> Call["tools/call"]:::agent
  Call --> Read["list_signals / list_surveys / get_survey"]:::data
  Call --> Engine["assess_primitive / classify_primitive"]:::engine
  Call --> Mutate["import_survey / record_decision / delete_survey"]:::risk
  Mutate --> Service["service layer, same as the UI"]:::infra
  Service --> Store[("hosted Postgres")]:::infra
  Service --> Seal["SHA-384 chain append"]:::agent
  Read --> Feed["deps.dev / OSV / arXiv / NIST"]:::data

An agent that does not keep cookies takes the ownerToken from the initialize result and sends it back as params.ownerToken; it is an unguessable 128-bit capability scoped to that owner. Mutating tools accept an idempotencyKey; replaying the same key returns the first result instead of writing twice.


Architecture

flowchart TB
  classDef data fill:#22d3ee,stroke:#0e7490,color:#083344
  classDef engine fill:#a78bfa,stroke:#6d28d9,color:#2e1065
  classDef agent fill:#34d399,stroke:#047857,color:#022c22
  classDef infra fill:#94a3b8,stroke:#475569,color:#0f172a
  classDef risk fill:#fb7185,stroke:#be123c,color:#4c0519
  Browser["Browser"]:::infra --> Proxy["proxy.ts assigns the owner cookie"]:::infra
  Proxy --> Pages["App Router pages, server by default"]:::infra
  Browser --> Rest["REST routes"]:::infra
  Browser --> Rpc["JSON-RPC 2.0"]:::agent
  Pages --> Service["service.ts, the only writer"]:::infra
  Rest --> Service
  Rpc --> Service
  Service --> Engine["engine hndl@1.0.0"]:::engine
  Service --> Extract["extract.ts"]:::engine
  Service --> Registry["crypto-registry.ts"]:::engine
  Service --> Adapter["SqlExecutor adapter"]:::infra
  Adapter --> Neon["Neon Postgres, production"]:::data
  Adapter --> Pglite["PGlite, local and CI"]:::data
  Service --> Seal["seal.ts"]:::agent
  Service --> Live["live sources"]:::data
  Engine --> Pages

Server components are the default; client components exist only where there is interaction. The engine, the registry, the resource model and the classifier are pure TypeScript with no Node dependencies, which is why the browser can run the identical function the server does — the sweep on the workbench is not an approximation of the score, it is the score.

flowchart LR
  classDef data fill:#22d3ee,stroke:#0e7490,color:#083344
  classDef engine fill:#a78bfa,stroke:#6d28d9,color:#2e1065
  classDef agent fill:#34d399,stroke:#047857,color:#022c22
  classDef infra fill:#94a3b8,stroke:#475569,color:#0f172a
  classDef risk fill:#fb7185,stroke:#be123c,color:#4c0519
  Manifest["manifest paste"]:::data --> Detect["detectEcosystem"]:::engine
  Source["source excerpt"]:::data --> Scan["scanSource, line by line"]:::engine
  Detect --> Parse["parseManifest per ecosystem"]:::engine
  Parse --> Pkg["match the curated crypto package list"]:::engine
  Scan --> Hits["one hit per primitive, with line numbers"]:::engine
  Pkg --> Row["ExtractedSurface"]:::engine
  Hits --> Row
  Row --> Resolve["resolvePrimitive into the scoring table"]:::engine
  Resolve --> Enrich["enrich up to six dependencies"]:::data
  Enrich --> Persist["surfaces and surveys rows"]:::infra
  Persist --> Score["analyzeSurvey"]:::engine
  Score --> Bands["band and deadline per surface"]:::engine
  Bands --> Band["no live signal scores neutral, never zero"]:::risk
flowchart TB
  classDef data fill:#22d3ee,stroke:#0e7490,color:#083344
  classDef engine fill:#a78bfa,stroke:#6d28d9,color:#2e1065
  classDef agent fill:#34d399,stroke:#047857,color:#022c22
  classDef infra fill:#94a3b8,stroke:#475569,color:#0f172a
  classDef risk fill:#fb7185,stroke:#be123c,color:#4c0519
  In["primitive, role, X, H, decision"]:::engine --> Deadline["mustStartBy = H - (X + Y)"]:::engine
  In --> Bits["quantum bits from SP 800-57"]:::engine
  In --> QCost["Shor resource estimate"]:::data
  Deadline --> Harvest["F1 harvest 0.26"]:::engine
  Bits --> QB["F2 quantum bits 0.22"]:::engine
  Deadline --> Sched["F3 headroom 0.18"]:::engine
  In --> Class["F4 classical 0.14"]:::engine
  In --> Supply["F5 supply 0.12"]:::engine
  In --> Use["F6 usage 0.08"]:::engine
  Harvest --> Sum["score = 100 x sum(w x s)"]:::engine
  QB --> Sum
  Sched --> Sum
  Class --> Sum
  Supply --> Sum
  Use --> Sum
  Sum --> Band["bands at 75 / 55 / 35 / 15"]:::risk
  Sum --> Repl["replacement and its lead time Y"]:::engine
  Band --> Out["score, factors, mustStartBy, decryptableFrom, seal"]:::engine
  Repl --> Out

Data provenance

flowchart LR
  classDef data fill:#22d3ee,stroke:#0e7490,color:#083344
  classDef engine fill:#a78bfa,stroke:#6d28d9,color:#2e1065
  classDef agent fill:#34d399,stroke:#047857,color:#022c22
  classDef infra fill:#94a3b8,stroke:#475569,color:#0f172a
  classDef risk fill:#fb7185,stroke:#be123c,color:#4c0519
  Deps["deps.dev release metadata"]:::data --> Live["live"]:::infra
  Osv["OSV advisories"]:::data --> Live
  Arxiv["arXiv quant-ph / cs.CR"]:::data --> Live
  Nist["NIST news feed"]:::data --> Live
  Live --> Norm["NormalizedSignal: source, status, fetchedAt, href"]:::infra
  Live -.->|timeout or unreachable| Fallback["sealed dated sample"]:::risk
  Fallback --> Norm
  Norm --> Score["supply factor"]:::engine
  Norm --> Board["live signal board"]:::infra
  Fallback -->|never| User["user-created surfaces"]:::infra
  User --> Store[("hosted store")]:::data

Source

Used for

Endpoint

deps.dev

Latest release, publish date, license per dependency

api.deps.dev/v3alpha

OSV

Published advisory records per dependency

api.osv.dev/v1/query

arXiv

Current quant-ph / cs.CR work on post-quantum migration

export.arxiv.org/api/query

NIST news

Standards announcements, filtered for cryptography

www.nist.gov/news-events/news/rss.xml

NIST IR 8547

Transition dates: deprecate after 2030, disallow after 2035

cited, not fetched

NIST SP 800-57

Comparable strengths; Grover halves symmetric strength

cited, not fetched

Gidney & Ekerå 2021

RSA Shor cost: 6,189 logical qubits at n=2048

cited, not fetched

Häner et al. 2020

ECDLP anchor: 2,124 logical qubits at 256 bits

cited, not fetched

FIPS 203 / 204 / 205

ML-KEM, ML-DSA and SLH-DSA parameter sets

cited, not fetched

Each live response reports live or fallback per source, with the fetch time. When a feed is unreachable the app serves a sealed dated sample and says so, including the sample's own date. Fallback data never replaces user-created data, and a dependency with no live signal is scored neutral, never as if it were clean.

Deployment

flowchart TB
  classDef data fill:#22d3ee,stroke:#0e7490,color:#083344
  classDef engine fill:#a78bfa,stroke:#6d28d9,color:#2e1065
  classDef agent fill:#34d399,stroke:#047857,color:#022c22
  classDef infra fill:#94a3b8,stroke:#475569,color:#0f172a
  classDef risk fill:#fb7185,stroke:#be123c,color:#4c0519
  Push["push to main"]:::infra --> Ci["GitHub Actions on Node 22"]:::agent
  Ci --> Type["typecheck + lint + test + build"]:::agent
  Type --> Ok{"all green?"}:::risk
  Ok -->|no| Stop["blocked, nothing deploys"]:::risk
  Ok -->|yes| Ver["vercel --prod"]:::infra
  Ver --> Neon["Neon integration injects DATABASE_URL"]:::data
  Ver --> Alias["public production alias"]:::infra
  Neon --> Guard["no DATABASE_URL means refuse, never fall back"]:::risk
  Alias --> Live["scripts/verify-live.mjs proves 100+ checks"]:::agent
  Live --> Badge["README badge, mcp.json, sitemap, OG metadata"]:::infra

DATABASE_URL is injected by the Neon integration on Vercel. Both Postgres drivers are declared in serverExternalPackages, because PGlite's WASM binary and seed data must stay outside the server bundle. scripts/verify-live.mjs runs the whole loop against the public alias with real HTTP and fails loudly if the store is not the hosted one.

📁 Project map

User routes

Route

What it is for

Persistence

/

Product entry: live posture plate, the import form, current research

Reads the owner's newest survey

/surveys

Workspace. Filter by band and sort in the URL

Reads the owner's surveys

/surveys/new

The primary action: manifest plus source excerpt

POST /api/surveys

/surveys/[id]

The workbench: fringe plate, horizon sweep, per-surface triage

PATCH and POST on surfaces

/surveys/[id]?panel=integrity

Seal replay with the full event list

Reads the audit chain

/analyze

Cross-survey what-if across five horizons, with bulk commit

PATCH per survey when committed

/standards

Scoring table, resource model, engine factors, the classifier

POST and PUT on /api/classify

/agent

Live JSON-RPC 2.0 console with preloaded calls and a full-loop button

Whatever the called tool writes

/export

Export centre with a rendered preview

Reads, and GET /api/export downloads

/verify

Replay every chain in the session

GET /api/integrity

/share/[id]

Public read-only report, only when the owner published it

None. Revocable

API routes

Route

Methods

Responsibility

/api/health

GET

Proves the store with a real write probe

/api/live

GET

Normalized signals with per-source status

/api/surveys

GET, POST

List and import

/api/surveys/[id]

GET, PATCH, DELETE

Read, rename / publish / horizon, retire

/api/surveys/[id]/surfaces

GET, POST

List and add a surface by hand

/api/surfaces/[id]

GET, PATCH, DELETE

Decide, retune the shelf life, retire

/api/classify

GET, POST, PUT

Classify, teach, and export the learned table

/api/export

GET

Markdown, JSON or CSV download

/api/integrity

GET

Seal replay, optionally with the event log

/api/settings

GET, PUT

Horizon and thresholds for the session

/api/mcp

GET, POST

JSON-RPC 2.0 and the tool manifest

Domain

Path

Responsibility

src/lib/crypto-registry.ts

The scoring table: strength, quantum bits, NIST dates, replacement

src/lib/engine.ts

hndl@1.0.0: six weighted factors, Mosca deadline arithmetic, banding

src/lib/resource.ts

Shor resource estimates from the published models

src/lib/extract.ts

Manifest parsing per ecosystem, source scanning, package hints

src/lib/classifier.ts

nbc@1.0.0 multinomial Naive Bayes, exportable weights

src/lib/corpus.ts

Seed training set of realistic call sites

src/lib/canonical.ts

Canonical JSON and SHA-256

src/lib/seal.ts

Genesis, seal computation, chain replay

src/lib/service.ts

The only place state changes

src/lib/db/

SqlExecutor contract, Neon and PGlite adapters, additive migrations

src/lib/live/

The four public sources, timeouts, normalization, sealed fallback

src/lib/export.ts

Markdown, JSON and CSV builders


Security model and ownership

  • Anonymous ownership. An edge proxy assigns a 128-bit random HttpOnly cookie before anything else runs and forwards the id on an internal header, so the very first page load already knows the owner. Every query filters on session_id. Another session reading your survey gets 404, not 403.

  • Destructive operations. Retirement is a soft delete plus a sealed event. Rows stay as tombstones so replay never breaks; the public report link is revoked.

  • Abuse control. A 60-writes-per-minute in-process limiter per session, payload caps on every text field, a 60-surface import cap, and an allowlisted egress set. The limiter is documented as best-effort: a serverless runtime can recycle the process at any moment, so a durable limiter belongs in a hosted rate limiter or an edge rule, not in this application. There is deliberately no control here that pretends otherwise.

  • Input. Every body is validated with a Zod schema, every id is checked against a UUID pattern, every string is length-bounded, every SQL statement is parameterised, and no upstream payload reaches the UI unnormalized.

  • Secrets. There are none to leak. No API keys, no tokens, no client-side environment reads. .env.example documents shape only.

  • Publishing is explicit and revocable. /share/[id] returns 404 for anything the owner has not published.

  • Errors. No stack traces and no environment values reach a client.

Integrity and seal replay

flowchart TB
  classDef agent fill:#34d399,stroke:#047857,color:#022c22
  classDef infra fill:#94a3b8,stroke:#475569,color:#0f172a
  classDef risk fill:#fb7185,stroke:#be123c,color:#4c0519
  Write["create, decide, retire"]:::agent --> Canon["canonicalJson: keys sorted, arrays ordered"]:::agent
  Canon --> Seal["SHA-384(prevSeal + canonicalJson(event))"]:::agent
  Seal --> Head["surveys.last_seal and event_count"]:::infra
  Replay["GET /api/integrity"]:::infra --> Genesis["genesis derived from the survey id"]:::agent
  Genesis --> Recompute["recompute every seal in seq order"]:::agent
  Recompute --> Verdict["valid, or the first broken seq with a reason"]:::risk
  Delete["delete"]:::risk --> Tomb["soft delete, row retained"]:::risk
  Tomb --> Replay

Timestamps are stored as application-generated ISO-8601 text rather than database types, which keeps the chain byte-identical across the hosted and embedded adapters. The unit tests recompute every digest with Node's crypto directly, so the implementation and the oracle would both have to change for the suite to pass.

User journey

flowchart LR
  classDef data fill:#22d3ee,stroke:#0e7490,color:#083344
  classDef engine fill:#a78bfa,stroke:#6d28d9,color:#2e1065
  classDef agent fill:#34d399,stroke:#047857,color:#022c22
  classDef infra fill:#94a3b8,stroke:#475569,color:#0f172a
  classDef risk fill:#fb7185,stroke:#be123c,color:#4c0519
  Visit["Land on /"]:::infra --> Import["Paste a manifest and a source excerpt"]:::data
  Import --> Read["Read the fringe plate and the factor table"]:::engine
  Read --> Sweep["Move the quantum horizon"]:::engine
  Sweep --> Decide["Record a decision with a rationale"]:::risk
  Decide --> Residual["Watch the residual score fall"]:::engine
  Residual --> Export["Download the migration plan"]:::data
  Export --> Share["Publish or revoke a report link"]:::agent
  Share --> Verify["Replay the seal chain"]:::agent
  Verify --> Retire["Retire the survey, tombstones retained"]:::risk

🗺️ Roadmap

Now — shipped and verified

flowchart LR
  classDef ok fill:#34d399,stroke:#047857,color:#022c22
  A["Extract manifests and source"]:::ok --> B["Score with hndl@1.0.0"]:::ok
  B --> C["Agent tools over JSON-RPC"]:::ok --> D["Sealed export"]:::ok
  • Import a manifest and a source excerpt, and see every cryptographic surface with the line it matched.

  • Move the quantum horizon and watch every must-migrate-by year recompute, then commit it.

  • Record a decision and a rationale, and watch the residual score fall.

  • Hand the plan to a reviewer as Markdown, JSON or CSV, and prove it with a seal replay.

Next

flowchart LR
  classDef next fill:#22d3ee,stroke:#0e7490,color:#083344
  A["Package graph reachability"]:::next --> B["Hybrid KEM rollout checklist"]:::next
  B --> C["Saved plan versions"]:::next --> D["Signed share links"]:::next
  • Reachability: score a surface by whether anything actually calls it, so a dormant RSA helper does not outrank the TLS terminator. Outcome: the worklist matches reality, not just the manifest.

  • Hybrid rollout checklist: per surface, the concrete steps for a classical-plus-PQ handshake, including certificate size and MTU implications. Outcome: an engineer can start on Monday.

  • Saved plan versions: snapshot a survey's analysis so a migration can be reviewed as a diff. Outcome: you can prove what changed between two reviews.

  • Signed share links: HMAC the public report path so a link cannot be guessed or replayed after revocation.

Later

flowchart LR
  classDef later fill:#a78bfa,stroke:#6d28d9,color:#2e1065
  A["SBOM ingest"]:::later --> B["Hardware inventory"]:::later
  B --> C["Policy-as-code export"]:::later
  • SBOM ingest: read CycloneDX and SPDX directly, so you do not have to paste a manifest.

  • Hardware inventory: model HSM and KMS boundaries, which is where real key custody lives.

  • Policy-as-code export: emit the plan in the format your compliance tooling already reads.


🤝 Contributing

Issues and pull requests are welcome. Please read CONTRIBUTING.md first, and SECURITY.md before reporting anything security-related.

npm ci && npm run typecheck && npm run lint && npm run test && npm run build

License

MIT © PQC Triage contributors. See LICENSE.

Not security advice. PQC Triage is an engineering aid. It does not certify a system as quantum-safe, it models a calendar rather than your threat model, and its resource figures are published literature values under stated assumptions, not forecasts. Do not paste production secrets: what you import is stored so it can be scored.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables AI agents to sign decisions with post-quantum cryptographic proofs and maintain secure audit trails for compliance. It provides tools for stamping events, verifying chain integrity, and exporting audit data across industries like finance and healthcare.
    4
    52 npm
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Post-quantum readiness for AI coding agents: scans code for quantum-vulnerable cryptography (RSA/ECDH/ECDSA/DH) and returns NIST ML-KEM/ML-DSA/SLH-DSA (and hybrid) migration guidance, with fix verification and dependency checks. Content-based, advisory tools only; runs local (npx @quantakrypto/mcp) or as a hosted OAuth endpoint.
    16
    12
    Apache 2.0
  • A
    license
    A
    quality
    A
    maintenance
    An MCP server that scans local codebases for quantum-vulnerable cryptography (secp256k1, Ed25519, RSA, etc.) and CI signing commands, classifying each finding as quantum-broken, post-quantum, or neither. It runs entirely locally with no network calls, providing a deterministic inventory for AI agents.
    5
    1,493 PyPI
    Apache 2.0