Problem
kamal-proxy domains list prints a Cobra table. The data behind it — DomainsStatusResponse, including per-domain Quarantine entries with until, failures and kind — is already structured on the RPC, but the only way out of the binary is the rendered table.
That blocks the gem. dash doctor wants to tell an operator mid-DNS-cutover "certificate issuance held until 14:32 (preflight)", which was the original goal of zoolutions/dash#126. Getting there today means running docker exec kamal-proxy domains list over SSH and scraping columns out of human-readable output — brittle against any future column change, and a poor failure mode inside a read-only diagnostic. So dash#126 shipped a weaker message instead: for ACME-issued domains whose DNS does not yet point at the proxy, it says no certificate can be issued until it does and that the proxy handles it automatically. True and useful, but it cannot name the hold or when it lifts.
Suggested shape
A --json flag on domains list (and plausibly domains stats), emitting DomainsStatusResponse more or less as-is. There is precedent in this codebase: kamal-proxy list --json already exists (internal/cmd/list.go), and the gem already calls it that way via Dash::Commands::Loadbalancer#list(json: true).
Rough scope:
internal/cmd/domains.go — add the flag to domainsListCommand, and marshal the response instead of building a table when it is set. Follow whatever list --json does so the two behave alike.
- Keep the table the default. The gem opts in; humans keep what they have.
- Worth confirming the JSON tags on
DomainsStatusResponse, DomainStatus, RegisteredDomainStatus and QuarantineStatus read well as a public surface, since making them machine-readable makes them a contract.
Follow-on in the gem
Once this ships and MINIMUM_VERSION names it, dash doctor can read real hold state — replacing the generic message added in dash#126 with the specific one, and degrading to the generic form against an older proxy.
Not urgent. The automatic release means a hold usually clears itself before anyone looks, so this improves diagnosis rather than fixing a broken path.
Problem
kamal-proxy domains listprints a Cobra table. The data behind it —DomainsStatusResponse, including per-domainQuarantineentries withuntil,failuresandkind— is already structured on the RPC, but the only way out of the binary is the rendered table.That blocks the gem.
dash doctorwants to tell an operator mid-DNS-cutover "certificate issuance held until 14:32 (preflight)", which was the original goal of zoolutions/dash#126. Getting there today means runningdocker exec kamal-proxy domains listover SSH and scraping columns out of human-readable output — brittle against any future column change, and a poor failure mode inside a read-only diagnostic. So dash#126 shipped a weaker message instead: for ACME-issued domains whose DNS does not yet point at the proxy, it says no certificate can be issued until it does and that the proxy handles it automatically. True and useful, but it cannot name the hold or when it lifts.Suggested shape
A
--jsonflag ondomains list(and plausiblydomains stats), emittingDomainsStatusResponsemore or less as-is. There is precedent in this codebase:kamal-proxy list --jsonalready exists (internal/cmd/list.go), and the gem already calls it that way viaDash::Commands::Loadbalancer#list(json: true).Rough scope:
internal/cmd/domains.go— add the flag todomainsListCommand, and marshal the response instead of building a table when it is set. Follow whateverlist --jsondoes so the two behave alike.DomainsStatusResponse,DomainStatus,RegisteredDomainStatusandQuarantineStatusread well as a public surface, since making them machine-readable makes them a contract.Follow-on in the gem
Once this ships and
MINIMUM_VERSIONnames it,dash doctorcan read real hold state — replacing the generic message added in dash#126 with the specific one, and degrading to the generic form against an older proxy.Not urgent. The automatic release means a hold usually clears itself before anyone looks, so this improves diagnosis rather than fixing a broken path.