Skip to content
Conduit

mcp server

shorwatch

Measure the public key a host is actually serving, corroborate it against Certificate Transparency, and compute a post-quantum harvest deadline you can triage and export.

aniruddhaadak80/shorwatchcommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 22, 2026

Shorwatch

Know which public keys an adversary can already harvest.

Live app MIT TypeScript Next.js 16 Node 22 Live feeds MCP Tests CI

Live App · Source · Agent · API · Issues


A cryptographic inventory is a questionnaire, and questionnaires lie.

Shorwatch reads the certificate a server is actually serving, parses the SubjectPublicKeyInfo out of the DER, checks how long that exact public key has been sitting in public Certificate Transparency logs, and computes the date a quantum adversary breaks it — showing every factor that produced the number.

It takes about ten seconds to get your first finding, needs no account and no API key, and every constant it uses is printed on /standards.

✨ Features

  • Measures the key, not a claim about it. A real TLS handshake on port 443; the RSA modulus size, named curve and algorithm OID are read from the DER bytes that came off the socket.
  • Answers the question a handshake cannot. The exact SPKI fingerprint is matched against Certificate Transparency logs, so you learn since when the key has been harvestable rather than only what it is today.
  • Explains every score. Five weighted factors produce an exposure score and a projected quantum deadline. Each factor shows the measurement behind it, and the result is sealed with SHA-384 so a doctored score is detectable.
  • Genuinely post-quantum-aware. Recognises ML-DSA, ML-KEM and OpenSSL hybrid key-agreement OIDs, so a key that is already quantum-safe is reported as quantum viable instead of being falsely flagged.
  • Re-ranks your queue from a real model. The qubit staircase on /analysis recomputes every deadline as you change the assumed machine, so the first rotation you do is the first one that matters.
  • Leaves with an artefact. An OpenSSL 3.5 hybrid configuration generated from the key you measured, a runbook printing the deadline arithmetic, and a sealed JSON dossier.
  • Scriptable. Eleven typed tools over JSON-RPC 2.0, with idempotent mutations that travel the same service layer the buttons do.

🚀 Quickstart

git clone https://github.com/aniruddhaadak80/shorwatch.git
cd shorwatch
npm ci
npm run dev

Open http://localhost:3000, type a host, and press Measure now.

No environment variables are required. Development runs against an embedded PGlite database created on first use, and every live source is keyless.

Production variables

VariableRequiredPurpose
DATABASE_URLYes in productionNeon Postgres (or any Postgres) connection string. Without it a production build refuses to start rather than silently using the embedded store.
NEXT_PUBLIC_SITE_URLNoPublic base URL used for metadata, sitemap and OpenGraph. Defaults to the verified production alias.
SHORWATCH_PGLITE_DIRNoEmbedded database directory for local development. Defaults to ./.pglite.
SHORWATCH_ALLOW_EMBEDDED_STORELocal onlySet to 1 to let next start run without DATABASE_URL. Never set this on a deployment.

Quality gates

npm run typecheck   # tsc --noEmit, strict
npm run lint        # eslint, zero warnings tolerated
npm run test        # 93 unit tests
npm run build       # production build
npm run check       # all four in sequence
npm run test:e2e    # browser journey (starts its own server)
npm run verify      # live verification against BASE_URL
npm run screenshots  # regenerate docs/ from a deployment

🔌 API

Every response uses one envelope: { "data": … } on success, and { "error": { "code", "message", "field?" } } on failure. List endpoints are paginated and every read is scoped to the calling session.

BASE=https://shorwatch-aniruddha-adaks-projects.vercel.app

# Health, including the real production store check
curl -s $BASE/api/health

# Measure a host and persist the finding
curl -s -X POST $BASE/api/watches \
  -H 'content-type: application/json' \
  -d '{"host":"vercel.com","label":"main site"}'

# Read it back
curl -s $BASE/api/watches/<id>

# Change the confidentiality horizon, then re-measure
curl -s -X PATCH $BASE/api/watches/<id> \
  -H 'content-type: application/json' \
  -d '{"retentionYears":25}'

curl -s -X POST $BASE/api/watches/<id>/probe

# Replay the audit chain
curl -s $BASE/api/watches/<id>/decide

# Export the migration bundle
curl -s "$BASE/api/export?id=<id>&format=openssl" -o openssl.cnf

# Delete, which requires the record's current engine seal
curl -s -X DELETE $BASE/api/watches/<id> \
  -H 'content-type: application/json' \
  -d '{"seal":"<engine.seal from the read-back>"}'

Agent interface

The endpoint is a live MCP-style JSON-RPC 2.0 server supporting initialize, tools/list and tools/call.

curl -s -X POST https://shorwatch-aniruddha-adaks-projects.vercel.app/api/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"probe_host","arguments":{"host":"arxiv.org"}}}'
ToolKindPurpose
probe_hostanalysisLive handshake, CT corroboration and a full engine result. Stores nothing.
list_watchesreadThe caller's watches with their latest summary.
get_watchreadOne watch with every stored key observation.
rank_watchesreadEvery watch ordered by deadline urgency.
verify_integrityreadReplays chains and names the first broken link.
export_migration_bundlereadOpenSSL config, runbook and JSON dossier.
create_watchwriteCreates a watch, measures it live, persists the observation.
update_watchwriteChanges label, retention or the machine model.
record_decisionwriteRecords the triage decision, same endpoint as the buttons.
delete_watchwriteSoft-deletes behind a seal check, leaving a tombstone.
set_machine_modelwriteStores the assumed adversary machine.

Mutations accept idempotencyKey, so retrying never duplicates. The live configuration is served at /mcp.json.

📁 Project map

User routes

RoutePurpose
/Product entry with a live measurement that stores nothing
/watchesWorkspace: create, filter, sort and paginate watches
/watches/[id]Dynamic detail: the measured certificate, factor ledger, resource estimate, decision, edit and delete
/analysisThe qubit staircase and the ranked queue
/standardsEvery constant the engine uses, with its source
/agentLive JSON-RPC console with preloaded one-click calls
/exportMigration runbook, OpenSSL configuration and JSON dossier
/verifyAudit chain replay for every record
/settingsConfidentiality horizon and assumed machine

API routes

RouteMethodsPurpose
/api/healthGETReal store round-trip and adapter identity
/api/watchesGET, POSTList with filters, create and measure
/api/watches/[id]GET, PATCH, DELETERead with observations, update, guarded delete
/api/watches/[id]/probePOSTRe-measure the host from the wire
/api/watches/[id]/decideGET, POSTRecord a decision, or replay this chain
/api/analysisGETThe ranked queue plus the session's settings
/api/integrityGETReplay every visible chain
/api/exportGETDownload the bundle as text, OpenSSL config or JSON
/api/settingsGET, PATCHRead and write the stored assumptions
/api/mcpGET, POSTJSON-RPC 2.0 endpoint

Library

PathResponsibility
src/lib/der.tsDER reader that extracts and fingerprints the SPKI
src/lib/engine/Scoring engine, resource model and the quantum advisor
src/lib/integrity/seal.tsCanonical JSON and the SHA-384 chain
src/lib/sources/Live adapters and the sealed offline fallback
src/lib/db/Schema, Neon adapter, PGlite adapter
src/lib/service.tsThe single service layer every write path uses
src/lib/mcp/tools.tsTool definitions and handlers
src/proxy.tsAnonymous session ownership

🧭 How it works

System architecture

graph LR
  V[Visitor] --> P[Next.js App Router]
  P --> R[REST routes]
  P --> M[MCP JSON-RPC]
  R --> S[Service layer]
  M --> S
  S --> E[Engine]
  S --> D[(Neon Postgres)]
  S --> L[Live sources]
  E --> D
  classDef live fill:#22d3ee,color:#0b1220,stroke:#0e7490
  classDef eng fill:#a78bfa,color:#0b1220,stroke:#7c3aed
  classDef ag fill:#34d399,color:#0b1220,stroke:#059669
  classDef ext fill:#fbbf24,color:#0b1220,stroke:#b45309
  classDef risk fill:#fb7185,color:#0b1220,stroke:#be123c
  classDef inf fill:#94a3b8,color:#0b1220,stroke:#64748b
  class L live
  class E eng
  class M ag
  class D inf
  class V,P,R,S inf

Data pipeline and the sealed fallback

graph TB
  H[Host on port 443] -->|DER certificate| X[DER reader]
  X --> O[SPKI fingerprint]
  O --> CT[Certificate Transparency]
  O --> EN[Engine]
  DNS[DNS-over-HTTPS] --> EN
  SURF[InternetDB] --> EN
  CT --> EN
  EN --> SC[(Score and seal)]
  CT -. unreachable .-> FB[Sealed offline sample]
  classDef live fill:#22d3ee,color:#0b1220,stroke:#0e7490
  classDef eng fill:#a78bfa,color:#0b1220,stroke:#7c3aed
  classDef ext fill:#fbbf24,color:#0b1220,stroke:#b45309
  classDef risk fill:#fb7185,color:#0b1220,stroke:#be123c
  classDef inf fill:#94a3b8,color:#0b1220,stroke:#64748b
  class H,O,CT,DNS,SURF live
  class X,EN,SC eng
  class FB risk
  class V inf

Fallback data is always labelled status: "fallback" and is never merged into a user record. A user-created finding is never replaced by fallback data.

The deterministic engine

graph LR
  K[Measured key] --> Q[Quantum weakness]
  K --> P[Public exposure]
  R[Retention horizon] --> O[Retention overlap]
  M[Machine model] --> L[Lead time]
  N[Address surface] --> A[Attack surface]
  Q & P & O & L & A --> W[Weighted sum]
  V[VQC advisor] --> W
  W --> D[Score and verdict]
  D --> S[SHA-384 seal]
  classDef live fill:#22d3ee,color:#0b1220,stroke:#0e7490
  classDef eng fill:#a78bfa,color:#0b1220,stroke:#7c3aed
  classDef ag fill:#34d399,color:#0b1220,stroke:#059669
  classDef inf fill:#94a3b8,color:#0b1220,stroke:#64748b
  class K,P,N live
  class Q,O,L,A,W,D,S eng
  class V ag
  class R,M inf

Weights: quantum weakness 0.30, public exposure 0.22, retention overlap 0.20, lead time 0.16, attack surface 0.12. Sum to exactly 1.00.

Agent sequence

sequenceDiagram
  participant C as Agent client
  participant J as /api/mcp
  participant S as Service layer
  participant D as Postgres
  C->>J: initialize
  J-->>C: protocolVersion, serverInfo
  C->>J: tools/list
  J-->>C: 11 typed tools
  C->>J: tools/call create_watch
  J->>S: createWatch(host)
  S->>D: INSERT watch + audit event
  S->>D: INSERT observation
  S-->>J: watch, observation, engine
  J-->>C: content + structuredContent
  C->>J: tools/call record_decision (same key)
  J->>S: decideWatch(id)
  S-->>J: seal
  J-->>C: replayed seal, no duplicate

Integrity and seal replay

graph TB
  G[Genesis 0000...] --> E1[Event 1 create]
  E1 --> E2[Event 2 probe]
  E2 --> E3[Event 3 decide]
  E3 --> E4[Event 4 delete]
  E4 --> T[Tombstone retained]
  E3 -. tamper .-> BR[First broken link named]
  classDef live fill:#22d3ee,color:#0b1220,stroke:#0e7490
  classDef ag fill:#34d399,color:#0b1220,stroke:#059669
  classDef risk fill:#fb7185,color:#0b1220,stroke:#be123c
  classDef inf fill:#94a3b8,color:#0b1220,stroke:#64748b
  class G,E1,E2,E3 live
  class E4,T ag
  class BR risk

seal_n = SHA-384( UTF-8(seal_{n-1}) || canonicalJson(event_n) ), where canonical JSON sorts object keys recursively and preserves array order. Delete is a soft delete so the chain stays replayable.

Deployment

graph LR
  G[GitHub main] --> C[GitHub Actions]
  C --> T[typecheck]
  C --> L[lint]
  C --> U[unit tests]
  C --> B[production build]
  B --> V[Vercel production]
  V --> N[(Neon Postgres)]
  classDef live fill:#22d3ee,color:#0b1220,stroke:#0e7490
  classDef ag fill:#34d399,color:#0b1220,stroke:#059669
  classDef ext fill:#fbbf24,color:#0b1220,stroke:#b45309
  classDef inf fill:#94a3b8,color:#0b1220,stroke:#64748b
  class G,V,N live
  class C,T,L,U,B ag
  class V,N live
  class X inf

🔐 The engine

shorwatch-quantum 1.0.0 returns a score, itemised factors with their weights and contributions, a verdict, an actionable recommendation, a projected CRQ date and a SHA-384 seal. The UI, the REST endpoint and the MCP tool all call the same function.

Anchored on Gidney & Eakerå (2019): 4,098 logical qubits and ~20,000,000 physical qubits at p = 1e-3 factor RSA-2048 in 8 hours.

L(n)   = 4098 · (n / 2048) · (log₂n / 11)
d(p)   = round(13 · log₂(1/p) / log₂(1000)), minimum 3
P(n,p) = L(n) · (d / 13)² · (20000000 / 4098)
crq(n) = 2035 + 7.7 · log₂(n / 2048), clamped to 2028–2060

The quantum advisor is a two-qubit variational circuit evaluated by exact statevector arithmetic — no sampling, so the engine stays deterministic and the REST and agent paths return byte-identical results. Its bounded ±3.5 point adjustment is derived from ⟨Z₀⟩ and ⟨Z₀Z₁⟩ with frozen, published weights.

🗺️ Roadmap

Now

  • Measure the live key from a real TLS handshake and parse its SPKI
  • Corroborate the key against Certificate Transparency by fingerprint
  • Publish a versioned, explainable engine with unit-tested vectors
  • Ship eleven typed MCP tools over JSON-RPC 2.0
  • Export an OpenSSL 3.5 hybrid configuration and a runbook
graph LR
  A[Live measurement] --> B[Engine]
  B --> C[Agent tools]
  B --> D[Export bundle]
  classDef live fill:#22d3ee,color:#0b1220,stroke:#0e7490
  classDef eng fill:#a78bfa,color:#0b1220,stroke:#7c3aed
  classDef ag fill:#34d399,color:#0b1220,stroke:#059669
  class A live
  class B eng
  class C,D ag

Next

  • Passive DNS and SAN enumeration, so a watch covers an estate rather than one hostname
  • A scheduled re-probe, so a key rotation is detected and the chain records the change automatically
  • Client-side compatibility matrix, answering "can my oldest client still negotiate a hybrid group?"
graph LR
  A[Passive DNS] --> C[Estate view]
  B[Scheduled re-probe] --> C
  C --> D[Client compatibility]
  classDef live fill:#22d3ee,color:#0b1220,stroke:#0e7490
  classDef eng fill:#a78bfa,color:#0b1220,stroke:#7c3aed
  classDef ag fill:#34d399,color:#0b1220,stroke:#059669
  class A,B live
  class C eng
  class D ag

Later

  • Signed findings, so an exported dossier can be attested to a key
  • Bring-your-own-store, so a team can keep findings in their own database
  • A wider circuit, replacing the two-qubit advisor once a labelled corpus exists to train against
graph LR
  A[Signed findings] --> B[Bring your own store]
  B --> C[Wider circuit]
  classDef live fill:#22d3ee,color:#0b1220,stroke:#0e7490
  classDef eng fill:#a78bfa,color:#0b1220,stroke:#7c3aed
  classDef ag fill:#34d399,color:#0b1220,stroke:#059669
  class A,B ag
  class C eng

🔒 Security model

  • No accounts. An HTTP-only cookie holds a 128-bit random owner id, set in src/proxy.ts before any render.
  • No cross-session access. Every query is scoped by owner_id; another session's record returns 404 rather than 403, so it does not confirm existence.
  • Guarded deletion. DELETE requires the record's current engine seal, which only something that could already read the record knows.
  • Auditable history. Every write appends to a SHA-384 chain, and /verify names the first broken link.
  • Input validation. Hosts, labels, notes, retention and machine models are bounded and enum-checked; SQL is always parameterised.
  • Abuse control. Per-session write limits. On serverless these are best-effort and per instance; the durable limits are the unique constraints, the bounded list endpoints and owner scoping. See SECURITY.md.

📡 Data provenance

SourceWhat it providesAttribution
TLS handshakeThe certificate and key actually servednode:tls
Cert SpotterCertificate Transparency issuancessslmate.com
Google DNS-over-HTTPSCAA and address recordsdevelopers.google.com
Shodan InternetDBAddress-level exposureinternetdb.shodan.io

Every response carries its source label, upstream URL, fetch time and an explicit live or fallback status.

⚠️ Safety disclaimer

Shorwatch reports measured facts and an explicitly published model. The cryptographically relevant quantum date is an order-of-magnitude educational projection, not a forecast, and nothing here is security advice. Validate every migration decision with your own cryptographic review and current standards guidance. The exported OpenSSL configuration is a starting point for review, never a drop-in deployment. Only point Shorwatch at hosts you are authorised to scan.

🤝 Contributing

Contributions are welcome, especially for Hacktoberfest. Good first tasks: adding an algorithm OID with a test fixture, adding a post-quantum parameter set, or improving an accessible state. Read CONTRIBUTING.md for the quality gates and the ground rules — notably that a change to a score bumps ENGINE_VERSION, and that tests must fail before the fix.

graph LR
  A[Fork] --> B[Run npm run check]
  B --> C[Open a pull request]
  C --> D[CI]
  D --> E[Review]
  classDef live fill:#22d3ee,color:#0b1220,stroke:#0e7490
  classDef ag fill:#34d399,color:#0b1220,stroke:#059669
  classDef ext fill:#fbbf24,color:#0b1220,stroke:#b45309
  class A live
  class B,C,D ag
  class E ext

📄 License

MIT © Aniruddha Adak

↑↓ navigate openesc close