INWX DnsServer: nameserver.info fails when record IDs are strings (breaks automatic DNS / ACME)

Issue Description

Automatic DNS management via the INWX DnsServer provider fails when the zone already contains DNS records whose INWX API returns string record IDs (IDs exceeding the former 32-bit integer range).

Stalwart uses the dns-update crate (InwxProvider in src/providers/inwx.rs). When updating an RRSet, the provider calls nameserver.info and deserializes record[].id as i64. INWX now returns these IDs as JSON strings, which causes deserialization to fail before any create/update/delete can complete.

This breaks DnsManagement tasks and prevents Stalwart from publishing ACME-related TXT records (_validation-persist, _acme-challenge, apex SPF/DMARC, etc.) on domains with existing INWX records.

The same INWX API change was fixed elsewhere (lego ≥ 4.29, goinwx, Traefik 3.6.4); Stalwart’s INWX port appears to predate that change.

Expected Behavior

  • DnsManagement should sync DNS records to INWX via the configured DnsServer (@type: Inwx).
  • set_rrset / add_to_rrset should read existing records with nameserver.info, reconcile the RRSet, and create/update/delete as needed.
  • ACME with automatic DNS (Dns01 / DnsPersist01) should be able to publish challenge TXT records.

Actual Behavior

DnsManagement fails when touching owners that already have TXT records in INWX (apex SPF/DKIM, subdomains, etc.). New records on previously empty owners may succeed (e.g. first SPF on mail.example.com), but any operation that must list existing records fails.

WebUI / task failure reason:

Failed to set DNS RRSet for example.com./TXT: Failed to set DNS RRSet: API error: Failed to parse INWX nameserver.info: invalid type: string "2377221647", expected i64

Reproduction Steps

  1. Use a domain on INWX with existing DNS records (any type; TXT at apex is enough — e.g. SPF/DKIM from a mail relay).
  2. Stalwart v0.16.x, native install (or Docker).
  3. Settings → Network → DNS → DNS Providers → create Inwx DnsServer (production API, sandbox: false).
  4. Management → Domains → Domains → enable DNS management: Automatic with that provider.
  5. Management → Tasks → New Task → DNS management for the domain.
  6. Observe task failure with Failed to parse INWX nameserver.info: invalid type: string "…", expected i64.

Minimal repro without Stalwart: call INWX nameserver.info for a name with existing records; response contains "id": "2377221647" (string). Current NameserverRecord { id: i64 } in dns-update cannot deserialize it.

Relevant Log Output

2026-06-05T09:44:27Z DEBUG DNS record lookup failed (dns.record-lookup-failed) hostname = “…”, details = “…”, type = “TXT”, reason = “DNS provider failed to list RRSet: API error: Failed to parse INWX nameserver.info: invalid type: string “2377221647”, expected i64”
2026-06-05T09:44:27Z INFO DNS record created (dns.record-created) hostname = “…”, details = “…”, type = “TXT”, value = [“v=spf1 a -all”]
2026-06-05T09:44:27Z WARN Task failed during processing (task-manager.task-failed) id = …, details = “DnsManagement”, reason = “Failed to set DNS RRSet for …/TXT: Failed to set DNS RRSet: API error: Failed to parse INWX nameserver.info: invalid type: string “2377221647”, expected i64”

Stalwart Version

v0.16.x

Installation Method

Binary (Linux)

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

Additional Context

In stalwartlabs/dns-updatesrc/providers/inwx.rs:

struct NameserverRecord {
    id: i64,  // INWX now returns string IDs
    ...
}

INWX announcement (via third-party clients): record IDs from nameserver.info / nameserver.createRecord are now strings; nameserver.deleteRecord, nameserver.updateRecord, and nameserver.info (recordId) must also use string IDs.

References:

Suggested fix

In dns-update/src/providers/inwx.rs:

  1. Change NameserverRecord.id to String (or a newtype / deserializer accepting both string and integer for backward compatibility with INWX sandbox).
  2. Pass "id" as a JSON string in nameserver.deleteRecord (and any update calls).

Example (minimal):

#[derive(Deserialize, Debug, Clone)]
struct NameserverRecord {
    id: String,
    // ...
}

// deleteRecord:
self.call("nameserver.deleteRecord", json!({ "id": stale.id }))

Optionally add a unit/integration test with mocked nameserver.info JSON using "id": "2377221647".

Stalwart depends on dns-update = "0.5" (crates/common/Cargo.toml); a patch release dns-update 0.5.1 + Stalwart bump would ship the fix.

Note: ACME itself is Stalwart’s native Rust implementation; only DNS updates go through dns-update (not lego).

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

This has been fixed on the main branch. The fix will be included in v0.16.8.