# Design ## Context `entrypoint.sh` starts D-Bus, Avahi and cupsd, configures the managed queue (`lpadmin -D/-o/-E` on every start) and then verifies cupsd answers on TCP 631. Any queue present in cupsd — including ones added manually via the web UI — is advertised as the DNS-SD instance `" @ "` (queue name when no Description is set). Avahi registers only one instance per name: with two queues deriving the same name, one is silently never advertised, and CUPS 2.4 does not rename on collision (upstream `scheduler/dnssd.c`, `TODO: Handle collisions`). Clients that pinned the losing name (macOS stores it in the `dnssd://` device URI) then report the printer offline. See proposal.md for the incident. The container ships `cups-client` (guaranteed `lpstat`), `avahi-utils` and `curl` (Containerfile). ## Goals / Non-Goals **Goals:** - At startup, detect when two or more queues would derive the same DNS-SD instance name and log an actionable warning naming the queues. - Keep the check strictly best-effort: it must never affect startup success or queue configuration (consistent with the existing warning style under `set -eu`). - Give operators a documented way to confirm and fix the condition by hand (README troubleshooting entry). **Non-Goals:** - No automatic remediation (no renaming, no un-sharing, no deleting queues). - No change to how/when `lpadmin` runs — the restart re-registration churn is out of scope for this change. - No client-side tooling; the check runs only inside the container. ## Decisions **D1 — Derive names from live cupsd data, not from mDNS.** The check compares *derived* instance names (`Description` or queue name per queue) instead of listening to actual Avahi registrations. Derivation is deterministic and race-free at startup; observing mDNS (`avahi-browse`) would depend on registration timing and only detect collisions after the fact. - *Alternative considered:* post-startup mDNS audit ("shared queue missing from advertisements") — catches more failure modes but is timing-sensitive and needs instance→queue mapping via TXT `rp`; rejected as the primary mechanism, still documented as a manual verification step in the README. **D2 — Inputs: `LC_ALL=C lpstat -l -p` for names/Descriptions, `printers.conf` for the shared flag.** - Queue names + Descriptions come from `lpstat -l -p` run with `LC_ALL=C` so the `Description:` line parses regardless of container locale (the live value is authoritative — it is exactly what cupsd will register). - Only shared queues are advertised, so the shared flag comes from `/etc/cups/printers.conf` (`` block: `Shared yes`). When the flag cannot be determined (file not yet flushed after this run's `lpadmin`, queue missing from it), the queue is **treated as shared** — a collision warning for a not-yet-shared queue is still actionable, whereas staying silent on a shared one reproduces the original incident. - *Alternative considered:* `ipptool` Get-Printer-Attributes — richer data, but availability of `ipptool` in the image is not guaranteed by the Containerfile package list; rejected to keep the check dependency-free beyond `cups-client`. **D3 — Placement and failure mode.** A `check_dnssd_name_collisions` function runs after the queue configuration blocks and the TCP reachability check, immediately before the final `ready` log line, so the warning is the last thing an operator sees in `podman logs`. It logs through the existing `log()` helper (`[entrypoint] warning: …` followed by indented detail lines, mirroring the TCP-631 error block) and never exits non-zero; every parse failure degrades to "check skipped" (the script runs under `set -eu`, so all command substitutions are guarded with `|| true`). **D4 — Collision = exact match on the name base.** The `@ ` suffix is identical for every queue on the server, so equality is decided on the base alone; the full derived name (base + `@ `) is still printed in the warning so it matches what `dns-sd -B` shows. The remedy text offers the two fixes that worked on the live system: give each queue a unique Description (`lpadmin -D`), or remove the redundant queue (`lpadmin -x`), followed by remove/re-add of the client queue (README details why clients must be re-added). ## Risks / Trade-offs - [Queues created *after* startup are not re-checked] → acceptable: the check runs on every container start, and the README documents a manual `dns-sd -B _ipp._tcp` verification for live changes. - [Stale shared flag if `printers.conf` is not flushed in time] → mitigated by assuming shared when unknown (errs toward warning), see D2. - [False warning when a shared flag parse misreads a queue block] → parsing is scoped to `` … `` blocks with exact keyword matches; a misread can at worst add a queue to the comparison, and the warning is advisory only. - [Warning becomes noise on intentionally duplicated setups] → duplication is never functional in CUPS/Avahi (one registrant always loses), so there is no legitimate configuration the warning would nag about. ## Migration Plan Log-output-only change: deploy by rebuilding the image and restarting the container (normal quadlet cycle). No state, config or client impact; rollback is reverting `entrypoint.sh`/README.