Skip to content

Muppy Builtin DNS

muppy.io's DNS setup is unusual. The platform holds the API keys of the real DNS providers — OVH, Cloudflare, Scaleway — and gives its customers DNS domains under them. Handing out the provider keys instead would give away the whole account, every zone included.

Muppy Builtin DNS (MBD) is how a Muppy instance gives DNS domains to others without giving away its keys: it publishes one subdomain over a signed REST API, and the other side configures that subdomain as a normal DNS domain and creates records in it, without ever seeing the backend. The feature is open at two levels:

  • Any Muppy customer can use the MBD Client as an HTTP DNS API — their instance creates records under a subdomain that muppy.io serves.
  • The MBD Server is open too, for a customer who cascades Muppy instances (Muppy Enterprise puts no limit on the number of Muppy servers): their own Muppy publishes subdomains to their downstream instances the same way.

MBD is a pair of DNS Provider values on a DNS Domain: MBD Server on the side that owns the zone, MBD Client on the side that consumes it.

MBD Client  ──HTTPS, AWS SigV4──▶  MBD Server  ──provider API──▶  OVH / Cloudflare
 acme.example.com                  acme.example.com               example.com

An MBD Server publishes one subdomain

Create a DNS Domain named after the subdomain you want to publish, and set its DNS Provider to Muppy Builtin DNS Server - MBD. Fill the MBD Server tab:

Field What it does
Backend DNS Domain the connected domain the records are really written to. Only an ovh or cloudflare domain in state connected is accepted.
AWS SigV4 Token the credential clients sign with. Create Token mints one with its Access Key ID and Secret already set.
Records Quota how many active records this domain accepts, across all its clients. Default 100.
MBD Status Active or Suspended.

The name must be exactly one label under the backend domain — three components in all. With a backend of example.com, acme.example.com is valid and a.b.example.com is not. Records are written to the backend as subdomains of it, and the sync reads them back by rebuilding that name; a deeper name produces records the sync never recognises.

An MBD Server owns no zone of its own. Every create, update and delete is delegated to the backend domain, so the records also show up there.

Connect checks the whole chain before marking the domain connected: the backend is set, is itself connected and answers; the name has the right shape; a SigV4 token is set. It fails loudly, and writes nothing when it fails.

The token is a shared credential, not a per-client one

Whoever holds it may create, update and delete records on every subdomain of the domain, and one token can authorize several MBD Server domains at once. Regenerating its keys silently breaks every client holding the old pair. Issue a separate token per client you do not want to disturb together.

Suspended denies every API request for the domain until it is set back to Active. Records already created are left untouched — clients stop being able to change anything, they do not lose anything.

An MBD Client consumes one

On the consuming instance, create a DNS Domain with the same name as the server's domain and set its DNS Provider to Muppy Builtin DNS Client - MBD. Fill the MBD Client tab:

Field What it does
MBD Server URL scheme and host only, https://…. Muppy appends the API paths itself.
AWS SigV4 Token a local token carrying the Access Key ID and Secret the server operator handed you. Paste them; do not regenerate them.
AWS Region / AWS Service Name signing labels, defaults mpy-eur-1 and muppy-client. Leave them alone unless the server operator asks otherwise.

https only — a single plain-http call burns the token

The first request that reaches an MBD Server over http:// marks the token compromised, permanently. There is no shortcut for a development box: reach a local server through its public https URL.

Connect lists the domain's records. That one call proves three things at once — the signature is valid, the token is authorized on this domain, and the domain is Active and connected on the server. It never raises: the outcome is written to the form — the state badge, and a red banner carrying the server's own error text when it failed. Test Connection to MBD Server runs the same call and reports in a toast, changing nothing.

Connecting also mints the HTTP Basic token a Let's Encrypt client needs and ticks Use Muppy as 'Let's Encrypt' DNS Provider, so the domain is immediately usable for DNS-01 challenges — see Let's Encrypt Certificates (DNS-01).

Credential Expiry

Credential Expiry on the DNS Domain shows what the server reports about the token you signed with, refreshed at every connection check. Unknown means nobody has successfully asked yet — it is deliberately distinct from Perpetual, which is the server explicitly stating the token has no lifetime.

The credential lives on the server, and so does the risk. A client cannot see the token record, cannot renew it and cannot tell how many other domains it also serves. An expiring or regenerated token is repaired on the server side, then the new Access Key ID and Secret are handed over again.

Protocol

Every verb is served on one URL:

https://<mbd-server-host>/mpy/dns/v1/records

Authentication is AWS Signature V4 — the signing scheme only. No AWS service is involved anywhere in MBD. region and service are free-form labels: the server reads both back from the Credential header you sent and recomputes the signature from them, so any consistent pair works. Sign with the Access Key ID and Secret the server operator issued.

Authorization is per token and domain. A signature that verifies still gets Access denied for a domain the token is not bound to.

Verb Body / query Does
POST JSON body: domain, name, record_type, value, ttl (default 3600) creates a record
GET query string: domain, optional record_type, optional name lists the active records
PUT JSON body: domain, name, record_type, and target and/or ttl updates an existing record
DELETE JSON body: domain, name, record_type deletes a record

name is the subdomain part alone, empty for the domain itself. POST names the record value value, PUT names it target — they are the same thing under two keys.

Creating a record, with the SigV4 headers omitted:

{
  "domain": "acme.example.com",
  "name": "www",
  "record_type": "A",
  "value": "203.0.113.10",
  "ttl": 3600
}

A listing answers:

{
  "status": "success",
  "records": [
    {
      "id": 42,
      "sub_domain": "www",
      "fqdn": "www.acme.example.com",
      "record_type": "A",
      "target": "203.0.113.10",
      "ttl": 3600,
      "description": ""
    }
  ],
  "count": 1,
  "quota_used": 1,
  "quota_max": 100,
  "credential": {
    "access_key": "MBD Server - acme.example.com/id=7",
    "lifetime": "limited",
    "expires": "2027-01-31T09:00:00Z"
  }
}

Every failure answers with the same two keys, whatever the verb:

{
  "status": "error",
  "error": "Access denied to domain 'acme.example.com'"
}

GET carries the failure in the HTTP status too — 400 for a refusal, 500 for an internal error. The other three verbs always answer 200 and put the outcome in status, so read status rather than the status code.

Compatibility

A field absent from a response means the server does not report it — never that the value is empty. MBD servers differ in age, and reading silence as a value turns an older server into a false statement.

For credential specifically:

What you get What it means
no credential object the server does not report credential lifetimes. Unknown.
credential with "expires": null and "lifetime": "perpetual" the server states the token has no expiry.
credential with an ISO 8601 expires the token expires then. Compute soon / expired yourself.

Signed examples with your own keys

The MBD Client tab generates ready-to-run curl and Python examples for all four verbs, signed with the credentials that domain actually holds. Tick Show API Examples with Real Credentials to reveal them, alongside the environment variables for a shell client. Use those rather than transcribing the shapes above — they carry your host, your domain and your keys.