{"id":50826829,"url":"https://github.com/cortech-online/dmarcheck","last_synced_at":"2026-06-13T20:01:04.014Z","repository":{"id":348131168,"uuid":"1196630640","full_name":"CorTech-Online/dmarcheck","owner":"CorTech-Online","description":"DNS email security analyzer — DMARC, SPF, DKIM, BIMI \u0026 MTA-STS. Cloudflare Worker with JSON API and interactive HTML report.","archived":false,"fork":false,"pushed_at":"2026-06-13T18:50:08.000Z","size":1713,"stargazers_count":4,"open_issues_count":28,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-13T20:00:43.897Z","etag":null,"topics":["bimi","cloudflare-workers","dkim","dmarc","dns","email-security","mta-sts","spf"],"latest_commit_sha":null,"homepage":"https://dmarc.mx","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CorTech-Online.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":"THREAT_MODEL.md","audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":"MAINTAINERS.md","copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-30T22:14:52.000Z","updated_at":"2026-06-13T18:48:51.000Z","dependencies_parsed_at":"2026-06-07T15:00:44.029Z","dependency_job_id":null,"html_url":"https://github.com/CorTech-Online/dmarcheck","commit_stats":null,"previous_names":["schmug/dmarcheck","cortech-online/dmarcheck"],"tags_count":222,"template":false,"template_full_name":null,"purl":"pkg:github/CorTech-Online/dmarcheck","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CorTech-Online%2Fdmarcheck","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CorTech-Online%2Fdmarcheck/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CorTech-Online%2Fdmarcheck/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CorTech-Online%2Fdmarcheck/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CorTech-Online","download_url":"https://codeload.github.com/CorTech-Online/dmarcheck/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CorTech-Online%2Fdmarcheck/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34298252,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bimi","cloudflare-workers","dkim","dmarc","dns","email-security","mta-sts","spf"],"created_at":"2026-06-13T20:00:27.801Z","updated_at":"2026-06-13T20:01:03.998Z","avatar_url":"https://github.com/CorTech-Online.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://dmarc.mx/logo.svg\" alt=\"DMarcus — the dmarcheck mascot\" width=\"120\" height=\"120\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003edmarcheck\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://opensource.org/licenses/MIT\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-MIT-yellow.svg\" alt=\"License: MIT\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://dmarc.mx\"\u003e\u003cimg src=\"https://img.shields.io/badge/Cloudflare%20Workers-Deployed-f38020?logo=cloudflare\" alt=\"Deploy to Cloudflare Workers\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.typescriptlang.org/\"\u003e\u003cimg src=\"https://img.shields.io/badge/TypeScript-5.7-blue?logo=typescript\" alt=\"TypeScript\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://deepwiki.com/schmug/dmarcheck\"\u003e\u003cimg src=\"https://deepwiki.com/badge.svg\" alt=\"Ask DeepWiki\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nDNS email security analyzer — checks DMARC, SPF, DKIM, BIMI, and MTA-STS records for any domain.\n\nMeet **DMarcus**, the @ creature who guards your inbox.\n\n**Live at [dmarc.mx](https://dmarc.mx)**\n\n## Three ways to use dmarcheck\n\n|  | Where | What you get |\n|---|---|---|\n| **Free scanner** | [dmarc.mx](https://dmarc.mx) | Unlimited on-demand scans, JSON API, 10 req/min per IP |\n| **Pro — $19/mo** | [dmarc.mx/pricing](https://dmarc.mx/pricing) | Nightly monitoring (25 domains), email alerts on grade drops, saved history, bulk scan, 60 req/hour API key |\n| **Self-host** | this repo | Clone, `wrangler deploy`, run your own. MIT-licensed. Pro features activate when you configure D1 / WorkOS / Stripe bindings — optional, see [below](#optional-paid-tier-env-vars). |\n\nThe hosted tier at `dmarc.mx` exists for people who'd rather pay than run it. Everything is MIT and there is no crippled-OSS / paid-premium split.\n\n## Features\n\n- **DMARC** — Policy parsing, validation, reporting URI checks\n- **SPF** — Full recursive include chain resolution, 10-lookup limit tracking, misconfiguration detection\n- **DKIM** — Auto-probes ~30 common selectors + custom selectors, key strength analysis\n- **BIMI** — Logo and VMC validation, DMARC policy cross-check\n- **MTA-STS** — DNS record + HTTPS policy file fetch and validation\n\n### Dual Output\n\n- **JSON API** for developers — `curl https://dmarc.mx/api/check?domain=dmarc.mx`\n- **Interactive HTML report** for browsers — expandable protocol cards, SPF include tree, educational tooltips\n\n### Grading\n\nLetter grades from A+ to F. DMARC is the gatekeeper — no DMARC record or `p=none` is an automatic F.\n\n| Grade | Criteria |\n|-------|----------|\n| **A+** | All five protocols fully configured and validated |\n| **A** | DMARC reject, SPF valid with hardfail, DKIM found, + BIMI or MTA-STS |\n| **B** | DMARC reject, SPF + DKIM present |\n| **C** | DMARC quarantine, SPF + DKIM present |\n| **D** | DMARC quarantine but missing SPF or DKIM |\n| **F** | No DMARC record or `p=none` |\n\n## What is email security?\n\nNew to DMARC, SPF, and DKIM? Cloudflare has a great primer: [What are DMARC, DKIM, and SPF?](https://www.cloudflare.com/learning/email-security/dmarc-dkim-spf/)\n\n### Message header analyzers\n\nOnce your DNS records are configured, you can verify authentication results in actual email headers:\n\n- [Google Admin Toolbox — Message Header Analyzer](https://toolbox.googleapps.com/apps/messageheader/)\n- [Microsoft Remote Connectivity Analyzer — Message Header](https://mha.azurewebsites.net/)\n\n## API\n\n```bash\n# JSON response\ncurl https://dmarc.mx/api/check?domain=dmarc.mx\n\n# With custom DKIM selectors\ncurl \"https://dmarc.mx/api/check?domain=dmarc.mx\u0026selectors=myselector,other\"\n\n# Content negotiation on /check\ncurl -H \"Accept: application/json\" \"https://dmarc.mx/check?domain=dmarc.mx\"\n```\n\nResponse includes a `summary` with elevated fields and full `protocols` detail:\n\n```json\n{\n  \"domain\": \"example.com\",\n  \"grade\": \"A\",\n  \"summary\": {\n    \"dmarc_policy\": \"reject\",\n    \"spf_result\": \"pass\",\n    \"spf_lookups\": \"3/10\",\n    \"dkim_selectors_found\": 2,\n    \"bimi_enabled\": true,\n    \"mta_sts_mode\": \"enforce\"\n  },\n  \"protocols\": { ... }\n}\n```\n\n## Self-Hosting\n\nYou can deploy your own instance of dmarcheck to Cloudflare Workers.\n\n### Prerequisites\n\n- [Node.js](https://nodejs.org/) (v18+)\n- A [Cloudflare account](https://dash.cloudflare.com/sign-up) (free plan works)\n\n### Setup\n\n```bash\ngit clone https://github.com/schmug/dmarcheck.git\ncd dmarcheck\nnpm install\n```\n\n### Local Development\n\n```bash\nnpm run dev       # starts local dev server on http://localhost:8790\nnpm test          # runs unit tests\n```\n\nTo point the DNS resolver at custom servers during local development (useful\nfor testing against `1.1.1.1`, `8.8.8.8`, or an internal resolver), set\n`DNS_SERVERS` to a comma-separated list:\n\n```bash\nDNS_SERVERS=8.8.8.8,1.1.1.1 npm run dev\n```\n\nWhen unset, the system default resolver (or the Cloudflare Workers DNS\npolyfill in production) is used.\n\n### Deploy to Your Own Cloudflare Account\n\n1. Authenticate with Cloudflare:\n   ```bash\n   npx wrangler login\n   ```\n\n2. Edit `wrangler.toml` to use your own custom domain (or remove the `routes` block to use the default `*.workers.dev` subdomain):\n   ```toml\n   routes = [\n     { pattern = \"yourdomain.com\", custom_domain = true },\n   ]\n   ```\n\n3. Deploy:\n   ```bash\n   npm run deploy\n   ```\n\n### Configuration\n\n| Setting | Location | Default |\n|---------|----------|---------|\n| Dev server port | `wrangler.toml` → `[dev].port` | 8790 |\n| Rate limit | `src/rate-limit.ts` → `LIMIT` | 10 req/IP/min |\n| Rate limit window | `src/rate-limit.ts` → `WINDOW_SECONDS` | 60s |\n| DKIM selectors | `src/analyzers/dkim.ts` → `COMMON_SELECTORS` | ~30 common selectors |\n| Scoring rubric | `wrangler.toml` → `[vars].SCORING_CONFIG` (JSON) | shipped rubric — see [`src/shared/scoring-config.ts`](src/shared/scoring-config.ts) for the knobs |\n\n### Database migrations\n\nThe dashboard, history, and alerts features use Cloudflare D1. Migrations live\nin `src/db/migrations/` and apply automatically via\n`.github/workflows/migrate.yml` after CI passes on `main`. The workflow\nexpects two GitHub repo secrets (Settings → Secrets and variables → Actions):\n\n| Secret | Value |\n|--------|-------|\n| `CLOUDFLARE_D1_TOKEN` | API token scoped to **D1:Edit** only (separate from the Workers deploy token so a leak in one doesn't widen into the other) |\n| `CLOUDFLARE_ACCOUNT_ID` | Your Cloudflare account ID |\n\nIf you skip the secrets, the workflow will fail and you can either delete\n`.github/workflows/migrate.yml` from your fork or apply migrations manually\nwith `npx wrangler d1 migrations apply dmarcheck-db --remote`.\n\n### Optional: paid-tier env vars\n\nThese unlock the same Pro features the hosted tier at `dmarc.mx` offers — see the [three ways to use dmarcheck](#three-ways-to-use-dmarcheck) table up top. They're all optional: the free scanner, dashboard, and API work without any of them. Set any as wrangler secrets (`wrangler secret put NAME`):\n\n| Secret | Purpose |\n|--------|---------|\n| `STRIPE_SECRET_KEY` | Stripe API key for Checkout + Portal calls |\n| `STRIPE_WEBHOOK_SECRET` | Signing secret for the `/webhooks/stripe` endpoint |\n| `STRIPE_PRICE_ID_PRO` | Price ID for the Pro plan offered via Checkout |\n| `CF_ANALYTICS_TOKEN` | Cloudflare Web Analytics beacon token (32-char lowercase hex). Injects a cookieless beacon on public HTML pages; skipped on `/dashboard/*`, `/auth/*`, `/webhooks/*`. Leave unset to disable. |\n\nIf any of the three Stripe secrets are missing, `isBillingEnabled` returns false and all\npaid-tier routes return 404. This keeps a fresh `wrangler deploy` working\nend-to-end for self-hosters who only want the free scanner. `CF_ANALYTICS_TOKEN`\nis independent — set it only if you want to send analytics to your own\nCloudflare Web Analytics dashboard.\n\n## Stack\n\n- [Hono](https://hono.dev) — lightweight web framework for Cloudflare Workers\n- TypeScript + `node:dns` via `nodejs_compat`\n- Rate limited via Cloudflare Cache API (no extra bindings needed)\n- Pro features (auth, billing, history, cron) use Cloudflare D1 + WorkOS + Stripe. The hosted tier at `dmarc.mx` runs on the same code and same stack — no private fork.\n\n## Contributing \u0026 reporting\n\n- **Found a bug or want a feature?** Open an issue in the\n  [issue tracker](https://github.com/schmug/dmarcheck/issues) — search first to\n  avoid duplicates.\n- **Found a security vulnerability?** Please **don't** open a public issue.\n  Report it privately via the process in [SECURITY.md](SECURITY.md).\n- **Want to contribute code?** See [CONTRIBUTING.md](CONTRIBUTING.md) for setup,\n  the test/lint/typecheck gate, and commit conventions. Maintainers and the\n  review policy are listed in [MAINTAINERS.md](MAINTAINERS.md).\n\nSecurity posture is documented in [SECURITY.md](SECURITY.md) and the\n[threat model](THREAT_MODEL.md).\n\n## Verifying a release\n\nEvery release ships a `SHA256SUMS` manifest (covering the SBOM and the\nGitHub-generated source archives) and a Sigstore keyless signature bundle\n(`SHA256SUMS.bundle`). The signing identity is the GitHub Actions workflow\nitself — no long-lived private key is involved.\n\n### Prerequisites\n\nInstall [cosign](https://docs.sigstore.dev/cosign/system_config/installation/)\n(minimum **v2.4**; v3.x recommended):\n\n```bash\n# macOS / Linux with Homebrew\nbrew install cosign\n\n# or via Go (installs the latest v3)\ngo install github.com/sigstore/cosign/v3/cmd/cosign@latest\n\n# cosign v2.4+ also verifies the current bundle format if you prefer v2:\n# go install github.com/sigstore/cosign/v2/cmd/cosign@latest\n```\n\n### Verify a release\n\nReplace `vYYYY.M.N` with the tag you want to verify (e.g. `v2026.5.1`).\n\n```bash\n# 1. Download the manifest and signature bundle\ngh release download vYYYY.M.N -R schmug/dmarcheck --pattern 'SHA256SUMS*'\n\n# 2. Verify the Sigstore signature — confirms the bundle was signed by\n#    the release workflow running on the schmug/dmarcheck repository\ncosign verify-blob \\\n  --bundle SHA256SUMS.bundle \\\n  --certificate-identity-regexp \"https://github.com/schmug/dmarcheck/.github/workflows/release.yml\" \\\n  --certificate-oidc-issuer \"https://token.actions.githubusercontent.com\" \\\n  SHA256SUMS\n\n# 3. Check file integrity\nsha256sum -c SHA256SUMS\n```\n\nA successful run of step 2 prints `Verified OK`. Step 3 confirms that the\ndownloaded archives match the checksums signed in step 2.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcortech-online%2Fdmarcheck","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcortech-online%2Fdmarcheck","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcortech-online%2Fdmarcheck/lists"}