Skip to main content

A delightfully Pythonic library for managing DNS at Porkbun

Project description

๐Ÿท Oinker

Domain management that doesn't stink! ๐Ÿฝ

A delightfully Pythonic library for managing domains at Porkbun. DNS records, DNSSEC, SSL certificates, URL forwarding, and more. Async-first with sync wrappers, type-safe, and thoroughly tested.

๐Ÿ“š Full Documentation | ๐Ÿฝ Not affiliated with Porkbun

CI codecov PyPI version Python 3.13+ License: MIT

Features

  • ๐Ÿš€ Async-first design - Built on httpx for modern async/await support
  • ๐Ÿ”’ Type-safe records - Dataclasses with validation for all DNS record types
  • ๐Ÿ”„ Sync wrappers - Use Piglet when you don't need async
  • ๐Ÿ’ป CLI included - Manage DNS from the command line
  • ๐Ÿ” Auto-retry - Exponential backoff for transient failures
  • ๐Ÿ Python 3.13+ - Modern Python with full type annotations

๐Ÿ“ฆ Installation

pip install oinker

For CLI support:

pip install "oinker[cli]"

๐Ÿš€ Quick Start

Set your Porkbun API credentials:

export PORKBUN_API_KEY="pk1_..."
export PORKBUN_SECRET_KEY="sk1_..."

Async (Recommended)

from oinker import AsyncPiglet, ARecord

async with AsyncPiglet() as piglet:
    # Test connection
    pong = await piglet.ping()
    print(f"Your IP: {pong.your_ip}")

    # List DNS records
    records = await piglet.dns.list("example.com")
    for record in records:
        print(f"{record.record_type} {record.name} -> {record.content}")

    # Create an A record
    record_id = await piglet.dns.create(
        "example.com",
        ARecord(content="1.2.3.4", name="www")
    )

    # Delete by ID
    await piglet.dns.delete("example.com", record_id=record_id)

Sync

from oinker import Piglet, ARecord

with Piglet() as piglet:
    pong = piglet.ping()
    print(f"Your IP: {pong.your_ip}")

    records = piglet.dns.list("example.com")

CLI

# Test connection
$ oinker ping
๐Ÿท Oink! Connected successfully.
   Your IP: 203.0.113.42

# List DNS records
$ oinker dns list example.com
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ID     โ”‚ Name            โ”‚ Type โ”‚ Content   โ”‚ TTL โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 123456 โ”‚ example.com     โ”‚ A    โ”‚ 1.2.3.4   โ”‚ 600 โ”‚
โ”‚ 123457 โ”‚ www.example.com โ”‚ A    โ”‚ 1.2.3.4   โ”‚ 600 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”˜

# Create an A record
$ oinker dns create example.com A www 1.2.3.4
๐Ÿท Squeee! Created record 123458

# Delete a record
$ oinker dns delete example.com --id 123458
๐Ÿท Gobbled up record 123458

๐Ÿ“ DNS Record Types

Oinker provides type-safe dataclasses for all Porkbun-supported record types:

from oinker import (
    ARecord,      # IPv4 address
    AAAARecord,   # IPv6 address
    MXRecord,     # Mail server
    TXTRecord,    # Text record
    CNAMERecord,  # Canonical name
    ALIASRecord,  # ALIAS/ANAME
    NSRecord,     # Name server
    SRVRecord,    # Service record
    TLSARecord,   # DANE/TLSA
    CAARecord,    # CA Authorization
    HTTPSRecord,  # HTTPS binding
    SVCBRecord,   # Service binding
    SSHFPRecord,  # SSH fingerprint
)

Records validate their content on construction:

from oinker import ARecord, ValidationError

try:
    ARecord(content="not-an-ip")
except ValidationError as e:
    print(e)  # Invalid IPv4 address: not-an-ip

๐ŸŒ Domain Operations

async with AsyncPiglet() as piglet:
    # List all domains
    domains = await piglet.domains.list()

    # Get/update nameservers
    ns = await piglet.domains.get_nameservers("example.com")
    await piglet.domains.update_nameservers("example.com", [
        "ns1.example.com",
        "ns2.example.com",
    ])

    # URL forwarding
    forwards = await piglet.domains.get_url_forwards("example.com")

    # Check domain availability
    availability = await piglet.domains.check("example.com")

๐Ÿ” DNSSEC

from oinker import DNSSECRecordCreate

async with AsyncPiglet() as piglet:
    # List DNSSEC records
    records = await piglet.dnssec.list("example.com")

    # Create DNSSEC record
    await piglet.dnssec.create("example.com", DNSSECRecordCreate(
        key_tag="64087",
        algorithm="13",
        digest_type="2",
        digest="15E445BD...",
    ))

๐Ÿ”’ SSL Certificates

async with AsyncPiglet() as piglet:
    bundle = await piglet.ssl.retrieve("example.com")
    print(bundle.certificate_chain)
    print(bundle.private_key)

โš ๏ธ Error Handling

from oinker import (
    AsyncPiglet,
    OinkerError,
    AuthenticationError,
    RateLimitError,
    NotFoundError,
)

async with AsyncPiglet() as piglet:
    try:
        await piglet.dns.list("example.com")
    except AuthenticationError:
        print("Check your API credentials")
    except NotFoundError:
        print("Domain not found or API access not enabled")
    except RateLimitError as e:
        print(f"Slow down! Retry after {e.retry_after}s")
    except OinkerError as e:
        print(f"API error: {e}")

๐Ÿ“š Documentation

For more examples and detailed API reference, check out the full documentation.

๐Ÿท Disclaimer

This project is not affiliated with Porkbun in any way. It's just a passion project by someone who really, really loves Porkbun. They're amazing! ๐Ÿฝ

๐Ÿ“„ License

MIT

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

oinker-0.1.2.tar.gz (28.2 kB view details)

Uploaded Source

Built Distribution

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

oinker-0.1.2-py3-none-any.whl (44.3 kB view details)

Uploaded Python 3

File details

Details for the file oinker-0.1.2.tar.gz.

File metadata

  • Download URL: oinker-0.1.2.tar.gz
  • Upload date:
  • Size: 28.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for oinker-0.1.2.tar.gz
Algorithm Hash digest
SHA256 8dd03a11316ceb5e0ce95b838e4f942a28b5e369350f89908866028d21ca4d47
MD5 98a0e33ae614bd850bc4846d708fd7f5
BLAKE2b-256 6f9e42cd284bf5580530960b817b5e3f6acea7b1501c96ba7530667a3d6156f7

See more details on using hashes here.

Provenance

The following attestation bundles were made for oinker-0.1.2.tar.gz:

Publisher: publish.yml on major/oinker

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oinker-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: oinker-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 44.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for oinker-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c7e3aa0bfcb76b928da12776aa36d5beb18c2a8e997cf58da7625d4dc3c503ab
MD5 a547d52bb440fa83bcca395ec44c5243
BLAKE2b-256 408b3db1ae3f4cee3a3afecd20afb565663ce595839d6e903816bf2ba0bf7593

See more details on using hashes here.

Provenance

The following attestation bundles were made for oinker-0.1.2-py3-none-any.whl:

Publisher: publish.yml on major/oinker

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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