fix(status): clarify tailscale exposure state

This commit is contained in:
Peter Steinberger
2026-04-26 01:46:52 +01:00
parent 2f6615d2ee
commit 1fe0e6fc4a
7 changed files with 30 additions and 8 deletions

View File

@@ -65,6 +65,10 @@ Docs: https://docs.openclaw.ai
### Fixes
- CLI/status: label the OpenClaw Serve/Funnel setting as `Tailscale exposure`
and show daemon state separately when available, so `gateway.tailscale.mode:
"off"` no longer reads like the Tailscale daemon is stopped. Fixes #71790.
Thanks @pesvobodak.
- Plugins/Bonjour: stop ciao mDNS watchdog failures from looping forever when
the advertiser stays stuck in `probing` or `announcing`; Bonjour now disables
itself for the current Gateway process after repeated failed restarts while

View File

@@ -16,6 +16,10 @@ Tailscale provides HTTPS, routing, and (for Serve) identity headers.
- `funnel`: Public HTTPS via `tailscale funnel`. OpenClaw requires a shared password.
- `off`: Default (no Tailscale automation).
Status and audit output use **Tailscale exposure** for this OpenClaw Serve/Funnel
mode. `off` means OpenClaw is not managing Serve or Funnel; it does not mean the
local Tailscale daemon is stopped or logged out.
## Auth
Set `gateway.auth.mode` to control the handshake:

View File

@@ -63,6 +63,18 @@ function createBaseParams(
}
describe("status-all diagnosis port checks", () => {
it("labels OpenClaw Tailscale exposure separately from daemon state", async () => {
const params = createBaseParams([]);
params.tailscale.backendState = "Running";
params.tailscale.dnsName = "box.tail.ts.net";
await appendStatusAllDiagnosis(params);
const output = params.lines.join("\n");
expect(output).toContain("✓ Tailscale exposure: off · daemon Running · box.tail.ts.net");
expect(output).not.toContain("Tailscale: off");
});
it("treats same-process dual-stack loopback listeners as healthy", async () => {
const params = createBaseParams([
{ pid: 5001, commandLine: "openclaw-gateway", address: "127.0.0.1:18789" },

View File

@@ -169,8 +169,8 @@ export async function appendStatusAllDiagnosis(params: {
const hasDns = Boolean(params.tailscale.dnsName);
const label =
params.tailscaleMode === "off"
? `Tailscale: off · ${backend}${params.tailscale.dnsName ? ` · ${params.tailscale.dnsName}` : ""}`
: `Tailscale: ${params.tailscaleMode} · ${backend}${params.tailscale.dnsName ? ` · ${params.tailscale.dnsName}` : ""}`;
? `Tailscale exposure: off · daemon ${backend}${params.tailscale.dnsName ? ` · ${params.tailscale.dnsName}` : ""}`
: `Tailscale exposure: ${params.tailscaleMode} · daemon ${backend}${params.tailscale.dnsName ? ` · ${params.tailscale.dnsName}` : ""}`;
emitCheck(label, okBackend && (params.tailscaleMode === "off" || hasDns) ? "ok" : "warn");
if (params.tailscale.error) {
lines.push(` ${muted(`error: ${params.tailscale.error}`)}`);

View File

@@ -145,7 +145,7 @@ describe("status-all format", () => {
includeBackendStateWhenOff: true,
includeDnsNameWhenOff: true,
}),
).toBe("off · Stopped · box.tail.ts.net");
).toBe("off · daemon Stopped · box.tail.ts.net");
});
it("formats service values across short and detailed runtime surfaces", () => {
@@ -301,7 +301,7 @@ describe("status-all format", () => {
).toEqual([
{ Item: "Version", Value: "1.0.0" },
{ Item: "Dashboard", Value: "https://openclaw.local" },
{ Item: "Tailscale", Value: "serve · https://tail.example" },
{ Item: "Tailscale exposure", Value: "serve · https://tail.example" },
{ Item: "Channel", Value: "stable" },
{ Item: "Git", Value: "main @ v1.0.0" },
{ Item: "Update", Value: "up to date" },
@@ -373,7 +373,7 @@ describe("status-all format", () => {
).toEqual([
{ Item: "Version", Value: "1.0.0" },
{ Item: "Dashboard", Value: "http://127.0.0.1:18789/" },
{ Item: "Tailscale", Value: "serve · box.tail.ts.net · https://box.tail.ts.net" },
{ Item: "Tailscale exposure", Value: "serve · box.tail.ts.net · https://box.tail.ts.net" },
{ Item: "Channel", Value: "stable (config)" },
{ Item: "Git", Value: "main · tag v1.2.3" },
{ Item: "Update", Value: "available · custom update" },

View File

@@ -112,7 +112,9 @@ export function formatStatusTailscaleValue(params: {
const decorateWarn = params.decorateWarn ?? ((value: string) => value);
if (params.tailscaleMode === "off") {
const suffix = [
params.includeBackendStateWhenOff && params.backendState ? params.backendState : null,
params.includeBackendStateWhenOff && params.backendState
? `daemon ${params.backendState}`
: null,
params.includeDnsNameWhenOff && params.dnsName ? params.dnsName : null,
]
.filter(Boolean)
@@ -192,7 +194,7 @@ export function buildStatusOverviewRows(params: {
const rows: StatusOverviewRow[] = [...(params.prefixRows ?? [])];
rows.push(
{ Item: "Dashboard", Value: params.dashboardValue },
{ Item: "Tailscale", Value: params.tailscaleValue },
{ Item: "Tailscale exposure", Value: params.tailscaleValue },
{ Item: "Channel", Value: params.channelLabel },
);
if (params.gitLabel) {

View File

@@ -81,7 +81,7 @@ describe("status-overview-surface", () => {
).toEqual([
{ Item: "OS", Value: "macOS · node 22" },
{ Item: "Dashboard", Value: "http://127.0.0.1:18789/" },
{ Item: "Tailscale", Value: "muted(off · box.tail.ts.net)" },
{ Item: "Tailscale exposure", Value: "muted(off · box.tail.ts.net)" },
{ Item: "Channel", Value: "stable (config)" },
{ Item: "Git", Value: "main · tag v1.2.3" },
{ Item: "Update", Value: "available · custom update" },