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
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
Pigletwhen 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file oinker-0.1.1.tar.gz.
File metadata
- Download URL: oinker-0.1.1.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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6a87ee4727d7d097b3c48dfe01fb5f99588504a11c04c0367c08d7894bb6788
|
|
| MD5 |
88bec4cdd2b47020b4517f45222b3412
|
|
| BLAKE2b-256 |
9b1b3da3484b657c9f4855190897a2993be0840e96e0e7c20d08c0443de016ca
|
Provenance
The following attestation bundles were made for oinker-0.1.1.tar.gz:
Publisher:
publish.yml on major/oinker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oinker-0.1.1.tar.gz -
Subject digest:
c6a87ee4727d7d097b3c48dfe01fb5f99588504a11c04c0367c08d7894bb6788 - Sigstore transparency entry: 832898826
- Sigstore integration time:
-
Permalink:
major/oinker@e4f98292a0779d533765315c5385733b93db463f -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/major
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e4f98292a0779d533765315c5385733b93db463f -
Trigger Event:
release
-
Statement type:
File details
Details for the file oinker-0.1.1-py3-none-any.whl.
File metadata
- Download URL: oinker-0.1.1-py3-none-any.whl
- Upload date:
- Size: 44.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d81ceb8e60c8c50270e3a985538a9cb139e19a4172d53149bf83979bb8666b24
|
|
| MD5 |
9d8ebf4f2bf0f4ec09e41753195f087e
|
|
| BLAKE2b-256 |
d0e5f93ba3173ca297900c3be7f1cf9011ebe21bb1003c494292ddfcee008e2a
|
Provenance
The following attestation bundles were made for oinker-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on major/oinker
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oinker-0.1.1-py3-none-any.whl -
Subject digest:
d81ceb8e60c8c50270e3a985538a9cb139e19a4172d53149bf83979bb8666b24 - Sigstore transparency entry: 832898828
- Sigstore integration time:
-
Permalink:
major/oinker@e4f98292a0779d533765315c5385733b93db463f -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/major
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e4f98292a0779d533765315c5385733b93db463f -
Trigger Event:
release
-
Statement type: