Skip to main content

Reliable WAN/LAN IP discovery and Cloudflare DNS synchronization

Project description

ip-reporter

ip-reporter is a dependency-free Python package and CLI for discovering local LAN addresses, public WAN addresses, and public addresses obtained through HTTP/2 or HTTP/3. It can synchronize one or many A/AAAA records in Cloudflare.

The import name is ip_reporter; the installed command is ip-reporter.

Install with uv

Install the command as a uv tool:

uv tool install ip-reporter
ip-reporter --scope wan --family ipv4

Use it as a library dependency:

uv add ip-reporter

For HTTP/2 and HTTP/3 probing on macOS, install Homebrew curl. The package rejects /usr/bin/curl on macOS so the system curl cannot silently disable those probes.

brew install curl
export IP_REPORTER_CURL="$(brew --prefix curl)/bin/curl"

The selected curl must report HTTP2 or HTTP3 in curl --version depending on the requested probe. HTTP/3 requires a curl build with HTTP/3 support.

CLI

With no subcommand, the CLI performs discovery. The default family is IPv4.

# Public IPv4; text output includes the scope, family, and transport.
ip-reporter --scope wan --family ipv4

# LAN IPv4 and IPv6.
ip-reporter discover --scope lan --family ipv4 --family ipv6

# Force HTTP/3 over IPv4 and IPv6.
ip-reporter --scope wan --family h3ipv4 --family h3ipv6

# Structured output for scripts.
ip-reporter --scope both --family ipv4 --family ipv6 --format json

# Print only address values, one per line.
ip-reporter --scope wan --family ipv4 --format values

WAN probing has independent failover endpoints. The defaults include two providers for IPv4, two for IPv6, and two candidates for each HTTP/3 family. Override them with repeated --wan-endpoint options or with the Python RemoteEndpoint API.

Internet speed

Run a simple receive/download and send/upload throughput check against Cloudflare's edge:

ip-reporter speed

Use ip-reporter speed --format json for structured output. The defaults download 10 MB and upload 2 MB. This is a quick single-transfer estimate, not a full multi-connection benchmark; sample sizes can be changed with --download-bytes and --upload-bytes.

The same check is available from Python:

from ip_reporter import check_internet_speed

speed = check_internet_speed()
print(speed.download_mbps, speed.upload_mbps)

Cloudflare DNS

Use an API token with the DNS Write permission. Prefer environment variables so credentials do not enter shell history:

export CLOUDFLARE_API_TOKEN="..."
export CLOUDFLARE_ZONE_ID="..."

Update one existing record:

ip-reporter dns update \
  --record home.example.com \
  --family ipv4

Update many names and both address families:

ip-reporter dns update \
  --record home.example.com \
  --record vpn.example.com \
  --family ipv4 \
  --family ipv6 \
  --ttl 60 \
  --not-proxied

Existing records are updated with PATCH and unchanged values are skipped. Missing records are not created unless --create-missing is supplied. Stale records are not deleted. Use --dry-run to inspect discovery without changing Cloudflare.

If you have a zone name rather than an ID, pass --zone-name; the client can resolve it through the Cloudflare API. Relative names and @ are accepted when the zone name is supplied:

ip-reporter dns update \
  --zone-name example.com \
  --record home \
  --family ipv4 \
  --create-missing

h3ipv4 writes an A record and h3ipv6 writes an AAAA record. The HTTP/3 transport is discovery metadata; the DNS type is determined from the resulting IP address.

Python API

The structured API is suitable for another package, a scheduler, or a daemon:

from ip_reporter import AddressFamily, IPReporter

reporter = IPReporter()
report = reporter.discover(
    scope="wan",
    families=(AddressFamily.IPV4, AddressFamily.IPV6),
)

for address in report.wan:
    print(address.value, address.family.value, address.protocol.value)

# Convenience methods return only strings.
wan_ipv4 = reporter.get_wan(families=(AddressFamily.IPV4,))
lan_ipv6 = reporter.get_lan(families=(AddressFamily.IPV6,))

The discovery dependencies are injectable, which makes applications and tests independent of the network:

from ip_reporter import AddressDiscovery, AddressFamily, IPReporter, RemoteEndpoint

class Probe:
    def fetch_ip(self, url, family):
        return ("203.0.113.10", "http2")

reporter = IPReporter(
    AddressDiscovery(
        wan_probe=Probe(),
        endpoints=(RemoteEndpoint("https://probe.example/ip", (AddressFamily.IPV4,)),),
    )
)

Synchronize Cloudflare from the same service:

from ip_reporter import AddressFamily, IPReporter

updates = IPReporter().update_cloudflare_dns(
    ["home.example.com", "vpn.example.com"],
    zone_id="your-zone-id",
    families=(AddressFamily.IPV4, AddressFamily.IPV6),
    create_missing=True,
)
print(updates.as_dict())

CloudflareClient.from_env() uses CLOUDFLARE_API_TOKEN, or the legacy CLOUDFLARE_EMAIL and CLOUDFLARE_API_KEY pair. A custom HTTP transport can be injected into CloudflareClient for testing.

Prefect IPv4 task

Install the optional Prefect integration, then use the task in a deployment or call it directly. It persists the last IPv4 to the defined state path and only calls Cloudflare when the newly discovered value differs. The default state path is ~/.local/state/ip-reporter/wan_ipv4.txt; provide an absolute path in production so the state survives task-run environments.

uv add 'ip-reporter[prefect]'
export CLOUDFLARE_API_TOKEN="..."
export CLOUDFLARE_ZONE_ID="..."
from ip_reporter.prefect import fetch_ipv4_and_sync_cloudflare

result = fetch_ipv4_and_sync_cloudflare(
    ["home.example.com", "vpn.example.com"],
    ipv4_state_path="/var/lib/ip-reporter/home-ipv4.txt",
    create_missing=True,
)
print(result)

The state file is atomically written only after Cloudflare succeeds. If the task fails, its next run will retry the DNS synchronization rather than assume the address was updated.

Build and publish

From a checkout:

uv sync --extra dev
uv run pytest
uv build
uv publish

Set UV_PUBLISH_TOKEN before uv publish, or configure another uv-supported PyPI credential method. Bump project.version in pyproject.toml for each release.

Cloudflare API details: DNS record create, DNS record update.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ip_reporter-0.1.3.tar.gz (195.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ip_reporter-0.1.3-py3-none-any.whl (26.7 kB view details)

Uploaded Python 3

File details

Details for the file ip_reporter-0.1.3.tar.gz.

File metadata

  • Download URL: ip_reporter-0.1.3.tar.gz
  • Upload date:
  • Size: 195.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for ip_reporter-0.1.3.tar.gz
Algorithm Hash digest
SHA256 5f54688038d5c9c9da7b61b21d628eb712be91cd0fbc8b343679b54d0d8bc698
MD5 d499d3696c3f360a216f84c2817bb6c8
BLAKE2b-256 7eabda279506ece26427ae104cc64fbc595bf92bed338cb3c5572360fc772aaf

See more details on using hashes here.

File details

Details for the file ip_reporter-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: ip_reporter-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 26.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for ip_reporter-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 b89602c8bb2523d0a823e28672aa3b4d3aa5cf8261d51139cc1af597e8970eae
MD5 c8465fceb9783d6fe94df42d0ca0ef43
BLAKE2b-256 a76aaf3a8cd6e7444d33bc6a311a8d007f6ea1ab8774bfbf36f7947a04beb4bb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page