Show HN: EasyDocs – generate OpenAPI docs from real traffic EasyDocs is a new open-source tool that generates accurate OpenAPI 3.0 specs from real API traffic using an AI model, running entirely on the user's machine with no data sent externally. It supports local-first operation, PII redaction, and framework-native accuracy, and includes features like version history, spec diffing, and a GitHub Action for PR comments. The API spec you can actually trust — generated on your machine, from real traffic. Project page: rubenglez.dev/easydocs https://rubenglez.dev/easydocs EasyDocs generates accurate, up-to-date OpenAPI 3.0 specs from your API's real traffic, running entirely on your machine with nothing sent to anyone. Add one line; no spec files to write, no annotations to maintain. Your docs describe what the API actually does, not what you thought it did when you last touched the YAML. Under the hood an AI model turns observed requests and responses into the spec — richer than mechanical type-merging — and you stay in control of what ships. Local-first — your traffic never leaves your machine. No cloud, fully self-hostable. Works fully offline — point it at a local Ollama model; no API key required, and no model vendor ever sees your data. PII-safe by default — secrets and personal data passwords, tokens, emails, card numbers are detected and redacted before the payload reaches a hosted AI provider, and flagged in the docs. Open-source & free — no SaaS lock-in, no paywalled features. Framework-native accuracy — adapters capture true route templates /users/:id , not the concrete URLs /users/123 a proxy sees. Accurate, not mechanical — an AI model reads real requests and responses to produce richer specs real descriptions, detected auth schemes , not the bare schema a type-merger infers. Accuracy is measured against hand-written ground truth — see the benchmark /RubenGlez/easydocs/blob/main/BENCHMARK.md . Route your requests through the EasyDocs proxy. Nothing to install in your project. npx @easydocs/cli proxy --project=my-api --port=3999 Then send requests through the proxy: http://localhost:3999?target=https://api.example.com/users npm install @easydocs/express js import { easydocs } from "@easydocs/express"; app.use easydocs { project: "my-api" } ; // all your existing routes stay the same npm install -D @easydocs/dashboard npx @easydocs/cli dashboard → http://localhost:4999 The dashboard also tracks version history: each endpoint records how its spec evolved over time, with a field-level diff between any two versions. Or export to a file: npx @easydocs/cli export openapi.json npx @easydocs/cli export --yaml openapi.yaml Commit your exported spec openapi.json and let EasyDocs comment the field-level changes on every PR. Diff two spec files directly: The diff is grouped by endpoint and each change is tagged breaking / additive / non-breaking, so a removed response field reads differently from a description tweak. npx @easydocs/cli diff old.json new.json human-readable summary npx @easydocs/cli diff old.json new.json --markdown PR-comment Markdown npx @easydocs/cli diff old.json new.json --fail-on=breaking exit 3 on a breaking change --fail-on accepts none default, never fails , breaking fail on any breaking change , or any fail on any change at all . Or drop in the GitHub Action — it diffs the committed spec against the base branch and posts a sticky comment updated in place on each push : .github/workflows/easydocs.yml name: API spec diff on: pull request permissions: pull-requests: write jobs: spec-diff: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: RubenGlez/easydocs@v1 with: spec: openapi.json fail-on: breaking optional; default 'none' comment-only By default the check is informational — it comments the diff and never fails the build. Set fail-on: breaking or any to turn it into a review gate that fails the PR when the spec changes cross that threshold; the comment is still posted. diff compares two spec files . drift compares your committed spec against what your API actually does — the spec EasyDocs derives from real traffic. It answers "is my spec still true?", not "did my spec change?". npx @easydocs/cli drift openapi.json against locally captured traffic npx @easydocs/cli drift openapi.json --markdown PR-comment Markdown npx @easydocs/cli drift committed.json live.json or compare two files directly It surfaces three kinds of divergence: endpoints and fields observed in traffic but missing from your spec your docs are stale , things documented but never observed dead or un-exercised , and values where your spec contradicts reality. This is the one check only EasyDocs can run — it's the only tool holding both the committed spec and the live traffic at the same time. Like diff , it's informational and never fails the build. See it in one command — no setup, no API key. Clone this repo and run: pnpm demo:drift It seeds a sample scenario observed traffic vs. a drifted openapi.json and prints the report, so you can see all three kinds of drift before wiring up your own API. - Middleware or proxy intercepts every request and response - A background queue feeds the captured data to an AI model — nothing blocks your request - The AI generates or updates an OpenAPI 3.0 Operation object for that endpoint - Response-shape hashing skips re-processing when the structure hasn't changed - Specs are stored in SQLite default or Postgres - The dashboard reads from that database and renders live docs | Package | Framework | |---|---| @easydocs/express | @easydocs/fastify @easydocs/hono @easydocs/nestjs @easydocs/nextjs @easydocs/h3 @easydocs/elysia @easydocs/trpc @easydocs/cli Set one environment variable: OpenAI OPENAI API KEY=sk-... Anthropic ANTHROPIC API KEY=sk-ant-... DeepSeek DEEPSEEK API KEY=sk-... Ollama local, no key needed configure in code: easydocs { ai: { provider: 'ollama' } } EasyDocs auto-detects the provider from your environment. If no key is set, it falls back to Ollama at localhost:11434 . easydocs { project: "my-api", // separate spec per service, default: 'default' ai: { provider: "openai", // 'openai' | 'anthropic' | 'ollama' | 'deepseek' model: "gpt-4o", apiKey: "...", // optional, falls back to env vars }, storage: { type: "sqlite", // 'sqlite' | 'postgres' url: "file:./docs.sqlite", }, capture: { ignoreRoutes: "/health", "/metrics" , includePaths: "/api" , }, privacy: { enabled: true, // on by default; detect & redact PII/secrets offline: false, // strict local-first: only ever use a local Ollama model placeholder: " REDACTED ", // value substituted for sensitive fields allowlist: "public token" , // key names never to flag customRules: { keyNames: "internalRef" , // extra sensitive key names valuePatterns: "^INT-\\d+$" , // extra regex value matchers }, }, dashboard: { autoStart: true, // spawn dashboard on first capture dev only port: 4999, }, } ; Detection is deterministic and fully offline. Values are redacted before being sent to a hosted provider OpenAI/Anthropic/DeepSeek ; with local Ollama nothing leaves the machine, so real values are kept for accuracy. Flagged fields are marked in the spec with x-easydocs-sensitive and shown with a badge in the dashboard. For regulated or air-gapped environments, set privacy.offline: true for a hard guarantee: EasyDocs pins itself to a local Ollama model, ignores any hosted API keys in the environment , and refuses to start if a hosted provider is explicitly configured. Nothing captured can ever reach a third-party service — redaction becomes moot because no payload leaves the machine at all. Scope traffic from different services to separate specs: // service-a app.use easydocs { project: "users-service" } ; // service-b app.use easydocs { project: "orders-service" } ; Switch between projects in the dashboard or scope the export: npx @easydocs/cli export --project=users-service users.json MIT