# ol.protocol53 next One Clojure API for managing DNS records across providers ## ol.protocol53.cloudflare # ol.protocol53.cloudflare Cloudflare DNS provider for protocol53. ## ->Provider ```clojure (->Provider api-token zone-token http-client) ``` [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/providers/cloudflare/src/main/ol/protocol53/cloudflare.clj#L643-L678) --- ## Provider [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/providers/cloudflare/src/main/ol/protocol53/cloudflare.clj#L643-L678) --- ## map->Provider ```clojure (map->Provider m) ``` [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/providers/cloudflare/src/main/ol/protocol53/cloudflare.clj#L643-L678) --- ## provider ```clojure (provider config) ``` Returns a Cloudflare provider from `config`. Options: | | | | --- | --- | | key | description | | `:api-token` | Scoped token used for DNS record requests. | | `:zone-token` | Optional scoped token used for zone reads. | | `:http-client` | Optional `java.net.http.HttpClient` for advanced HTTP policy. | [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/providers/cloudflare/src/main/ol/protocol53/cloudflare.clj#L680-L692) ## ol.protocol53.deadline # ol.protocol53.deadline Time budgets for protocol53 operations. A deadline is a fixed point in the future that answers one question: has an operation run out of time? Callers usually pass a positive `java.time.Duration` under `:timeout`; the protocol53 facade converts it to the `:deadline` in normalized provider options. Callers may instead create a deadline when several operations must share one budget. Deadlines are monotonic. They ride on `System/nanoTime`, so clock changes — NTP corrections, daylight saving, someone setting the system clock — cannot move them. That makes them reliable for timeouts and meaningless for anything else: a deadline is just a number that only makes sense inside the process that created it. Do not serialize it, store it, share it across processes, or compare it against wall-clock time. Build one with [`after`](#after). Check it with [`remaining`](#remaining), [`expired?`](#expired?), and [`within?`](#within?). ## after ```clojure (after duration) ``` Returns a deadline that falls `duration` from now. `duration` is a positive `java.time.Duration`; it must be shorter than `Long/MAX_VALUE` nanoseconds, roughly 292 years. ```clojure (deadline/after (java.time.Duration/ofSeconds 30)) ``` [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/deadline.clj#L42-L53) --- ## remaining ```clojure (remaining operation-deadline) ``` Returns how much time is left before `operation-deadline`. The value is a `java.time.Duration` that never goes negative; once the deadline has passed it is `Duration/ZERO`. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/deadline.clj#L55-L67) --- ## expired? ```clojure (expired? operation-deadline) ``` Returns `true` once `operation-deadline` has no time left. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/deadline.clj#L69-L72) --- ## within? ```clojure (within? operation-deadline duration) ``` Returns `true` when `duration` still fits before `operation-deadline`. Use it to decide whether to start work that needs at least `duration` of budget. `duration` is a positive `java.time.Duration`. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/deadline.clj#L74-L81) ## ol.protocol53.desec # ol.protocol53.desec deSEC DNS provider for protocol53. ## ->Provider ```clojure (->Provider token http-client) ``` [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/providers/desec/src/main/ol/protocol53/desec.clj#L545-L580) --- ## Provider [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/providers/desec/src/main/ol/protocol53/desec.clj#L545-L580) --- ## map->Provider ```clojure (map->Provider m) ``` [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/providers/desec/src/main/ol/protocol53/desec.clj#L545-L580) --- ## provider ```clojure (provider config) ``` Returns a deSEC provider from `config`. Options: | key | description | -------------- | ----------- | `:token` | deSEC API token used for domain and RRset requests. | `:http-client` | Optional `java.net.http.HttpClient` for advanced HTTP policy. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/providers/desec/src/main/ol/protocol53/desec.clj#L582-L593) ## ol.protocol53.http # ol.protocol53.http Synchronous HTTP transport for bundled protocol53 providers. This namespace is an internal provider utility, not a general-purpose HTTP client. Loading it requires Java 11 or newer. Advanced client policy remains available through a caller-supplied `java.net.http.HttpClient`. ## request ```clojure (request request) ``` Sends one synchronous HTTP request and returns its response. Options: | key | description | ----------------|------------ | `:uri` | Required URI string or component map. | `:method` | HTTP method keyword or string (default `:get`). | `:headers` | Map of header names to string or sequential values. | `:query-params` | Map of UTF-8 form-encoded query parameters. | `:form-params` | Map encoded as the request body. | `:body` | String, `java.io.File`, or `java.io.InputStream`. | `:as` | `:string` (default) or `:stream`. | `:timeout` | Request timeout in milliseconds. | `:throw` | Throw for exceptional statuses unless `false`. | `:version` | `:http1.1` or `:http2` (default `:http2`). | `:client` | `java.net.http.HttpClient` or request function. Returns `:status`, vector-valued `:headers`, `:body`, and the negotiated `:version`. The body is a string by default. With `:as :stream`, it is a `java.io.InputStream` available after the response headers; the caller must exhaust or close it. Streaming callers should normally use `:throw false` to retain ownership of the stream for every status. Exceptional statuses throw `ExceptionInfo` with the complete response as `ex-data`. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/http.clj#L200-L231) ## ol.protocol53.malli # ol.protocol53.malli Optional Malli schemas for protocol53 inputs and outcomes. This namespace requires consumers to supply Malli. Core protocol53 namespaces do not load it. ## record Schema for a complete portable record. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L25-L32) --- ## record-selector Schema for a delete selector with wildcard fields. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L34-L41) --- ## records Schema for a vector of complete records. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L43-L45) --- ## record-selectors Schema for a vector of delete selectors. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L47-L49) --- ## zone Schema for one zone. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L51-L53) --- ## zones Schema for a vector of zones. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L55-L57) --- ## opts Describes open caller options with exactly one time budget. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L59-L66) --- ## provider-opts Describes open normalized provider options with only `:deadline`. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L68-L73) --- ## record-result-data Schema for the inner result of a record operation. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L81-L83) --- ## zone-result-data Schema for the inner result of zone listing. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L85-L87) --- ## error-data Schema for inner error data. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L89-L105) --- ## record-success Schema for a successful record-operation outcome. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L113-L116) --- ## zone-success Schema for a successful zone-listing outcome. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L118-L121) --- ## error-outcome Schema for an error outcome. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L123-L126) --- ## record-outcome Schema for exactly one record result or error. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L128-L133) --- ## zone-outcome Schema for exactly one zone result or error. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L135-L140) --- ## get-records-fn Function schema for `ol.protocol53/get-records!`. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L142-L145) --- ## append-records-fn Function schema for `ol.protocol53/append-records!`. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L147-L151) --- ## set-records-fn Function schema for `ol.protocol53/set-records!`. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L153-L155) --- ## delete-records-fn Function schema for `ol.protocol53/delete-records!`. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L157-L161) --- ## list-zones-fn Function schema for `ol.protocol53/list-zones!`. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/malli.clj#L163-L166) ## ol.protocol53.porkbun # ol.protocol53.porkbun Porkbun DNS provider for protocol53. ## ->Provider ```clojure (->Provider api-key secret-key http-client) ``` [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/providers/porkbun/src/main/ol/protocol53/porkbun.clj#L707-L742) --- ## Provider [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/providers/porkbun/src/main/ol/protocol53/porkbun.clj#L707-L742) --- ## map->Provider ```clojure (map->Provider m) ``` [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/providers/porkbun/src/main/ol/protocol53/porkbun.clj#L707-L742) --- ## provider ```clojure (provider config) ``` Returns a Porkbun provider from `config`. Options: | | | | --- | --- | | key | description | | `:api-key` | Porkbun API key. | | `:secret-key` | Porkbun secret API key. | | `:http-client` | Optional `java.net.http.HttpClient` for advanced HTTP policy. | [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/providers/porkbun/src/main/ol/protocol53/porkbun.clj#L756-L768) ## ol.protocol53.protocols # ol.protocol53.protocols Provider capability extension points. ## RecordGetter Retrieves all records in one zone. _protocol_ [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/protocols.clj#L4-L6) ### -get-records! ```clojure (-get-records! provider zone opts) ``` --- ## RecordAppender Appends records without modifying existing records. _protocol_ [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/protocols.clj#L8-L10) ### -append-records! ```clojure (-append-records! provider zone records opts) ``` --- ## RecordSetter Replaces the selected complete RRsets. _protocol_ [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/protocols.clj#L12-L14) ### -set-records! ```clojure (-set-records! provider zone records opts) ``` --- ## RecordDeleter Deletes matching records and ignores misses. _protocol_ [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/protocols.clj#L16-L18) ### -delete-records! ```clojure (-delete-records! provider zone records opts) ``` --- ## ZoneLister Lists zones supported by record operations. _protocol_ [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53/protocols.clj#L20-L22) ### -list-zones! ```clojure (-list-zones! provider opts) ``` ## ol.protocol53.specs # ol.protocol53.specs Specifications for protocol53 inputs and outcomes. ## ol.protocol53.testkit.dotenv # ol.protocol53.testkit.dotenv Minimal `.env` file loading for development lifecycle suites. ## load-env-file ```clojure (load-env-file filename) ``` Loads assignments from `filename` into a string map. Returns an empty map when `filename` does not name an existing file. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/testkit/src/main/ol/protocol53/testkit/dotenv.clj#L36-L43) ## ol.protocol53.testkit # ol.protocol53.testkit Provider-agnostic lifecycle checks for protocol53 implementations. This suite creates, replaces, and deletes DNS records. Run it only against a dedicated disposable test zone. Generated owners use the reserved `p53test-` prefix so interrupted runs can be cleaned safely. ## cleanup! ```clojure (cleanup! config) ``` Removes reserved lifecycle records and verifies their absence. Use this after an interrupted run. The function changes DNS and rejects the call unless `:allow-live-changes?` is exactly `true`. Options: | | | | --- | --- | | key | description | | `:provider` | Provider implementing all four record capabilities (required). | | `:zone` | Dedicated absolute zone name ending in `.` (required). | | `:allow-live-changes?` | Destructive-operation acknowledgement; must be `true` (required). | | `:timeout` | Positive `java.time.Duration` for each call (default 30 seconds). | | `:skip-record-types` | Set of nonessential uppercase or lowercase types (default `#{}`). | | `:skip-empty-txt?` | Omit the exact-empty TXT fixture when unsupported (default `false`). | [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/testkit/src/main/ol/protocol53/testkit.clj#L256-L273) --- ## run! ```clojure (run! config) ``` Runs the provider lifecycle suite against a dedicated DNS zone. The suite validates configuration, cleans stale `p53test-` records, and then runs list, get, append, set, delete, wildcard-delete, and final cleanup checks sequentially. It emits `clojure.test` assertions in the active test. Options: | | | | --- | --- | | key | description | | `:provider` | Provider implementing all four record capabilities (required). | | `:zone` | Dedicated absolute zone name ending in `.` (required). | | `:allow-live-changes?` | Destructive-operation acknowledgement; must be `true` (required). | | `:timeout` | Positive `java.time.Duration` for each call (default 30 seconds). | | `:skip-record-types` | Set of nonessential uppercase or lowercase types (default `#{}`). | | `:skip-empty-txt?` | Omit the exact-empty TXT fixture when unsupported (default `false`). | | `:ignore-ttl?` | Ignore TTL differences in lifecycle checks (default `false`). | | `:expect-empty-zone?` | Reject remaining active non-`NS` records (default `false`). | [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/testkit/src/main/ol/protocol53/testkit.clj#L444-L477) ## ol.protocol53 # ol.protocol53 The DNS operations you call independent of any one provider. protocol53 gives you a small, fixed set of operations — [`get-records!`](#get-records!), [`append-records!`](#append-records!), [`set-records!`](#set-records!), [`delete-records!`](#delete-records!), and [`list-zones!`](#list-zones!) — that behave the same behind every supported DNS provider. Write against these functions once and switch providers, such as Cloudflare, without changing your call sites. A provider is any value that implements the capabilities in [`ol.protocol53.protocols`](api/ol-protocol53-protocols.adoc). You build one from its own namespace, for example `ol.protocol53.cloudflare/provider`, and pass it as the first argument to every operation. Each operation takes trailing `opts` with exactly one time budget. A positive `java.time.Duration` under `:timeout` starts a fresh budget for the call. A process-local `:deadline`, created with [`ol.protocol53.deadline/after`](api/ol-protocol53-deadline.adoc#after), lets several calls share one budget. The facade normalizes both forms to a deadline before provider dispatch. Operations do not throw on expected failures. Each returns a map with exactly one of these keys: * `:ol.protocol53/result` on success, holding the records or zones produced. * `:ol.protocol53/error` on failure, describing what went wrong and whether a retry might succeed. So a typical call checks for `:ol.protocol53/error` before using the result. ## get-records! ```clojure (get-records! provider zone opts) ``` Reads every record in `zone` and returns them in the result. Read-only; it never changes the zone. On success the result holds a vector of records under `:records`. Supply exactly one time-budget option. Options: | key | description | ----------- | ----------- | `:timeout` | Positive `java.time.Duration` starting a fresh budget. | `:deadline` | Process-local deadline for sharing an existing budget. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53.clj#L85-L102) --- ## append-records! ```clojure (append-records! provider zone records opts) ``` Adds `records` to `zone`, leaving every existing record in place. Use this to create records without disturbing what is already there. On success the result lists the records as the provider stored them. Supply exactly one time-budget option. Options: | key | description | ----------- | ----------- | `:timeout` | Positive `java.time.Duration` starting a fresh budget. | `:deadline` | Process-local deadline for sharing an existing budget. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53.clj#L104-L131) --- ## set-records! ```clojure (set-records! provider zone records opts) ``` Replaces whole RRsets in `zone` with `records`. For each `name`/`type` pair present in `records`, this removes every existing record of that name and type and installs the ones you gave. Names and types you do not mention are left untouched. Use it to declare the exact desired state of the RRsets you care about. Supply exactly one time-budget option. Options: | key | description | ----------- | ----------- | `:timeout` | Positive `java.time.Duration` starting a fresh budget. | `:deadline` | Process-local deadline for sharing an existing budget. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53.clj#L133-L161) --- ## delete-records! ```clojure (delete-records! provider zone records opts) ``` Removes records in `zone` that match `records` and ignores the rest. Each entry in `records` is a selector: it must name a record and may narrow the match by `type`, `ttl`, or `data`. A selector that matches nothing is not an error. On success the result lists the records that were actually removed. Supply exactly one time-budget option. Options: | key | description | ----------- | ----------- | `:timeout` | Positive `java.time.Duration` starting a fresh budget. | `:deadline` | Process-local deadline for sharing an existing budget. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53.clj#L163-L192) --- ## list-zones! ```clojure (list-zones! provider opts) ``` Lists the zones `provider` can operate on. On success the result holds a vector of zone maps under `:zones`. Use it to discover which zones the other operations may target. Supply exactly one time-budget option. Options: | key | description | ----------- | ----------- | `:timeout` | Positive `java.time.Duration` starting a fresh budget. | `:deadline` | Process-local deadline for sharing an existing budget. [source,window=_blank](https://github.com/outskirtslabs/protocol53/blob/main/api/src/main/ol/protocol53.clj#L194-L210) ## ol.protocol53 # ol.protocol53 > One Clojure API for managing DNS records across providers. ![doc](https://img.shields.io/badge/doc-outskirtslabs-orange.svg) ![status: experimental](https://img.shields.io/badge/status-experimental-red.svg) ![alt=nixbot](https://ci.outskirtslabs.com/repos/github/outskirtslabs/protocol53/badge.svg) `ol.protocol53` gives Clojure applications one provider-independent API for querying and changing DNS records. Applications can switch DNS services without changing their record-management code. The shared API lives in `api/`. Provider integrations live in `providers/` and translate the common operations into each service’s API. Protocol53 supports: * retrieving records from a zone * adding records * creating or replacing whole RRsets * removing matching records * listing available zones Provider capabilities and limitations vary, but applications use the same core abstractions across every supported integration. Project status: **[Experimental](https://docs.outskirtslabs.com/open-source-vital-signs#experimental)**. ## Available Providers * [Cloudflare](https://docs.outskirtslabs.com/ol.protocol53/next/providers/cloudflare) * [deSEC](https://docs.outskirtslabs.com/ol.protocol53/next/providers/desec) * [Porkbun](https://docs.outskirtslabs.com/ol.protocol53/next/providers/porkbun) ## Installation Until the first release, use the API’s Git coordinate: ```clojure {:deps {com.outskirtslabs/protocol53 {:git/url "https://github.com/outskirtslabs/protocol53.git" :git/sha "4a0ad834256d7e1e1a6a95936ea5e7ca14f13988" :deps/root "api"}}} ``` ## Quick Start Create a provider, then pass it to the common operations with a time budget: ```clojure (require '[ol.protocol53 :as protocol53] '[ol.protocol53.cloudflare :as cloudflare]) (import '[java.time Duration]) (def dns (cloudflare/provider {:api-token (System/getenv "CLOUDFLARE_API_TOKEN")})) (protocol53/append-records! dns "example.com" [{:name "_acme-challenge" :type "TXT" :ttl 60 :data "challenge-token"}] {:timeout (Duration/ofSeconds 10)}) ``` Operations return either `:ol.protocol53/result` or `:ol.protocol53/error`; expected provider failures do not throw. ## Documentation * [Docs](https://docs.outskirtslabs.com/ol.protocol53/next/) * [API Reference](https://docs.outskirtslabs.com/ol.protocol53/next/api) * [Support via GitHub Issues](https://github.com/outskirtslabs/protocol53/issues) ## Scope `ol.protocol53` targets common DNS record operations rather than every provider-specific feature. DNS record types and provider APIs vary too much for a universal abstraction, so Protocol53 focuses on the operations applications use across providers. ## Related Projects [`ol.clave`](https://docs.outskirtslabs.com/ol.clave/next/) is Outskirts Labs' ACME client for automated HTTPS certificate management. ACME DNS-01 validation needs temporary TXT records, which Protocol53 can manage across its supported DNS providers. A direct Clave adapter remains future work. * [Clave documentation](https://docs.outskirtslabs.com/ol.clave/next/) * [Clave source code](https://github.com/outskirtslabs/clave) ## License Copyright (C) 2026 Casey Link mailto:casey@outskirtslabs.com[casey@outskirtslabs.com] Distributed under the [MIT](https://spdx.org/licenses/MIT.html) license. ## Cloudflare provider # Cloudflare provider The [Cloudflare](https://cloudflare.com) provider implements all [Protocol53](https://github.com/outskirtslabs/protocol53) operations with Cloudflare’s v4 API. ## Installation Until the first release, use the provider’s Git coordinate: ```clojure {:deps {com.outskirtslabs/protocol53-cloudflare {:git/url "https://github.com/outskirtslabs/protocol53.git" :git/sha "4a0ad834256d7e1e1a6a95936ea5e7ca14f13988" :deps/root "providers/cloudflare"}}} ``` The provider module also brings in the shared Protocol53 API. ## Authentication Use scoped API tokens, not legacy global API keys. For one token, grant `Zone:Read` and `Zone.DNS:Write` across the zones you manage: ```clojure (require '[ol.protocol53.cloudflare :as cloudflare]) (def dns (cloudflare/provider {:api-token "token"})) ``` For separate read and write credentials, grant `Zone:Read` to `:zone-token` and `Zone.DNS:Write` to `:api-token`: ```clojure (def dns (cloudflare/provider {:api-token "dns-write-token" :zone-token "zone-read-token"})) ``` Pass a caller-built `java.net.http.HttpClient` as `:http-client` when advanced HTTP policy requires Java interop. ## Integration Tests
⚠️ WARNING
Run the lifecycle suite only against a dedicated disposable zone. It creates, replaces, and deletes DNS records. A provider or testkit defect could affect records outside the reserved names.
Set these variables in the process environment or the repository `.env` file. Process values take precedence over `.env` values. | | | | | --- | --- | --- | | Variable | Required | Description | | `CLOUDFLARE_API_TOKEN` | yes | Scoped token with DNS write access. | | `CLOUDFLARE_TEST_ZONE` | one of | Dedicated absolute zone name. | | `CLOUDFLARE_DOMAIN` | one of | Alternative zone name; a trailing dot is added when absent. | | `CLOUDFLARE_ZONE_TOKEN` | no | Separate scoped token with zone read access. | `CLOUDFLARE_ZONE_ID` is not required because the provider discovers the zone by name. The wrapper sets `:skip-empty-txt? true` because Cloudflare rejects empty TXT RDATA; it still runs ordinary and wildcard TXT lifecycle checks. Run every discovered provider integration suite from the repository root: ```shell bb test:integration ``` The runner discovers `providers/*/src/test-integration`. Cloudflare is skipped when its credentials are missing; another provider failure does not stop the remaining suites. Cloudflare creates and deletes records whose effective owner starts with `p53test-`. Integration tests do not run from `bb test`, `bb qa`, or `bb ci`. Do not run concurrent lifecycle suites against one zone. ## deSEC provider # deSEC provider The [deSEC](https://desec.io) provider implements all [Protocol53](https://github.com/outskirtslabs/protocol53) operations with deSEC’s v1 API. ## Installation Until the first release, use the provider’s Git coordinate: ```clojure {:deps {com.outskirtslabs/protocol53-desec {:git/url "https://github.com/outskirtslabs/protocol53.git" :git/sha "4a0ad834256d7e1e1a6a95936ea5e7ca14f13988" :deps/root "providers/desec"}}} ``` The provider module also brings in the shared Protocol53 API. ## Authentication Create a basic token through https://desec.io/tokens[deSEC’s token management interface]. ```clojure (require '[ol.protocol53.desec :as desec]) (def dns (desec/provider {:token "token"})) ``` Pass a caller-built `java.net.http.HttpClient` as `:http-client` when advanced HTTP policy requires Java interop. ## TTL Policy A deSEC RRset cannot have a TTL below 3600 seconds. The provider always clamps lower requested values to 3600 and returns the value deSEC stored. TTL belongs to the complete RRset, so all records with the same owner and type share one TTL. When setting an RRset whose input records disagree, the first record’s TTL is used. ## RRset and Rate-Limit Behavior Append and delete operations read the zone before applying one atomic bulk write. As with other Protocol53 providers, callers must coordinate concurrent mutations of the same RRset. HTTP 429 responses are retried according to deSEC’s `Retry-After` header while the operation deadline permits. ## Integration Tests
⚠️ WARNING
Run the lifecycle suite only against a dedicated disposable zone. It creates, replaces, and deletes DNS records whose effective owner starts with `p53test-`.
Set these variables in the process environment or the repository `.env` file. Process values take precedence over `.env` values. | | | | | --- | --- | --- | | Variable | Required | Description | | `DESEC_TOKEN` | yes | Basic deSEC token with access to the test domain. | | `DESEC_DOMAIN` | yes | Dedicated zone name, with or without a trailing dot. | Run every configured provider integration suite from the repository root: ```shell bb test:integration ``` The deSEC wrapper ignores TTL differences in the shared lifecycle comparison because all fixture TTLs below 3600 are clamped. Provider-specific tests still assert the exact 3600-second policy. Do not run concurrent lifecycle suites against one zone. ## Porkbun provider # Porkbun provider The [Porkbun](https://porkbun.com) provider implements all [Protocol53](https://github.com/outskirtslabs/protocol53) operations with Porkbun’s v3 API. ## Installation Until the first release, use the provider’s Git coordinate: ```clojure {:deps {com.outskirtslabs/protocol53-porkbun {:git/url "https://github.com/outskirtslabs/protocol53.git" :git/sha "4a0ad834256d7e1e1a6a95936ea5e7ca14f13988" :deps/root "providers/porkbun"}}} ``` The provider module also brings in the shared Protocol53 API. ## Authentication Enable API access for the Porkbun account and each domain the provider should manage, then create an API key and secret key. ```clojure (require '[ol.protocol53.porkbun :as porkbun]) (def dns (porkbun/provider {:api-key "pk1_..." :secret-key "sk1_..."})) ``` Pass a caller-built `java.net.http.HttpClient` as `:http-client` when advanced HTTP policy requires Java interop. ## Record Behavior Porkbun derives its minimum record TTL from account settings; 600 seconds is the typical floor. The provider raises lower requests to 600, then rereads the zone after append and set operations so it returns the TTL Porkbun actually stored. MX and SRV priorities are translated between Protocol53’s portable record data and Porkbun’s separate `prio` field. ## Integration Tests
⚠️ WARNING
Run the lifecycle suite only against a dedicated disposable zone. It creates, replaces, and deletes DNS records whose effective owner starts with `p53test-`.
Set these variables in the process environment or the repository `.env` file. Process values take precedence over `.env` values. | | | | | --- | --- | --- | | Variable | Required | Description | | `PORKBUN_API_KEY` | yes | Porkbun API key with access to the test domain. | | `PORKBUN_SECRET_KEY` | yes | Secret key paired with the API key. | | `PORKBUN_DOMAIN` | yes | Dedicated zone, with or without a trailing dot. | Run the Porkbun lifecycle suite from the repository root: ```shell bb test:integration --provider porkbun ``` The wrapper ignores TTL differences because Porkbun applies the test account’s minimum to fixture TTLs. Do not run concurrent lifecycle suites against one zone.