{"id":50182027,"url":"https://github.com/ipanalytics/mmdbforge","last_synced_at":"2026-05-25T07:05:08.333Z","repository":{"id":359371559,"uuid":"1245756837","full_name":"ipanalytics/MMDBForge","owner":"ipanalytics","description":"MMDB Forge is a developer toolkit for inspecting, validating, diffing, and explaining custom MaxMind DB files.","archived":false,"fork":false,"pushed_at":"2026-05-21T15:13:37.000Z","size":95,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-21T23:52:49.372Z","etag":null,"topics":["cli","data-quality","database-tools","developer-tools","diff","geoip","golang","ip-geolocation","maxmind","maxminddb","mmdb","networking","release-audit","schema-validation"],"latest_commit_sha":null,"homepage":"https://ipanalytics.github.io/MMDBForge/","language":"Go","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/ipanalytics.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-21T14:23:37.000Z","updated_at":"2026-05-21T15:24:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ipanalytics/MMDBForge","commit_stats":null,"previous_names":["ipanalytics/mmdbforge"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/ipanalytics/MMDBForge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FMMDBForge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FMMDBForge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FMMDBForge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FMMDBForge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ipanalytics","download_url":"https://codeload.github.com/ipanalytics/MMDBForge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipanalytics%2FMMDBForge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33464014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-25T06:32:55.349Z","status":"ssl_error","status_checked_at":"2026-05-25T06:32:35.322Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["cli","data-quality","database-tools","developer-tools","diff","geoip","golang","ip-geolocation","maxmind","maxminddb","mmdb","networking","release-audit","schema-validation"],"created_at":"2026-05-25T07:04:42.054Z","updated_at":"2026-05-25T07:05:08.225Z","avatar_url":"https://github.com/ipanalytics.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MMDB Forge\n\n**MMDB Forge is a developer toolkit for inspecting, validating, diffing, and explaining custom MaxMind DB files.**\n\nMost MaxMind DB tools are built around one question:\n\n```text\nWhat does this IP resolve to?\n```\n\nMMDB Forge is built for people who generate, ship, and maintain their own `.mmdb`\nfiles. It helps answer the questions that matter before a database release goes\nto production:\n\n```text\nWhat changed between two versions?\nDid the schema break?\nWhy did this IP get this record?\nWhich fields disappeared?\nHow many prefixes changed country, ASN, VPN status, confidence, or risk score?\nWhere did confidence suddenly become 0?\nWhich records became null?\nDid the file become much larger?\nDo known test IPs still return the expected values?\n```\n\nThink of it as **jq + diff + validator + CI guardrails** for `.mmdb` releases.\n\n## What It Does\n\nMMDB Forge is both a CLI and a small Go codebase organized around MMDB release\nquality checks.\n\nIt can:\n\n- inspect a single IP lookup with the matched network prefix\n- explain why a record matched and surface suspicious record details\n- compare old and new database versions on sampled records or a fixed IP list\n- validate records against a simple JSON schema\n- list all observed fields in a database\n- calculate field coverage and top values\n- run smoke tests for known IP expectations\n- run a full release audit that combines diff, stats, schema validation, and smoke tests\n\n## Install\n\nFrom source:\n\n```bash\ngit clone https://github.com/ipanalytics/MMDBForge.git\ncd mmdbforge\ngo build ./cmd/mmdbforge\n```\n\nInstall the local checkout into your `GOBIN`:\n\n```bash\ngo install ./cmd/mmdbforge\n```\n\nCheck the CLI:\n\n```bash\nmmdbforge --help\n```\n\n## Quick Start\n\nInspect an IP:\n\n```bash\nmmdbforge inspect vpn.mmdb 91.196.220.30\n```\n\nCompare two releases:\n\n```bash\nmmdbforge diff vpn-2026-05-20.mmdb vpn-2026-05-21.mmdb --sample 100000\n```\n\nValidate records against a schema:\n\n```bash\nmmdbforge validate examples/vpn.schema.json vpn.mmdb --sample 50000\n```\n\nRun a release audit:\n\n```bash\nmmdbforge audit release \\\n  --old vpn-2026-05-20.mmdb \\\n  --new vpn-2026-05-21.mmdb \\\n  --schema examples/vpn.schema.json \\\n  --smoke examples/smoke.json \\\n  --sample 100000 \\\n  --markdown report.md\n```\n\n## Commands\n\n```text\nmmdbforge inspect \u003cdb.mmdb\u003e \u003cip\u003e\nmmdbforge explain \u003cdb.mmdb\u003e \u003cip\u003e\nmmdbforge explain-diff \u003cold.mmdb\u003e \u003cnew.mmdb\u003e \u003cip\u003e\nmmdbforge lint cidr \u003cprefixes.txt|prefixes.csv|prefixes.jsonl\u003e\nmmdbforge diff \u003cold.mmdb\u003e \u003cnew.mmdb\u003e [flags]\nmmdbforge validate \u003cschema.json\u003e \u003cdb.mmdb\u003e [flags]\nmmdbforge stats \u003cdb.mmdb\u003e [flags]\nmmdbforge stats diff \u003cold.mmdb\u003e \u003cnew.mmdb\u003e [flags]\nmmdbforge fields \u003cdb.mmdb\u003e [flags]\nmmdbforge smoke \u003cdb.mmdb\u003e \u003csmoke.json\u003e\nmmdbforge test run \u003cconfig.yaml\u003e [--output run.json]\nmmdbforge test compare \u003cbaseline.json\u003e \u003ccurrent.json\u003e [--output compare.json]\nmmdbforge test report \u003ccompare.json\u003e [--markdown [file]] [--html [file]]\nmmdbforge prefixes \u003cdb.mmdb\u003e [new.mmdb] [flags]\nmmdbforge bench \u003cdb.mmdb\u003e [new.mmdb] [flags]\nmmdbforge audit release --old \u003cold.mmdb\u003e --new \u003cnew.mmdb\u003e [flags]\n```\n\nAll commands print pretty JSON by default. `diff` and `audit release` can also\nwrite Markdown reports.\n\n## inspect\n\n`inspect` performs a lookup and returns the record together with the matched\nnetwork prefix.\n\n```bash\nmmdbforge inspect vpn.mmdb 91.196.220.30\n```\n\nExample output:\n\n```json\n{\n  \"ip\": \"91.196.220.30\",\n  \"database\": \"vpn.mmdb\",\n  \"matched_prefix\": \"91.196.220.30/32\",\n  \"record\": {\n    \"privacy\": {\n      \"is_vpn\": true,\n      \"vpn_provider\": \"NordVPN\",\n      \"is_hosting\": true\n    },\n    \"confidence\": 95\n  }\n}\n```\n\nUse it when you want a direct, debuggable lookup without writing a small Go\nprogram or piping raw data through another tool.\n\n## explain\n\n`explain` shows the lookup result plus useful context about the matched record.\n\n```bash\nmmdbforge explain vpn.mmdb 91.196.220.30\n```\n\nExample output:\n\n```json\n{\n  \"ip\": \"91.196.220.30\",\n  \"database\": \"vpn.mmdb\",\n  \"matched_prefix\": \"91.196.220.30/32\",\n  \"prefix_length\": 32,\n  \"record_size_bytes\": 384,\n  \"fields\": [\n    \"privacy.is_vpn\",\n    \"privacy.vpn_provider\",\n    \"privacy.is_hosting\",\n    \"confidence\"\n  ],\n  \"warnings\": [\n    \"matched host-level record; check if this database intentionally stores host-level entries\"\n  ],\n  \"record\": {\n    \"privacy\": {\n      \"is_vpn\": true,\n      \"vpn_provider\": \"NordVPN\",\n      \"is_hosting\": true\n    },\n    \"confidence\": 95\n  }\n}\n```\n\nThis is useful when a generated database has unexpected host-level records,\nmissing risk explanation fields, or confidence values outside the expected\nrange.\n\n## diff\n\n`diff` is the main release-safety command.\n\nIt samples records from the old database, looks up the same IPs in the new\ndatabase, and reports record-level and field-level changes.\n\n```bash\nmmdbforge diff old.mmdb new.mmdb --sample 100000\n```\n\nExample output:\n\n```json\n{\n  \"sample_size\": 100000,\n  \"changed_records\": 8421,\n  \"changed_percent\": 8.42,\n  \"field_changes\": {\n    \"privacy.is_vpn\": {\n      \"false_to_true\": 1203,\n      \"true_to_false\": 312\n    },\n    \"privacy.vpn_provider\": {\n      \"changed\": 842,\n      \"added\": 1203,\n      \"removed\": 312\n    },\n    \"network_context.geo_country_code\": {\n      \"changed\": 91\n    }\n  },\n  \"top_changes\": [\n    {\n      \"field\": \"privacy.vpn_provider\",\n      \"old\": null,\n      \"new\": \"NordVPN\",\n      \"count\": 433\n    },\n    {\n      \"field\": \"network_context.geo_country_code\",\n      \"old\": \"GB\",\n      \"new\": \"US\",\n      \"count\": 28\n    }\n  ],\n  \"failed\": false\n}\n```\n\nUseful flags:\n\n```bash\n--sample 100000\n--full\n--ips test-ips.txt\n--fields privacy.is_vpn,privacy.vpn_provider\n--json\n--markdown\n--markdown report.md\n--fail-on-change privacy.is_vpn\n--fail-on-drop confidence\n--fail-on-missing-field confidence\n--fail-threshold changed_percent=20\n```\n\nExamples:\n\n```bash\nmmdbforge diff old.mmdb new.mmdb \\\n  --sample 100000 \\\n  --fields privacy.is_vpn,privacy.vpn_provider,confidence\n```\n\n```bash\nmmdbforge diff old.mmdb new.mmdb \\\n  --ips fixtures/release-check-ips.txt \\\n  --fail-on-change privacy.is_vpn\n```\n\n```bash\nmmdbforge diff old.mmdb new.mmdb \\\n  --sample 100000 \\\n  --fail-threshold changed_percent=25 \\\n  --fail-on-missing-field confidence\n```\n\n`diff` is intentionally sampling-based by default. For very large databases,\nthis gives a fast release signal without requiring a full exhaustive comparison.\nFor deterministic checks, pass a fixed IP list with `--ips`. For exhaustive\ndatabase traversal, use `--full`.\n\n## explain-diff\n\n`explain-diff` explains exactly what changed for one IP between two database\nversions.\n\n```bash\nmmdbforge explain-diff old.mmdb new.mmdb 91.196.220.30\n```\n\nIt returns the old matched prefix, new matched prefix, old record, new record,\nand a sorted list of changed dotted fields. This is the fastest way to debug a\nsingle customer-reported IP or a smoke-test regression.\n\n## lint cidr\n\n`lint cidr` checks raw source prefixes before they are compiled into MMDB.\n\n```bash\nmmdbforge lint cidr prefixes.txt\nmmdbforge lint cidr prefixes.csv\nmmdbforge lint cidr prefixes.jsonl\n```\n\nSupported input:\n\n- plain text: first token is CIDR\n- CSV/TSV: first column is CIDR\n- JSONL: `cidr`, `network`, or `prefix` string field\n\nIt reports invalid CIDRs, duplicate CIDRs, and narrower prefixes shadowed by a\nbroader prefix. This catches source data mistakes that are hard to reconstruct\nafter the database has already been compiled.\n\n## validate\n\n`validate` checks sampled records against a JSON schema.\n\n```bash\nmmdbforge validate examples/vpn.schema.json vpn.mmdb --sample 50000\n```\n\nSchema example:\n\n```json\n{\n  \"required\": [\n    \"privacy.is_vpn\",\n    \"confidence\"\n  ],\n  \"fields\": {\n    \"privacy.is_vpn\": {\n      \"type\": \"boolean\"\n    },\n    \"privacy.vpn_provider\": {\n      \"type\": [\"string\", \"null\"]\n    },\n    \"confidence\": {\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 100\n    },\n    \"risk_score\": {\n      \"type\": \"integer\",\n      \"minimum\": 0,\n      \"maximum\": 100\n    }\n  },\n  \"rules\": [\n    {\n      \"if\": \"privacy.is_vpn == true\",\n      \"then_required\": [\"privacy.privacy_service\"]\n    },\n    {\n      \"if\": \"risk_score \u003e= 80\",\n      \"then_required\": [\"risk_reasons\"]\n    }\n  ]\n}\n```\n\nExample output:\n\n```json\n{\n  \"checked_records\": 50000,\n  \"errors\": [\n    {\n      \"ip\": \"91.196.220.30\",\n      \"matched_prefix\": \"91.196.220.30/32\",\n      \"field\": \"risk_reasons\",\n      \"message\": \"risk_score \u003e= 80 requires risk_reasons\"\n    }\n  ]\n}\n```\n\nSupported schema features:\n\n- `required`: dotted field paths that must exist\n- `type`: `string`, `integer`, `number`, `boolean`, `object`, `array`, `null`\n- `minimum` and `maximum` for numeric fields\n- `rules`: simple conditional requirements using `==`, `!=`, `\u003e`, `\u003e=`, `\u003c`, `\u003c=`\n\nThe schema format is deliberately small. It is meant for MMDB release contracts,\nnot for modeling every possible JSON Schema feature.\n\nMMDB Forge also accepts a basic JSON Schema-style object with `required` and\nnested `properties`. Nested properties are flattened into dotted MMDB field\npaths before validation.\n\n## stats\n\n`stats` summarizes metadata, file size, field coverage, and top scalar values.\n\n```bash\nmmdbforge stats vpn.mmdb --sample 10000 --top 10\n```\n\nExample output:\n\n```json\n{\n  \"database_type\": \"ipanalytics-vpn\",\n  \"ip_version\": [\"ipv4\", \"ipv6\"],\n  \"build_epoch\": 1779364800,\n  \"node_count\": 1842201,\n  \"file_size_mb\": 96.4,\n  \"checked_records\": 10000,\n  \"field_coverage\": {\n    \"privacy.is_vpn\": 100.0,\n    \"privacy.vpn_provider\": 84.2,\n    \"confidence\": 99.9,\n    \"risk_reasons\": 22.1\n  },\n  \"top_values\": {\n    \"privacy.vpn_provider\": [\n      [\"NordVPN\", 1204],\n      [\"Surfshark\", 881],\n      [\"ExpressVPN\", 604]\n    ],\n    \"network_context.connection_type\": [\n      [\"hosting\", 9012],\n      [\"residential\", 552]\n    ]\n  }\n}\n```\n\nUse `stats` when you want to know whether a release changed shape even before\nlooking at exact field transitions.\n\nCompare field coverage between two releases:\n\n```bash\nmmdbforge stats diff old.mmdb new.mmdb --sample 100000\nmmdbforge stats diff old.mmdb new.mmdb --full\nmmdbforge stats diff old.mmdb new.mmdb --sample 100000 --table\n```\n\n## fields\n\n`fields` lists every dotted field path observed in sampled records.\n\n```bash\nmmdbforge fields vpn.mmdb --sample 10000\n```\n\nExample output:\n\n```json\n{\n  \"checked_records\": 10000,\n  \"fields\": [\n    \"confidence\",\n    \"network_context.geo_country_code\",\n    \"privacy.is_hosting\",\n    \"privacy.is_vpn\",\n    \"privacy.vpn_provider\",\n    \"risk_reasons\",\n    \"risk_score\"\n  ]\n}\n```\n\nThis is useful when creating a schema for an existing database or investigating\nwhether a generator accidentally renamed or removed fields.\n\n## smoke\n\n`smoke` runs regression tests for known IPs.\n\n```bash\nmmdbforge smoke vpn.mmdb examples/smoke.json\n```\n\nSmoke file:\n\n```json\n[\n  {\n    \"ip\": \"91.196.220.30\",\n    \"expect\": {\n      \"privacy.is_vpn\": true,\n      \"privacy.vpn_provider\": \"NordVPN\"\n    }\n  },\n  {\n    \"ip\": \"8.8.8.8\",\n    \"expect\": {\n      \"profile.is_anycast\": true\n    }\n  }\n]\n```\n\nExample output:\n\n```json\n{\n  \"checked\": 2,\n  \"passed\": 1,\n  \"failed\": 1,\n  \"results\": [\n    {\n      \"ip\": \"91.196.220.30\",\n      \"passed\": true\n    },\n    {\n      \"ip\": \"8.8.8.8\",\n      \"passed\": false,\n      \"failures\": [\n        {\n          \"field\": \"profile.is_anycast\",\n          \"expected\": true,\n          \"actual\": false,\n          \"message\": \"value mismatch\"\n        }\n      ]\n    }\n  ]\n}\n```\n\nSmoke tests are best for high-value examples: known VPN exits, known residential\nIPs, known anycast IPs, test prefixes, internal fixtures, and customer-reported\nedge cases.\n\nSmoke cases support exact expectations, allowed value sets, and denied value\nsets:\n\n```json\n{\n  \"ip\": \"91.196.220.30\",\n  \"expect\": {\n    \"privacy.is_vpn\": true,\n    \"privacy.vpn_provider\": \"NordVPN\"\n  },\n  \"allow\": {\n    \"geo.city_name\": [\"Los Angeles\", \"London\"]\n  },\n  \"deny\": {\n    \"privacy.vpn_provider\": [null, \"\"]\n  }\n}\n```\n\n## test\n\n`test` is the MMDB Forge testbench. It turns golden IP samples into reusable\nrelease artifacts.\n\nRun a testbench config:\n\n```bash\nmmdbforge test run examples/ipbench.yaml --output baseline.json\n```\n\nRun it again for a new database version:\n\n```bash\nmmdbforge test run examples/ipbench.yaml --output current.json\n```\n\nCompare two runs:\n\n```bash\nmmdbforge test compare baseline.json current.json --output compare.json\n```\n\nGenerate reports:\n\n```bash\nmmdbforge test report compare.json --markdown testbench.md\nmmdbforge test report compare.json --html testbench.html\n```\n\nConfig example:\n\n```yaml\nname: vpn-release-golden-samples\ndatabase: vpn.mmdb\nfields:\n  - privacy.is_vpn\n  - privacy.vpn_provider\n  - network.connection_type\n  - geo.city_name\ncases:\n  - ip: 91.196.220.30\n    expect:\n      privacy.is_vpn: true\n      privacy.vpn_provider: NordVPN\n      network.connection_type: hosting\n    allow:\n      geo.city_name:\n        - Los Angeles\n        - London\n    deny:\n      privacy.vpn_provider:\n        - \"\"\n        - null\n```\n\nThis is the “unit tests for IP intelligence databases” layer: exact expected\nfields, tolerated alternatives, forbidden values, persistent run artifacts, run\ncomparison, and HTML/Markdown reports.\n\n## prefixes\n\n`prefixes` checks prefix-shape distribution and warns when host-level records\ndominate the sampled networks.\n\n```bash\nmmdbforge prefixes vpn.mmdb --sample 100000 --table\nmmdbforge prefixes vpn.mmdb --full\nmmdbforge prefixes old.mmdb new.mmdb --sample 100000\n```\n\nThis catches release mistakes such as unexpected `/32` or `/128` explosions.\nCompiled MMDB data is already stored as a search trie, so this is a release\nshape check for the built database rather than a raw source CIDR overlap linter.\n\n## bench\n\n`bench` measures sampled lookup throughput.\n\n```bash\nmmdbforge bench vpn.mmdb --sample 100000 --table\nmmdbforge bench vpn.mmdb --full\nmmdbforge bench old.mmdb new.mmdb --sample 100000\n```\n\nUse it to catch releases that become significantly slower even when schema and\nsmoke tests pass.\n\n## audit release\n\n`audit release` combines the core release checks into one command.\n\n```bash\nmmdbforge audit release \\\n  --old vpn-2026-05-20.mmdb \\\n  --new vpn-2026-05-21.mmdb \\\n  --schema examples/vpn.schema.json \\\n  --smoke examples/smoke.json \\\n  --policy examples/release.policy.json \\\n  --sample 100000 \\\n  --markdown report.md \\\n  --html report.html\n```\n\nIt runs:\n\n- metadata and file size checks\n- sampled diff\n- field coverage stats\n- field coverage diff\n- prefix-shape checks\n- lookup benchmark comparison\n- release policy gates\n- standalone Markdown and HTML reports\n- schema validation\n- known IP smoke tests\n- release verdict generation\n\nMarkdown output example:\n\n```md\n# MMDB Release Audit\n\nVerdict: WARN\n\n- 8.42% sampled records changed\n- file size changed 4.80%\n- schema validation errors: 0\n- smoke tests failed: 0\n```\n\nVerdicts:\n\n- `PASS`: no configured checks failed\n- `WARN`: suspicious but not contract-breaking changes, such as a large file size increase\n- `FAIL`: schema validation or smoke tests failed\n\nPolicy example:\n\n```json\n{\n  \"max_changed_percent\": 25,\n  \"max_file_growth_percent\": 50,\n  \"max_lookup_slowdown_percent\": 25,\n  \"max_host_level_growth_percent\": 50,\n  \"required_fields\": [\n    \"privacy.is_vpn\",\n    \"confidence\"\n  ],\n  \"allowed_dropped_fields\": []\n}\n```\n\n## CI Example\n\nMMDB Forge is designed to be useful in CI.\n\n```yaml\nname: MMDB release checks\n\non:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  audit:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: actions/setup-go@v5\n        with:\n          go-version: \"1.22\"\n\n      - name: Install mmdbforge\n        run: go install ./cmd/mmdbforge\n\n      - name: Audit MMDB release\n        run: |\n          mmdbforge audit release \\\n            --old artifacts/vpn-previous.mmdb \\\n            --new artifacts/vpn-current.mmdb \\\n            --schema examples/vpn.schema.json \\\n            --policy examples/release.policy.json \\\n            --smoke fixtures/vpn-smoke.json \\\n            --sample 100000 \\\n            --markdown report.md \\\n            --html report.html\n\n      - name: Upload report\n        uses: actions/upload-artifact@v4\n        with:\n          name: mmdb-audit-report\n          path: |\n            report.md\n            report.html\n```\n\nFor stricter release gates, use `diff` directly:\n\n```bash\nmmdbforge diff artifacts/vpn-previous.mmdb artifacts/vpn-current.mmdb \\\n  --sample 100000 \\\n  --fail-threshold changed_percent=25 \\\n  --fail-on-missing-field confidence \\\n  --fail-on-drop privacy.vpn_provider\n```\n\n## Data Model\n\nMMDB Forge flattens nested record fields into dotted paths:\n\n```json\n{\n  \"privacy\": {\n    \"is_vpn\": true,\n    \"vpn_provider\": \"NordVPN\"\n  },\n  \"confidence\": 95\n}\n```\n\nbecomes:\n\n```text\nprivacy.is_vpn\nprivacy.vpn_provider\nconfidence\n```\n\nThis makes schema rules, diff summaries, CI checks, and smoke expectations easy\nto write and easy to review.\n\n## Why This Exists\n\nWhen teams build their own MMDB files, failures are rarely obvious from a single\nlookup. Real release problems look like this:\n\n```text\nthe new database is 3x larger\nsome fields disappeared\ncountry_code suddenly became registry country\nconfidence escaped the 0..100 range\nall VPN records became risk_score=100\n/32 records exploded unexpectedly\nprovider names disappeared\nIPv6 coverage broke\nknown smoke-test IPs changed behavior\n```\n\nMMDB Forge gives those problems names, commands, and CI failure modes.\n\n## Project Layout\n\n```text\ncmd/mmdbforge/      CLI entrypoint\ninternal/lookup/    inspect and explain commands\ninternal/diff/      sampled release diff\ninternal/schema/    schema validation\ninternal/stats/     field coverage and top values\ninternal/smoke/     known IP regression tests\ninternal/audit/     release audit orchestration\ninternal/report/    JSON and Markdown output\nexamples/           schema and smoke examples\ndocs/               command guides and CI notes\n```\n\n\n## License\n\nMIT. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fipanalytics%2Fmmdbforge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fipanalytics%2Fmmdbforge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fipanalytics%2Fmmdbforge/lists"}