MMDBForge

English version: README.md

MMDB Forge

MMDB Forge — набор инструментов разработчика для инспектирования, валидации, сравнения (diff) и объяснения собственных файлов MaxMind DB.

MMDB Forge

Большинство инструментов для MaxMind DB построены вокруг одного вопроса:

What does this IP resolve to?

MMDB Forge создан для тех, кто генерирует, поставляет и сопровождает собственные файлы .mmdb. Он помогает ответить на вопросы, которые важно решить до того, как релиз базы данных попадёт в production:

What changed between two versions?
Did the schema break?
Why did this IP get this record?
Which fields disappeared?
How many prefixes changed country, ASN, VPN status, confidence, or risk score?
Where did confidence suddenly become 0?
Which records became null?
Did the file become much larger?
Do known test IPs still return the expected values?

Считайте это jq + diff + валидатор + CI-предохранители для релизов .mmdb.

Что он делает

MMDB Forge — это одновременно CLI и небольшая кодовая база на Go, организованная вокруг проверок качества релизов MMDB.

Он умеет:

Установка

Из исходного кода:

git clone https://github.com/ipanalytics/MMDBForge.git
cd mmdbforge
go build ./cmd/mmdbforge

Установите локальную рабочую копию (checkout) в ваш GOBIN:

go install ./cmd/mmdbforge

Проверка CLI:

mmdbforge --help

Быстрый старт

Инспектирование IP:

mmdbforge inspect vpn.mmdb 91.196.220.30

Сравнение двух релизов:

mmdbforge diff vpn-2026-05-20.mmdb vpn-2026-05-21.mmdb --sample 100000

Валидация записей по схеме:

mmdbforge validate examples/vpn.schema.json vpn.mmdb --sample 50000

Запуск аудита релиза:

mmdbforge audit release \
  --old vpn-2026-05-20.mmdb \
  --new vpn-2026-05-21.mmdb \
  --schema examples/vpn.schema.json \
  --smoke examples/smoke.json \
  --sample 100000 \
  --markdown report.md

Команды

mmdbforge inspect <db.mmdb> <ip>
mmdbforge explain <db.mmdb> <ip>
mmdbforge explain-diff <old.mmdb> <new.mmdb> <ip>
mmdbforge lint cidr <prefixes.txt|prefixes.csv|prefixes.jsonl>
mmdbforge diff <old.mmdb> <new.mmdb> [flags]
mmdbforge validate <schema.json> <db.mmdb> [flags]
mmdbforge stats <db.mmdb> [flags]
mmdbforge stats diff <old.mmdb> <new.mmdb> [flags]
mmdbforge fields <db.mmdb> [flags]
mmdbforge smoke <db.mmdb> <smoke.json>
mmdbforge test run <config.yaml> [--output run.json]
mmdbforge test compare <baseline.json> <current.json> [--output compare.json]
mmdbforge test report <compare.json> [--markdown [file]] [--html [file]]
mmdbforge prefixes <db.mmdb> [new.mmdb] [flags]
mmdbforge bench <db.mmdb> [new.mmdb] [flags]
mmdbforge audit release --old <old.mmdb> --new <new.mmdb> [flags]

Все команды по умолчанию выводят форматированный (pretty) JSON. diff и audit release также могут записывать отчёты в Markdown.

inspect

inspect выполняет поиск и возвращает запись вместе с совпавшим сетевым префиксом.

mmdbforge inspect vpn.mmdb 91.196.220.30

Пример вывода:

{
  "ip": "91.196.220.30",
  "database": "vpn.mmdb",
  "matched_prefix": "91.196.220.30/32",
  "record": {
    "privacy": {
      "is_vpn": true,
      "vpn_provider": "NordVPN",
      "is_hosting": true
    },
    "confidence": 95
  }
}

Используйте его, когда нужен прямой, удобный для отладки поиск без написания небольшой программы на Go или прогонки сырых данных через другой инструмент.

explain

explain показывает результат поиска плюс полезный контекст о совпавшей записи.

mmdbforge explain vpn.mmdb 91.196.220.30

Пример вывода:

{
  "ip": "91.196.220.30",
  "database": "vpn.mmdb",
  "matched_prefix": "91.196.220.30/32",
  "prefix_length": 32,
  "record_size_bytes": 384,
  "fields": [
    "privacy.is_vpn",
    "privacy.vpn_provider",
    "privacy.is_hosting",
    "confidence"
  ],
  "warnings": [
    "matched host-level record; check if this database intentionally stores host-level entries"
  ],
  "record": {
    "privacy": {
      "is_vpn": true,
      "vpn_provider": "NordVPN",
      "is_hosting": true
    },
    "confidence": 95
  }
}

Это полезно, когда в сгенерированной базе есть неожиданные записи уровня хоста (host-level), отсутствуют поля объяснения риска или значения confidence вне ожидаемого диапазона.

diff

diff — основная команда для контроля безопасности релиза.

Он берёт выборку записей из старой базы, ищет те же IP в новой базе и сообщает об изменениях на уровне записей и на уровне полей.

mmdbforge diff old.mmdb new.mmdb --sample 100000

Пример вывода:

{
  "sample_size": 100000,
  "changed_records": 8421,
  "changed_percent": 8.42,
  "field_changes": {
    "privacy.is_vpn": {
      "false_to_true": 1203,
      "true_to_false": 312
    },
    "privacy.vpn_provider": {
      "changed": 842,
      "added": 1203,
      "removed": 312
    },
    "network_context.geo_country_code": {
      "changed": 91
    }
  },
  "top_changes": [
    {
      "field": "privacy.vpn_provider",
      "old": null,
      "new": "NordVPN",
      "count": 433
    },
    {
      "field": "network_context.geo_country_code",
      "old": "GB",
      "new": "US",
      "count": 28
    }
  ],
  "failed": false
}

Полезные флаги:

--sample 100000
--full
--ips test-ips.txt
--fields privacy.is_vpn,privacy.vpn_provider
--json
--markdown
--markdown report.md
--fail-on-change privacy.is_vpn
--fail-on-drop confidence
--fail-on-missing-field confidence
--fail-threshold changed_percent=20

Примеры:

mmdbforge diff old.mmdb new.mmdb \
  --sample 100000 \
  --fields privacy.is_vpn,privacy.vpn_provider,confidence
mmdbforge diff old.mmdb new.mmdb \
  --ips fixtures/release-check-ips.txt \
  --fail-on-change privacy.is_vpn
mmdbforge diff old.mmdb new.mmdb \
  --sample 100000 \
  --fail-threshold changed_percent=25 \
  --fail-on-missing-field confidence

diff по умолчанию намеренно использует выборку. Для очень больших баз данных это даёт быстрый сигнал о готовности релиза без необходимости полного исчерпывающего сравнения. Для детерминированных проверок передайте фиксированный список IP через --ips. Для исчерпывающего обхода базы данных используйте --full.

explain-diff

explain-diff точно объясняет, что изменилось для одного IP между двумя версиями базы данных.

mmdbforge explain-diff old.mmdb new.mmdb 91.196.220.30

Он возвращает старый совпавший префикс, новый совпавший префикс, старую запись, новую запись и отсортированный список изменившихся полей в точечной нотации. Это самый быстрый способ отладки единичного IP, о котором сообщил клиент, или регрессии, выявленной smoke-тестом.

lint cidr

lint cidr проверяет исходные префиксы до их компиляции в MMDB.

mmdbforge lint cidr prefixes.txt
mmdbforge lint cidr prefixes.csv
mmdbforge lint cidr prefixes.jsonl

Поддерживаемые форматы входных данных:

Он сообщает о недопустимых CIDR, дублирующихся CIDR и более узких префиксах, перекрытых более широким префиксом. Это позволяет выявить ошибки в исходных данных, которые трудно восстановить после того, как база данных уже скомпилирована.

validate

validate проверяет выбранные записи на соответствие JSON-схеме.

mmdbforge validate examples/vpn.schema.json vpn.mmdb --sample 50000

Пример схемы:

{
  "required": [
    "privacy.is_vpn",
    "confidence"
  ],
  "fields": {
    "privacy.is_vpn": {
      "type": "boolean"
    },
    "privacy.vpn_provider": {
      "type": ["string", "null"]
    },
    "confidence": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100
    },
    "risk_score": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100
    }
  },
  "rules": [
    {
      "if": "privacy.is_vpn == true",
      "then_required": ["privacy.privacy_service"]
    },
    {
      "if": "risk_score >= 80",
      "then_required": ["risk_reasons"]
    }
  ]
}

Пример вывода:

{
  "checked_records": 50000,
  "errors": [
    {
      "ip": "91.196.220.30",
      "matched_prefix": "91.196.220.30/32",
      "field": "risk_reasons",
      "message": "risk_score >= 80 requires risk_reasons"
    }
  ]
}

Поддерживаемые возможности схемы:

Формат схемы намеренно минималистичен. Он предназначен для контрактов релизов MMDB, а не для моделирования всех возможных возможностей JSON Schema.

MMDB Forge также принимает базовый объект в стиле JSON Schema с required и вложенными properties. Вложенные свойства разворачиваются в точечные пути полей MMDB перед валидацией.

stats

stats суммирует метаданные, размер файла, покрытие полей и наиболее частые скалярные значения.

mmdbforge stats vpn.mmdb --sample 10000 --top 10

Пример вывода:

{
  "database_type": "ipanalytics-vpn",
  "ip_version": ["ipv4", "ipv6"],
  "build_epoch": 1779364800,
  "node_count": 1842201,
  "file_size_mb": 96.4,
  "checked_records": 10000,
  "field_coverage": {
    "privacy.is_vpn": 100.0,
    "privacy.vpn_provider": 84.2,
    "confidence": 99.9,
    "risk_reasons": 22.1
  },
  "top_values": {
    "privacy.vpn_provider": [
      ["NordVPN", 1204],
      ["Surfshark", 881],
      ["ExpressVPN", 604]
    ],
    "network_context.connection_type": [
      ["hosting", 9012],
      ["residential", 552]
    ]
  }
}

Используйте stats, когда нужно узнать, изменил ли релиз структуру данных, ещё до просмотра точных переходов полей.

Сравните покрытие полей между двумя релизами:

mmdbforge stats diff old.mmdb new.mmdb --sample 100000
mmdbforge stats diff old.mmdb new.mmdb --full
mmdbforge stats diff old.mmdb new.mmdb --sample 100000 --table

fields

fields выводит все пути полей в точечной нотации, обнаруженные в выборке записей.

mmdbforge fields vpn.mmdb --sample 10000

Пример вывода:

{
  "checked_records": 10000,
  "fields": [
    "confidence",
    "network_context.geo_country_code",
    "privacy.is_hosting",
    "privacy.is_vpn",
    "privacy.vpn_provider",
    "risk_reasons",
    "risk_score"
  ]
}

Это полезно при создании схемы для существующей базы данных или при проверке того, не переименовал ли генератор поля случайно или не удалил ли их.

smoke

smoke запускает регрессионные тесты для известных IP-адресов.

mmdbforge smoke vpn.mmdb examples/smoke.json

Файл smoke-тестов:

[
  {
    "ip": "91.196.220.30",
    "expect": {
      "privacy.is_vpn": true,
      "privacy.vpn_provider": "NordVPN"
    }
  },
  {
    "ip": "8.8.8.8",
    "expect": {
      "profile.is_anycast": true
    }
  }
]

Пример вывода:

{
  "checked": 2,
  "passed": 1,
  "failed": 1,
  "results": [
    {
      "ip": "91.196.220.30",
      "passed": true
    },
    {
      "ip": "8.8.8.8",
      "passed": false,
      "failures": [
        {
          "field": "profile.is_anycast",
          "expected": true,
          "actual": false,
          "message": "value mismatch"
        }
      ]
    }
  ]
}

Smoke-тесты лучше всего подходят для важных примеров: известных выходных узлов VPN, известных резидентных IP-адресов, известных anycast-адресов, тестовых префиксов, внутренних фикстур и граничных случаев, о которых сообщили клиенты.

Smoke-кейсы поддерживают точные ожидания, разрешённые наборы значений и запрещённые наборы значений:

{
  "ip": "91.196.220.30",
  "expect": {
    "privacy.is_vpn": true,
    "privacy.vpn_provider": "NordVPN"
  },
  "allow": {
    "geo.city_name": ["Los Angeles", "London"]
  },
  "deny": {
    "privacy.vpn_provider": [null, ""]
  }
}

test

test — это тестовый стенд (testbench) MMDB Forge. Он превращает эталонные (golden) IP-выборки в повторно используемые артефакты релизов.

Запустите конфигурацию тестового стенда:

mmdbforge test run examples/ipbench.yaml --output baseline.json

Запустите её снова для новой версии базы данных:

mmdbforge test run examples/ipbench.yaml --output current.json

Сравните два запуска:

mmdbforge test compare baseline.json current.json --output compare.json

Сформируйте отчёты:

mmdbforge test report compare.json --markdown testbench.md
mmdbforge test report compare.json --html testbench.html

Пример конфигурации:

name: vpn-release-golden-samples
database: vpn.mmdb
fields:
  - privacy.is_vpn
  - privacy.vpn_provider
  - network.connection_type
  - geo.city_name
cases:
  - ip: 91.196.220.30
    expect:
      privacy.is_vpn: true
      privacy.vpn_provider: NordVPN
      network.connection_type: hosting
    allow:
      geo.city_name:
        - Los Angeles
        - London
    deny:
      privacy.vpn_provider:
        - ""
        - null

Это слой «модульных тестов для баз данных IP-аналитики»: точные ожидаемые поля, допустимые альтернативы, запрещённые значения, сохраняемые артефакты запусков, сравнение запусков и отчёты в HTML/Markdown.

prefixes

prefixes проверяет распределение формы префиксов и предупреждает, когда записи уровня хоста доминируют в выборке сетей.

mmdbforge prefixes vpn.mmdb --sample 100000 --table
mmdbforge prefixes vpn.mmdb --full
mmdbforge prefixes old.mmdb new.mmdb --sample 100000

Это позволяет выявить ошибки релиза, такие как неожиданный взрывной рост записей /32 или /128. Скомпилированные данные MMDB уже хранятся в виде поискового префиксного дерева (trie), поэтому это проверка формы собранной базы данных, а не линтер перекрытий CIDR в необработанных исходных данных.

bench

bench измеряет пропускную способность поиска на выборке.

mmdbforge bench vpn.mmdb --sample 100000 --table
mmdbforge bench vpn.mmdb --full
mmdbforge bench old.mmdb new.mmdb --sample 100000

Используйте его, чтобы выявлять релизы, которые стали заметно медленнее, даже когда проверки схемы и smoke-тесты проходят.

audit release

audit release объединяет основные проверки релиза в одну команду.

mmdbforge audit release \
  --old vpn-2026-05-20.mmdb \
  --new vpn-2026-05-21.mmdb \
  --schema examples/vpn.schema.json \
  --smoke examples/smoke.json \
  --policy examples/release.policy.json \
  --sample 100000 \
  --markdown report.md \
  --html report.html

Он выполняет:

Пример вывода в Markdown:

# MMDB Release Audit

Verdict: WARN

- 8.42% sampled records changed
- file size changed 4.80%
- schema validation errors: 0
- smoke tests failed: 0

Вердикты:

Пример политики:

{
  "max_changed_percent": 25,
  "max_file_growth_percent": 50,
  "max_lookup_slowdown_percent": 25,
  "max_host_level_growth_percent": 50,
  "required_fields": [
    "privacy.is_vpn",
    "confidence"
  ],
  "allowed_dropped_fields": []
}

Пример CI

MMDB Forge спроектирован для применения в CI.

name: MMDB release checks

on:
  pull_request:
  workflow_dispatch:

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-go@v5
        with:
          go-version: "1.22"

      - name: Install mmdbforge
        run: go install ./cmd/mmdbforge

      - name: Audit MMDB release
        run: |
          mmdbforge audit release \
            --old artifacts/vpn-previous.mmdb \
            --new artifacts/vpn-current.mmdb \
            --schema examples/vpn.schema.json \
            --policy examples/release.policy.json \
            --smoke fixtures/vpn-smoke.json \
            --sample 100000 \
            --markdown report.md \
            --html report.html

      - name: Upload report
        uses: actions/upload-artifact@v4
        with:
          name: mmdb-audit-report
          path: |
            report.md
            report.html

Для более строгих условий допуска релиза используйте diff напрямую:

mmdbforge diff artifacts/vpn-previous.mmdb artifacts/vpn-current.mmdb \
  --sample 100000 \
  --fail-threshold changed_percent=25 \
  --fail-on-missing-field confidence \
  --fail-on-drop privacy.vpn_provider

Модель данных

MMDB Forge превращает вложенные поля записей в плоские пути, разделённые точками:

{
  "privacy": {
    "is_vpn": true,
    "vpn_provider": "NordVPN"
  },
  "confidence": 95
}

преобразуется в:

privacy.is_vpn
privacy.vpn_provider
confidence

Благодаря этому правила схемы, сводки diff, проверки CI и ожидания smoke-тестов легко писать и легко проверять.

Почему этот проект существует

Когда команды собирают собственные MMDB-файлы, сбои редко очевидны по одному запросу (lookup). Реальные проблемы релизов выглядят так:

the new database is 3x larger
some fields disappeared
country_code suddenly became registry country
confidence escaped the 0..100 range
all VPN records became risk_score=100
/32 records exploded unexpectedly
provider names disappeared
IPv6 coverage broke
known smoke-test IPs changed behavior

MMDB Forge даёт этим проблемам имена, команды и режимы сбоя CI.

Структура проекта

cmd/mmdbforge/      CLI entrypoint
internal/lookup/    inspect and explain commands
internal/diff/      sampled release diff
internal/schema/    schema validation
internal/stats/     field coverage and top values
internal/smoke/     known IP regression tests
internal/audit/     release audit orchestration
internal/report/    JSON and Markdown output
examples/           schema and smoke examples
docs/               command guides and CI notes

Лицензия

MIT. См. LICENSE.