Skip to main content

Migadu

@shrug/migaduv2026.10.03.3· MIT· 11d agoBEHAVIORSACCEPTED WARNINGS
01README

Migadu mailbox-admin REST API (v1) client for swamp. The third mail primitive alongside @shrug/imap (messages/folders) and @shrug/managesieve (server-side filtering): it fills the MAILBOX ADMIN gap -- declarative provisioning and lifecycle of mailboxes, send-as identities, forwardings, domain aliases, and address rewrites -- over https://api.migadu.com/v1/ (HTTP Basic auth; admin-account email + API key, both vault-resolved). Full CRUD across every surface.

Read methods

listDomains, getDomain (+ getDomainRecords / getDomainDiagnostics / getDomainUsage), listMailboxes / getMailbox, listAliases / getAlias, listRewrites / getRewrite, listIdentities / getIdentity, listForwardings / getForwarding. Each fans out over a configured OR self-discovered domain/mailbox selection in a single call (repo rule 6) and writes versioned snapshot resources.

Write methods (gated)

Every write defaults dryRun=true: it emits a wouldAffect preview with zero mutation. dryRun=false is a two-pass apply -- pass 1 GETs every target and, if any delete/update target is absent, aborts the WHOLE batch before a single mutation (verify-before-destructive, repo rule 5); pass 2 issues one write per changed item and re-GETs to record verified evidence. Every write is idempotent (unchanged state sends zero requests). The mailbox/identity password is redacted from every recorded/returned snapshot on both the read and write paths (and from surfaced HTTP error detail); the plaintext only ever reaches the outbound request body.

UPDATE semantics: mailbox / identity / alias / forwarding is PUT = full replace (read-modify-write preserves unmentioned live fields); domain is PATCH = partial. createDomain is POST-only; activateDomain is a gated, idempotent state change. There is deliberately NO deleteDomain -- Migadu exposes no domain-delete endpoint.

Consolidation

reconcileForwardings reads a declarative desiredForwardings map from a tracked-YAML global argument (repo rule 12), diffs it against a live snapshot, and fans out only the missing upserts -- it never deletes and never mutates on dryRun; an extra live forwarding absent from the desired set is reported for a human to review, not auto-removed.

02Behaviors1
@shrug/migaduv2026.10.03.3extensions/models/migadu.ts
fn listDomains()
List every domain the credential can see (GET /domains). The
fn getDomain(name: string)
Fetch one domain (GET /domains/{name}). Read-only.
ArgumentTypeDescription
namestring
fn getDomainRecords(name: string)
Fetch a domain's DNS/MX records (GET /domains/{name}/records).
ArgumentTypeDescription
namestring
fn getDomainDiagnostics(name: string)
Fetch a domain's diagnostics (GET /domains/{name}/diagnostics).
ArgumentTypeDescription
namestring
fn getDomainUsage(name: string)
Fetch a domain's usage (GET /domains/{name}/usage). Read-only.
ArgumentTypeDescription
namestring
fn createDomain(items: array, dryRun?: boolean)
Fan out: create every domain in `items` (POST /domains).
ArgumentTypeDescription
itemsarray
dryRun?boolean
fn updateDomain(items: array, dryRun?: boolean)
Fan out: update every domain in `items`
ArgumentTypeDescription
itemsarray
dryRun?boolean
fn activateDomain(items: array, dryRun?: boolean)
Fan out: activate every domain in `items`
ArgumentTypeDescription
itemsarray
dryRun?boolean
fn listMailboxes()
Fan out over every configured (or self-discovered) domain and list
fn getMailbox(domain: string, localPart: string)
Fetch one mailbox (GET /domains/{domain}/mailboxes/{localPart}). Read-only.
ArgumentTypeDescription
domainstring
localPartstring
fn listAliases()
Fan out over every configured (or self-discovered) domain and list
fn listRewrites()
Fan out over every configured (or self-discovered) domain and list
fn listIdentities(domain: string, localPart: string)
Fan out over an explicit {domain, localPart}[] mailbox selection
ArgumentTypeDescription
domainstring
localPartstring
fn listForwardings(domain: string, localPart: string)
Fan out over an explicit {domain, localPart}[] mailbox selection
ArgumentTypeDescription
domainstring
localPartstring
fn getIdentity(domain: string, mailboxLocalPart: string, identityLocalPart: string)
Fetch one send-as identity
ArgumentTypeDescription
domainstring
mailboxLocalPartstring
identityLocalPartstring
fn getAlias(domain: string, localPart: string)
Fetch one domain alias (GET /domains/{domain}/aliases/{localPart}).
ArgumentTypeDescription
domainstring
localPartstringThe alias's local part
fn getForwarding(domain: string, localPart: string, address: string)
Fetch one forwarding
ArgumentTypeDescription
domainstring
localPartstring
addressstringThe forwarding's target address
fn getRewrite(domain: string, name: string)
Fetch one rewrite rule (GET /domains/{domain}/rewrites/{name}).
ArgumentTypeDescription
domainstring
namestringThe rewrite rule's name
fn upsertMailbox(items: array, dryRun?: boolean)
Fan out: create-or-update every mailbox in `items`
ArgumentTypeDescription
itemsarray
dryRun?boolean
fn deleteMailbox(items: array, dryRun?: boolean)
Fan out: delete every mailbox in `items`
ArgumentTypeDescription
itemsarray
dryRun?boolean
fn upsertIdentity(items: array, dryRun?: boolean)
Fan out: create-or-update every send-as identity in `items`
ArgumentTypeDescription
itemsarray
dryRun?boolean
fn deleteIdentity(items: array, dryRun?: boolean)
Fan out: delete every send-as identity in `items`
ArgumentTypeDescription
itemsarray
dryRun?boolean
fn upsertAlias(items: array, dryRun?: boolean)
Fan out: create-or-update every domain alias in `items`
ArgumentTypeDescription
itemsarray
dryRun?boolean
fn deleteAlias(items: array, dryRun?: boolean)
Fan out: delete every domain alias in `items`
ArgumentTypeDescription
itemsarray
dryRun?boolean
fn upsertForwarding(items: array, dryRun?: boolean)
Fan out: create-or-update every forwarding in `items`
ArgumentTypeDescription
itemsarray
dryRun?boolean
fn deleteForwarding(items: array, dryRun?: boolean)
Fan out: delete every forwarding in `items`
ArgumentTypeDescription
itemsarray
dryRun?boolean
fn upsertRewrite(items: array, dryRun?: boolean)
Fan out: create-or-update every rewrite rule in `items`
ArgumentTypeDescription
itemsarray
dryRun?boolean
fn deleteRewrite(items: array, dryRun?: boolean)
Fan out: delete every rewrite rule in `items`
ArgumentTypeDescription
itemsarray
dryRun?boolean
fn reconcileForwardings(dryRun?: boolean)
Read the declarative `desiredForwardings` global argument
ArgumentTypeDescription
dryRun?boolean

Resources

domains(infinite)— The account's domain index (GET /domains)
domain(infinite)— One domain (GET /domains/{name})
domainRecords(infinite)— A domain's DNS/MX records (GET /domains/{name}/records)
domainDiagnostics(infinite)— A domain's diagnostics (GET /domains/{name}/diagnostics)
domainUsage(infinite)— A domain's usage (GET /domains/{name}/usage)
mailboxes(infinite)— A domain's mailboxes (GET /domains/{domain}/mailboxes)
mailbox(infinite)— One mailbox (GET /domains/{domain}/mailboxes/{localPart})
identity(infinite)— One send-as identity (GET .../mailboxes/{localPart}/identities/{identityLocalPart})
alias(infinite)— One domain alias (GET /domains/{domain}/aliases/{localPart})
forwarding(infinite)— One forwarding (GET .../mailboxes/{localPart}/forwardings/{address})
rewrite(infinite)— One rewrite rule (GET /domains/{domain}/rewrites/{name})
identities(infinite)— One mailbox's send-as identities (GET .../mailboxes/{localPart}/identities)
forwardings(infinite)— One mailbox's forwardings (GET .../mailboxes/{localPart}/forwardings)
aliases(infinite)— A domain's aliases (GET /domains/{domain}/aliases)
rewrites(infinite)— A domain's rewrite rules (GET /domains/{domain}/rewrites)
wouldAffect(infinite)— A dryRun=true preview of a gated write method's effect; NO mutation performed
applied(infinite)— The result of a gated write method executed with dryRun=false
reconcilePreview(infinite)— A dryRun=true diff of desiredForwardings vs. live forwardings; NO mutation performed
reconcileApplied(infinite)— The result of reconcileForwardings executed with dryRun=false
03Stats
A
100 / 100
Downloads
9
Archive size
47.9 KB
  • Has README or module doc2/2earned
  • README has a code example1/1earned
  • README is substantive1/1earned
  • Most symbols documented1/1earned
  • No slow types (deprecated)1/1earned
  • Dependencies pass trust audit2/2earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License2/2earned
  • Verified public repository2/2earned

05Platforms
06Labels