Skip to content

fix(bonjour): truncate mDNS service name and hostname to 63-byte DNS label limit [AI-assisted]#72809

Merged
steipete merged 2 commits into
openclaw:mainfrom
luyao618:fix/37705-bonjour-label-length
Apr 27, 2026
Merged

fix(bonjour): truncate mDNS service name and hostname to 63-byte DNS label limit [AI-assisted]#72809
steipete merged 2 commits into
openclaw:mainfrom
luyao618:fix/37705-bonjour-label-length

Conversation

@luyao618

Copy link
Copy Markdown
Contributor

🤖 AI-assisted (built with Codex via Hermes orchestration). Test level: fully tested. Prompt summary available on request.

Summary

  • Problem: When the system hostname exceeds 63 bytes (common with Kubernetes pod names like app-41627eae5842473f9e05f139ea307277-7f9477f4d6-lqqzf), the @homebridge/ciao DNS label encoder throws an AssertionError that crashes the gateway on startup.
  • Why it matters: Prevents OpenClaw from starting at all on Kubernetes and other platforms with long auto-generated hostnames.
  • What changed: Added truncateToDnsLabel() that safely truncates UTF-8 strings to the 63-byte DNS label limit (RFC 1035), applied to both the mDNS service instance name and hostname before passing them to ciao.
  • What did NOT change (scope boundary): No changes to ciao library internals, no changes to Bonjour discovery logic, watchdog, or lifecycle management.

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

  • Plugin (bonjour)

Linked Issue/PR

Root Cause

  • Root cause: safeServiceName() only checked for empty strings but did not enforce the 63-byte DNS label limit from RFC 1035. Long hostnames (e.g., Kubernetes pod names) were passed directly to @homebridge/ciao's DNSLabelCoder, which asserts label length ≤ 63 bytes.
  • Missing detection / guardrail: No input validation on hostname/service name length before passing to the mDNS library.
  • Contributing context: Kubernetes generates pod hostnames that routinely exceed 63 bytes; the original code assumed short hostnames.

Regression Test Plan

  • Coverage level that should have caught this:
    • Unit test
  • Target test or file: extensions/bonjour/src/advertiser.test.ts
  • Scenario the test should lock in: (1) Long Kubernetes-style hostname produces service name and hostname both within 63 bytes. (2) Multi-byte CJK hostname is truncated at UTF-8 code point boundaries without producing replacement characters.
  • Why this is the smallest reliable guardrail: Unit test with mock ciao service verifies the truncation contract without needing a real mDNS stack.
  • Existing test that already covers this (if any): N/A
  • If no new test is added, why not: N/A — two new tests added.

User-visible / Behavior Changes

  • Gateway no longer crashes on startup when the system hostname exceeds 63 bytes. The mDNS service name is silently truncated to fit within DNS label limits.

Diagram (if applicable)

N/A

Security Impact (required)

  • New permissions/capabilities? No
  • The truncation is purely cosmetic for mDNS discovery names. No security-sensitive data is involved — hostnames are already public via mDNS broadcast.

@greptile-apps

greptile-apps Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds truncateToDnsLabel() to enforce the 63-byte RFC 1035 limit on mDNS service names and hostnames before they reach @homebridge/ciao, preventing the AssertionError crash on Kubernetes environments with long auto-generated pod hostnames. The implementation correctly handles multi-byte UTF-8 sequences by decoding after slicing and stripping the U+FFFD replacement character.

Confidence Score: 4/5

Safe to merge; the fix is correct and well-scoped, with only minor edge-case and test-coverage concerns.

No P0 or P1 issues found. Two P2 findings: a theoretical trailing-hyphen edge case in the truncation helper (only reachable via a contrived OPENCLAW_MDNS_HOSTNAME value), and a test that doesn't fully exercise the hostname truncation path because the chosen fixture is below the 63-byte threshold.

extensions/bonjour/src/advertiser.test.ts — first new test should use a hostname longer than 63 bytes to properly exercise the hostname truncation branch.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/bonjour/src/advertiser.ts
Line: 198

Comment:
**Truncation may produce a trailing hyphen**

After slicing at byte 63, the result could end with `-` (e.g. if `OPENCLAW_MDNS_HOSTNAME` is set to a long value like `very-long-host-name-....-`). RFC 1035 §2.3.1 forbids labels that start or end with a hyphen, and `@homebridge/ciao` validates this. Consider stripping trailing hyphens after decoding.

```suggestion
  return decoded.replace(/\uFFFD$/, "").replace(/-+$/, "").trim() || "OpenClaw";
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/bonjour/src/advertiser.test.ts
Line: 737

Comment:
**Test hostname is below the 63-byte limit**

`app-41627eae5842473f9e05f139ea307277-7f9477f4d6-lqqzf` is 53 bytes — well under the 63-byte threshold — so `truncateToDnsLabel` returns it unchanged for the `hostname` field. The assertion `encode(hostname).byteLength ≤ 63` passes trivially without exercising the truncation path. Truncation of the service name IS exercised (because appending ` (OpenClaw)` yields 64 bytes), but the dedicated hostname-truncation branch remains untested. Use a hostname of 64+ bytes (e.g. `"a".repeat(64)`) to cover that path.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(bonjour): truncate mDNS service name..." | Re-trigger Greptile

Comment thread extensions/bonjour/src/advertiser.ts Outdated
const truncated = encoded.slice(0, MAX_DNS_LABEL_BYTES);
const decoded = new TextDecoder("utf-8", { fatal: false }).decode(truncated);
// Strip any replacement character from incomplete multi-byte sequence at the end
return decoded.replace(/\uFFFD$/, "").trim() || "OpenClaw";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Truncation may produce a trailing hyphen

After slicing at byte 63, the result could end with - (e.g. if OPENCLAW_MDNS_HOSTNAME is set to a long value like very-long-host-name-....-). RFC 1035 §2.3.1 forbids labels that start or end with a hyphen, and @homebridge/ciao validates this. Consider stripping trailing hyphens after decoding.

Suggested change
return decoded.replace(/\uFFFD$/, "").trim() || "OpenClaw";
return decoded.replace(/\uFFFD$/, "").replace(/-+$/, "").trim() || "OpenClaw";
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/bonjour/src/advertiser.ts
Line: 198

Comment:
**Truncation may produce a trailing hyphen**

After slicing at byte 63, the result could end with `-` (e.g. if `OPENCLAW_MDNS_HOSTNAME` is set to a long value like `very-long-host-name-....-`). RFC 1035 §2.3.1 forbids labels that start or end with a hyphen, and `@homebridge/ciao` validates this. Consider stripping trailing hyphens after decoding.

```suggestion
  return decoded.replace(/\uFFFD$/, "").replace(/-+$/, "").trim() || "OpenClaw";
```

How can I resolve this? If you propose a fix, please make it concise.

@@ -735,6 +735,54 @@ describe("gateway bonjour advertiser", () => {
await started.stop();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Test hostname is below the 63-byte limit

app-41627eae5842473f9e05f139ea307277-7f9477f4d6-lqqzf is 53 bytes — well under the 63-byte threshold — so truncateToDnsLabel returns it unchanged for the hostname field. The assertion encode(hostname).byteLength ≤ 63 passes trivially without exercising the truncation path. Truncation of the service name IS exercised (because appending (OpenClaw) yields 64 bytes), but the dedicated hostname-truncation branch remains untested. Use a hostname of 64+ bytes (e.g. "a".repeat(64)) to cover that path.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/bonjour/src/advertiser.test.ts
Line: 737

Comment:
**Test hostname is below the 63-byte limit**

`app-41627eae5842473f9e05f139ea307277-7f9477f4d6-lqqzf` is 53 bytes — well under the 63-byte threshold — so `truncateToDnsLabel` returns it unchanged for the `hostname` field. The assertion `encode(hostname).byteLength ≤ 63` passes trivially without exercising the truncation path. Truncation of the service name IS exercised (because appending ` (OpenClaw)` yields 64 bytes), but the dedicated hostname-truncation branch remains untested. Use a hostname of 64+ bytes (e.g. `"a".repeat(64)`) to cover that path.

How can I resolve this? If you propose a fix, please make it concise.

@steipete

Copy link
Copy Markdown
Contributor

Re-reviewed against current main and current head.

This is the active fix path for #37705: main still has scoped Bonjour fatal-crash handling, but no DNS-label truncation for the service name / env hostname path. Current head applies byte-safe truncation to both service name and host label, strips trailing hyphens after truncation, includes multibyte coverage, has a changelog entry, and the latest PR checks are green.

No blocker from me. Please keep #72332 separate/closed; that PR was for the older Windows/ciao fatal-crash cluster and is now superseded by main, while this PR covers the new 63-byte label failure.

@steipete steipete force-pushed the fix/37705-bonjour-label-length branch from 102fbbf to eefbc98 Compare April 27, 2026 20:10

@steipete steipete left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified this against the reported #37705 crash path and current ciao behavior.

@homebridge/ciao asserts each DNS label with Buffer.byteLength(label) <= 63; the reported Kubernetes-style host is 53 bytes, but the default service instance label becomes 64 bytes after appending (OpenClaw), so this is the right failure boundary. Current main only trims safeServiceName() and passes the host label through unclamped.

Current head clamps both the service instance label and hostname with byte-aware UTF-8 truncation, strips trailing hyphens after truncation, preserves the existing fallback behavior, and covers the reported overlong path plus multibyte truncation.

Local proof on head 102fbbf897a9caf6a972e4c3bd6a63ff95c28029:

  • pnpm test extensions/bonjour/src/advertiser.test.ts -> 23 passed
  • pnpm check:changed -> passed

Good to land. Keep #37705 open until this merges, then close it as fixed by this PR.

luyao618 and others added 2 commits April 27, 2026 21:12
…label limit

When the system hostname exceeds 63 bytes (common with Kubernetes pod
names), the @homebridge/ciao DNS label encoder throws an AssertionError
that crashes the gateway on startup.

Add truncateToDnsLabel() that safely truncates UTF-8 strings at byte
boundaries, applied to both the service instance name and hostname
before passing them to ciao.

Closes openclaw#37705

AI-assisted (built with Hermes orchestration).
@steipete steipete force-pushed the fix/37705-bonjour-label-length branch from eefbc98 to 1c5a681 Compare April 27, 2026 20:12
@steipete steipete merged commit 16322d5 into openclaw:main Apr 27, 2026
67 of 68 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

plugin: bonjour Plugin integration: bonjour size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Gateway Launch Failure: Label cannot be longer than 63 bytes

3 participants