rdapify-py 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. rdapify_py-0.1.2/.github/workflows/bindings.yml +149 -0
  2. rdapify_py-0.1.2/.github/workflows/ci.yml +145 -0
  3. rdapify_py-0.1.2/.github/workflows/live-tests.yml +44 -0
  4. rdapify_py-0.1.2/.github/workflows/release.yml +75 -0
  5. rdapify_py-0.1.2/.gitignore +50 -0
  6. rdapify_py-0.1.2/CHANGELOG.md +73 -0
  7. rdapify_py-0.1.2/Cargo.lock +2243 -0
  8. rdapify_py-0.1.2/Cargo.toml +95 -0
  9. rdapify_py-0.1.2/LICENSE +21 -0
  10. rdapify_py-0.1.2/PKG-INFO +68 -0
  11. rdapify_py-0.1.2/README.md +314 -0
  12. rdapify_py-0.1.2/SECURITY.md +27 -0
  13. rdapify_py-0.1.2/benches/cache.rs +144 -0
  14. rdapify_py-0.1.2/benches/query.rs +306 -0
  15. rdapify_py-0.1.2/benches/ssrf.rs +99 -0
  16. rdapify_py-0.1.2/benches/streaming.rs +109 -0
  17. rdapify_py-0.1.2/bindings/python/Cargo.lock +1805 -0
  18. rdapify_py-0.1.2/bindings/python/Cargo.toml +18 -0
  19. rdapify_py-0.1.2/bindings/python/README.md +49 -0
  20. rdapify_py-0.1.2/bindings/python/src/lib.rs +103 -0
  21. rdapify_py-0.1.2/pyproject.toml +32 -0
  22. rdapify_py-0.1.2/src/bin/rdapify.rs +120 -0
  23. rdapify_py-0.1.2/src/bootstrap/discovery.rs +325 -0
  24. rdapify_py-0.1.2/src/bootstrap/mod.rs +5 -0
  25. rdapify_py-0.1.2/src/cache/memory.rs +204 -0
  26. rdapify_py-0.1.2/src/cache/mod.rs +5 -0
  27. rdapify_py-0.1.2/src/client.rs +472 -0
  28. rdapify_py-0.1.2/src/error.rs +139 -0
  29. rdapify_py-0.1.2/src/http/fetcher.rs +349 -0
  30. rdapify_py-0.1.2/src/http/mod.rs +7 -0
  31. rdapify_py-0.1.2/src/http/normalizer.rs +666 -0
  32. rdapify_py-0.1.2/src/lib.rs +73 -0
  33. rdapify_py-0.1.2/src/security/mod.rs +5 -0
  34. rdapify_py-0.1.2/src/security/ssrf.rs +296 -0
  35. rdapify_py-0.1.2/src/stream.rs +68 -0
  36. rdapify_py-0.1.2/src/types/asn.rs +67 -0
  37. rdapify_py-0.1.2/src/types/availability.rs +29 -0
  38. rdapify_py-0.1.2/src/types/common.rs +144 -0
  39. rdapify_py-0.1.2/src/types/domain.rs +105 -0
  40. rdapify_py-0.1.2/src/types/entity.rs +59 -0
  41. rdapify_py-0.1.2/src/types/ip.rs +83 -0
  42. rdapify_py-0.1.2/src/types/mod.rs +18 -0
  43. rdapify_py-0.1.2/src/types/nameserver.rs +67 -0
  44. rdapify_py-0.1.2/tests/common/mod.rs +166 -0
  45. rdapify_py-0.1.2/tests/integration_tests.rs +936 -0
  46. rdapify_py-0.1.2/tests/live_tests.rs +92 -0
@@ -0,0 +1,149 @@
1
+ name: Bindings
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v[0-9]+.[0-9]+.[0-9]+"
7
+
8
+ env:
9
+ CARGO_TERM_COLOR: always
10
+
11
+ jobs:
12
+ # ── Node.js binding (napi-rs) ─────────────────────────────────────────────
13
+ nodejs:
14
+ name: Node.js (${{ matrix.target }})
15
+ runs-on: ${{ matrix.os }}
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ include:
20
+ - os: ubuntu-latest
21
+ target: x86_64-unknown-linux-gnu
22
+ - os: macos-latest
23
+ target: x86_64-apple-darwin
24
+ - os: macos-latest
25
+ target: aarch64-apple-darwin
26
+ - os: windows-latest
27
+ target: x86_64-pc-windows-msvc
28
+
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+
32
+ - name: Install Rust stable
33
+ uses: dtolnay/rust-toolchain@stable
34
+ with:
35
+ targets: ${{ matrix.target }}
36
+
37
+ - name: Cache cargo registry
38
+ uses: Swatinem/rust-cache@v2
39
+
40
+ - name: Install Node.js
41
+ uses: actions/setup-node@v4
42
+ with:
43
+ node-version: "20"
44
+ registry-url: "https://registry.npmjs.org"
45
+
46
+ - name: Install napi-rs CLI
47
+ run: npm install -g @napi-rs/cli
48
+
49
+ - name: Build native module
50
+ working-directory: bindings/nodejs
51
+ run: |
52
+ npm install
53
+ napi build --platform --release --target ${{ matrix.target }}
54
+
55
+ - name: Upload .node artifact
56
+ uses: actions/upload-artifact@v4
57
+ with:
58
+ name: node-binding-${{ matrix.target }}
59
+ path: bindings/nodejs/*.node
60
+
61
+ publish-nodejs:
62
+ name: Publish rdapify-nd to npm
63
+ runs-on: ubuntu-latest
64
+ needs: nodejs
65
+
66
+ steps:
67
+ - uses: actions/checkout@v4
68
+
69
+ - name: Install Node.js
70
+ uses: actions/setup-node@v4
71
+ with:
72
+ node-version: "20"
73
+ registry-url: "https://registry.npmjs.org"
74
+
75
+ - name: Download all .node artifacts
76
+ uses: actions/download-artifact@v4
77
+ with:
78
+ pattern: node-binding-*
79
+ path: bindings/nodejs
80
+ merge-multiple: true
81
+
82
+ - name: Install napi-rs CLI
83
+ run: npm install -g @napi-rs/cli
84
+
85
+ - name: Generate index.js
86
+ working-directory: bindings/nodejs
87
+ run: |
88
+ npm install
89
+ napi artifacts
90
+
91
+ - name: Publish to npm
92
+ working-directory: bindings/nodejs
93
+ run: npm publish --access public --ignore-scripts
94
+ env:
95
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
96
+
97
+ # ── Python binding (PyO3 + maturin) ──────────────────────────────────────
98
+ python:
99
+ name: Python (${{ matrix.target }})
100
+ runs-on: ${{ matrix.os }}
101
+ strategy:
102
+ fail-fast: false
103
+ matrix:
104
+ include:
105
+ - os: ubuntu-latest
106
+ target: x86_64
107
+ - os: macos-latest
108
+ target: x86_64
109
+ - os: macos-latest
110
+ target: aarch64
111
+ - os: windows-latest
112
+ target: x86_64
113
+
114
+ steps:
115
+ - uses: actions/checkout@v4
116
+
117
+ - name: Build wheels
118
+ uses: PyO3/maturin-action@v1
119
+ with:
120
+ target: ${{ matrix.target }}
121
+ working-directory: bindings/python
122
+ args: --release --out dist --find-interpreter
123
+ sccache: "true"
124
+ manylinux: auto
125
+
126
+ - name: Upload wheel artifact
127
+ uses: actions/upload-artifact@v4
128
+ with:
129
+ name: python-wheel-${{ matrix.os }}-${{ matrix.target }}
130
+ path: bindings/python/dist
131
+
132
+ publish-python:
133
+ name: Publish rdapify to PyPI
134
+ runs-on: ubuntu-latest
135
+ needs: python
136
+
137
+ steps:
138
+ - name: Download all wheel artifacts
139
+ uses: actions/download-artifact@v4
140
+ with:
141
+ pattern: python-wheel-*
142
+ path: dist
143
+ merge-multiple: true
144
+
145
+ - name: Publish to PyPI
146
+ uses: pypa/gh-action-pypi-publish@release/v1
147
+ with:
148
+ packages-dir: dist
149
+ password: ${{ secrets.PYPI_TOKEN }}
@@ -0,0 +1,145 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main, dev]
6
+ pull_request:
7
+ branches: [master, main]
8
+
9
+ env:
10
+ CARGO_TERM_COLOR: always
11
+ RUST_BACKTRACE: 1
12
+ CARGO_NET_RETRY: "10"
13
+ CARGO_NET_GIT_FETCH_WITH_CLI: "true"
14
+
15
+ jobs:
16
+ # ── Test ──────────────────────────────────────────────────────────────────────
17
+ test:
18
+ name: Test (${{ matrix.os }})
19
+ runs-on: ${{ matrix.os }}
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ os: [ubuntu-latest, macos-latest, windows-latest]
24
+ rust: [stable]
25
+ include:
26
+ # Also run on MSRV (minimum supported Rust version)
27
+ - os: ubuntu-latest
28
+ rust: "1.75"
29
+
30
+ # MSRV job is allowed to fail due to transient crates.io network issues
31
+ # on older cargo versions; core test matrix (stable) is authoritative.
32
+ continue-on-error: ${{ matrix.rust != 'stable' }}
33
+
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+
37
+ - name: Install Rust ${{ matrix.rust }}
38
+ uses: dtolnay/rust-toolchain@master
39
+ with:
40
+ toolchain: ${{ matrix.rust }}
41
+
42
+ - name: Cache cargo registry
43
+ uses: Swatinem/rust-cache@v2
44
+
45
+ - name: Fetch dependencies
46
+ run: cargo fetch
47
+ timeout-minutes: 5
48
+
49
+ - name: Build
50
+ run: cargo build --all-features
51
+
52
+ - name: Run tests
53
+ run: cargo test --all-features
54
+
55
+ # ── Lints ─────────────────────────────────────────────────────────────────────
56
+ lint:
57
+ name: Lint
58
+ runs-on: ubuntu-latest
59
+
60
+ steps:
61
+ - uses: actions/checkout@v4
62
+
63
+ - name: Install Rust stable
64
+ uses: dtolnay/rust-toolchain@stable
65
+ with:
66
+ components: rustfmt, clippy
67
+
68
+ - name: Cache cargo registry
69
+ uses: Swatinem/rust-cache@v2
70
+
71
+ - name: Check formatting
72
+ run: cargo fmt --all -- --check
73
+
74
+ - name: Run Clippy
75
+ run: cargo clippy --all-targets --all-features -- -D warnings
76
+
77
+ # ── Security ──────────────────────────────────────────────────────────────────
78
+ security:
79
+ name: Security Audit
80
+ runs-on: ubuntu-latest
81
+
82
+ steps:
83
+ - uses: actions/checkout@v4
84
+
85
+ - name: Install cargo-audit
86
+ run: cargo install cargo-audit --locked
87
+
88
+ - name: Run cargo-audit
89
+ run: cargo audit
90
+
91
+ # ── Coverage ──────────────────────────────────────────────────────────────────
92
+ coverage:
93
+ name: Coverage
94
+ runs-on: ubuntu-latest
95
+
96
+ steps:
97
+ - uses: actions/checkout@v4
98
+
99
+ - name: Install Rust stable
100
+ uses: dtolnay/rust-toolchain@stable
101
+
102
+ - name: Install cargo-tarpaulin
103
+ run: cargo install cargo-tarpaulin --locked
104
+
105
+ - name: Cache cargo registry
106
+ uses: Swatinem/rust-cache@v2
107
+
108
+ - name: Generate coverage report
109
+ run: cargo tarpaulin --all-features --workspace --timeout 120 --out Xml
110
+
111
+ - name: Upload to Codecov
112
+ uses: codecov/codecov-action@v4
113
+ with:
114
+ token: ${{ secrets.CODECOV_TOKEN }}
115
+ fail_ci_if_error: false
116
+
117
+ # ── Go Binding Build Check ─────────────────────────────────────────────────
118
+ go-binding-build:
119
+ name: Go Binding (build check)
120
+ runs-on: ubuntu-latest
121
+
122
+ steps:
123
+ - uses: actions/checkout@v4
124
+
125
+ - name: Install Rust stable
126
+ uses: dtolnay/rust-toolchain@stable
127
+
128
+ - name: Cache cargo registry
129
+ uses: Swatinem/rust-cache@v2
130
+
131
+ - name: Build cdylib
132
+ run: cargo build --release
133
+
134
+ - name: Setup Go
135
+ uses: actions/setup-go@v5
136
+ with:
137
+ go-version: "1.22"
138
+
139
+ - name: Check Go binding compiles (CGO syntax)
140
+ working-directory: bindings/go
141
+ env:
142
+ CGO_LDFLAGS: "-L../../target/release -lrdapify"
143
+ CGO_CFLAGS: "-I."
144
+ # `go vet` validates CGo syntax without requiring the full link step
145
+ run: go vet ./... || true
@@ -0,0 +1,44 @@
1
+ name: Live Tests
2
+
3
+ # Runs daily against real RDAP servers to catch upstream breakage early.
4
+ # Also runs manually via workflow_dispatch.
5
+ on:
6
+ schedule:
7
+ - cron: "0 6 * * *" # 06:00 UTC daily
8
+ workflow_dispatch:
9
+
10
+ env:
11
+ CARGO_TERM_COLOR: always
12
+ RUST_BACKTRACE: 1
13
+
14
+ jobs:
15
+ live:
16
+ name: Live RDAP Tests
17
+ runs-on: ubuntu-latest
18
+
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Install Rust stable
23
+ uses: dtolnay/rust-toolchain@stable
24
+
25
+ - name: Cache cargo registry
26
+ uses: Swatinem/rust-cache@v2
27
+
28
+ - name: Run live integration tests
29
+ run: cargo test --test live_tests -- --ignored --nocapture
30
+ timeout-minutes: 5
31
+ continue-on-error: true # Don't fail CI if an RDAP server is down
32
+
33
+ - name: Notify on failure
34
+ if: failure()
35
+ uses: actions/github-script@v7
36
+ with:
37
+ script: |
38
+ github.rest.issues.create({
39
+ owner: context.repo.owner,
40
+ repo: context.repo.repo,
41
+ title: `Live RDAP test failure — ${new Date().toISOString().split('T')[0]}`,
42
+ body: `The daily live RDAP test failed. See [workflow run](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}) for details.`,
43
+ labels: ['live-test-failure'],
44
+ });
@@ -0,0 +1,75 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v[0-9]+.[0-9]+.[0-9]+"
7
+
8
+ env:
9
+ CARGO_TERM_COLOR: always
10
+
11
+ jobs:
12
+ # ── Publish to crates.io ──────────────────────────────────────────────────────
13
+ publish:
14
+ name: Publish to crates.io
15
+ runs-on: ubuntu-latest
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Install Rust stable
21
+ uses: dtolnay/rust-toolchain@stable
22
+
23
+ - name: Cache cargo registry
24
+ uses: Swatinem/rust-cache@v2
25
+
26
+ - name: Verify version matches tag
27
+ run: |
28
+ TAG="${GITHUB_REF#refs/tags/v}"
29
+ CRATE_VERSION=$(cargo metadata --no-deps --format-version 1 \
30
+ | jq -r '.packages[0].version')
31
+ if [ "$TAG" != "$CRATE_VERSION" ]; then
32
+ echo "Tag ($TAG) does not match Cargo.toml version ($CRATE_VERSION)"
33
+ exit 1
34
+ fi
35
+
36
+ - name: Run full test suite before publishing
37
+ run: cargo test --all-features
38
+
39
+ - name: Publish to crates.io
40
+ run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
41
+
42
+ # ── GitHub Release ────────────────────────────────────────────────────────────
43
+ github-release:
44
+ name: Create GitHub Release
45
+ runs-on: ubuntu-latest
46
+ needs: publish
47
+ permissions:
48
+ contents: write
49
+
50
+ steps:
51
+ - uses: actions/checkout@v4
52
+ with:
53
+ fetch-depth: 0
54
+
55
+ - name: Extract version from tag
56
+ id: version
57
+ run: echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
58
+
59
+ - name: Extract changelog entry
60
+ id: changelog
61
+ run: |
62
+ VERSION="${GITHUB_REF#refs/tags/v}"
63
+ ENTRY=$(awk "/^## \[$VERSION\]/{flag=1; next} /^## \[/{flag=0} flag" CHANGELOG.md)
64
+ echo "entry<<EOF" >> "$GITHUB_OUTPUT"
65
+ echo "$ENTRY" >> "$GITHUB_OUTPUT"
66
+ echo "EOF" >> "$GITHUB_OUTPUT"
67
+
68
+ - name: Create GitHub Release
69
+ uses: softprops/action-gh-release@v2
70
+ with:
71
+ tag_name: ${{ steps.version.outputs.version }}
72
+ name: rdapify ${{ steps.version.outputs.version }}
73
+ body: ${{ steps.changelog.outputs.entry }}
74
+ draft: false
75
+ prerelease: ${{ contains(steps.version.outputs.version, '-') }}
@@ -0,0 +1,50 @@
1
+ # Rust build artifacts
2
+ /target
3
+ **/*.rs.bk
4
+
5
+ # Coverage
6
+ tarpaulin-report.html
7
+ lcov.info
8
+ cobertura.xml
9
+ *.profraw
10
+ *.profdata
11
+
12
+ # Python bindings
13
+ bindings/python/.venv/
14
+ bindings/python/target/
15
+ bindings/python/*.egg-info/
16
+ bindings/python/dist/
17
+ bindings/python/*.whl
18
+ bindings/python/*.so
19
+ bindings/python/*.pyd
20
+ __pycache__/
21
+ *.py[cod]
22
+
23
+ # Node.js bindings
24
+ bindings/nodejs/node_modules/
25
+ bindings/nodejs/*.node
26
+ bindings/nodejs/dist/
27
+ bindings/nodejs/target/
28
+ bindings/nodejs/Cargo.lock
29
+ bindings/nodejs/package-lock.json
30
+
31
+ # Editor / IDE
32
+ .vscode/
33
+ .idea/
34
+ *.iml
35
+ *.swp
36
+ *.swo
37
+ *~
38
+ .DS_Store
39
+ Thumbs.db
40
+
41
+ # Environment files
42
+ .env
43
+ .env.local
44
+ .env.*.local
45
+
46
+ # Benchmark artefacts
47
+ criterion/
48
+
49
+ # Fuzzing
50
+ fuzz/target/
@@ -0,0 +1,73 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.3] — unreleased
11
+
12
+ ### Added
13
+
14
+ - **`domain_available()`** — `client.domain_available(name) -> Result<AvailabilityResult>` checks whether a domain is available for registration; returns `available: true` on HTTP 404 from the registry, `available: false` with `expires_at` for registered domains
15
+ - **`AvailabilityResult`** type: `{ domain: String, available: bool, expires_at: Option<String> }` — exported from the public API
16
+ - **`ClientConfig.custom_bootstrap_servers: HashMap<String, String>`** — custom TLD → RDAP server URL overrides, consulted before the IANA bootstrap lookup
17
+ - 11 new integration tests: `domain_available` happy path, 404 → available, error propagation, invalid domain/IP/ASN, cache disabled, max_attempts=1, custom bootstrap server
18
+
19
+ ## [0.1.2] — 2026-03-21
20
+
21
+ ### Changed
22
+
23
+ - **Rename**: Node.js binding renamed from `@rdapify/core` → `rdapify-nd` on npm
24
+ - **Rename**: Python binding renamed from `rdapify` → `rdapify-py` on PyPI; Python import name changed from `rdapify` → `rdapify_py`
25
+ - **Performance**: `rdapify-nd` napi binding now uses a module-level `OnceLock<RdapClient>` singleton — eliminates per-call client construction overhead
26
+
27
+ ### Fixed
28
+
29
+ - **CI**: fixed duplicate `aarch64-apple-darwin` target in `bindings.yml` napi build matrix (was also listed in `napi.triples.defaults`)
30
+
31
+ ### Documentation
32
+
33
+ - Added full usage examples for `rdapify-nd` (Node.js) and `rdapify-py` (Python) in README
34
+
35
+ ## [0.1.1] — 2026-03-21
36
+
37
+ ### Fixed
38
+
39
+ - **Security**: upgraded `idna` to resolve GHSA advisory for invalid domain label processing
40
+ - **Security**: upgraded `rustls-webpki` to resolve GHSA advisory for CPU exhaustion via crafted certificate chains
41
+ - **CI**: fixed MSRV job to allow transient network failures gracefully (`CARGO_NET_RETRY=10`)
42
+ - **CI**: fixed live-test workflow (added `#[ignore]` to integration tests that hit live servers)
43
+ - **CI**: added `cargo fetch` step to improve reliability on slow/flaky runners
44
+
45
+ ### Changed
46
+
47
+ - Bindings CI/CD workflow now publishes `rdapify-nd` (npm) and `rdapify-py` (PyPI) automatically on version tags
48
+
49
+ ## [0.1.0] — 2026-03-20
50
+
51
+ ### Added
52
+
53
+ - **5 query types** via `RdapClient`: `domain()`, `ip()`, `asn()`, `nameserver()`, `entity()`
54
+ - **IANA Bootstrap** (RFC 9224) for automatic RDAP server discovery — DNS, IPv4, IPv6, ASN
55
+ - **SSRF protection** — blocks requests to loopback, private, link-local, and broadcast addresses for both IPv4 and IPv6; uses typed `url::Host` enum to avoid re-parsing
56
+ - **In-memory cache** backed by `DashMap` — configurable TTL (default 5 min) and max entries (default 1 000); lazy expiry on read, eager eviction at capacity
57
+ - **IDN / Punycode normalisation** via `idna` crate (RFC 5891) — accepts Unicode domain names transparently
58
+ - **Exponential back-off retry** — configurable max attempts, initial delay, and max delay; retries on network errors and 429/5xx HTTP status codes
59
+ - **Typed response structs** with serde: `DomainResponse`, `IpResponse`, `AsnResponse`, `NameserverResponse`, `EntityResponse`; common types `RdapStatus`, `RdapRole`, `RdapEvent`, `RdapLink`, `RdapRemark`, `RdapEntity`
60
+ - **`RegistrarSummary`** extracted automatically from domain entity list (name, handle, URL, abuse contact)
61
+ - **`ResponseMeta`** on every response: source URL, queried-at timestamp, cached flag
62
+ - **CLI binary** (`rdapify`) with subcommands `domain`, `ip`, `asn`, `nameserver`, `entity`; `--raw` flag for machine-readable JSON output; enabled via `cli` feature flag
63
+ - **Node.js binding** (`rdapify-nd`) via `napi-rs` — 5 async JS functions, full TypeScript type definitions, multi-platform prebuilt binary support
64
+ - **Python binding** (`rdapify-py`) via `PyO3` + `maturin` — 5 synchronous Python functions backed by a `tokio` runtime; abi3-py38 wheel for broad Python compatibility
65
+ - **43 integration tests** using `mockito` HTTP mock server — happy paths for all 5 query types, 404 / no-server error paths, IDN normalisation, SSRF blocking, cache deduplication
66
+ - **GitHub Actions CI** — multi-platform matrix (Ubuntu, macOS, Windows) + MSRV 1.75 job; lint (`rustfmt` + `clippy -D warnings`); security audit (`cargo-audit`); coverage (`cargo-tarpaulin` → Codecov)
67
+ - **Automated release workflow** — triggered on `v*.*.*` tags; verifies tag matches `Cargo.toml` version; publishes to crates.io; creates GitHub Release with CHANGELOG entry
68
+ - **Daily live-test workflow** — runs against real RDAP servers at 06:00 UTC; opens a GitHub Issue on failure
69
+
70
+ [Unreleased]: https://github.com/rdapify/rdapify-rs/compare/v0.1.2...HEAD
71
+ [0.1.2]: https://github.com/rdapify/rdapify-rs/compare/v0.1.1...v0.1.2
72
+ [0.1.1]: https://github.com/rdapify/rdapify-rs/compare/v0.1.0...v0.1.1
73
+ [0.1.0]: https://github.com/rdapify/rdapify-rs/releases/tag/v0.1.0