Specter · Client manager { if (event.key === "Escape") cancelRemoval(); }} />

Tunnels {tunnelRows.length}

Server{status?.apex || (statusError ? "Unavailable" : "Loading…")}
Connected {#if status}{connectedNodes.length} {connectedNodes.length === 1 ? "node" : "nodes"}{:else}Not checked{/if}
Last sync{synchronization?.attemptedAt ? timeLabel(synchronization.attemptedAt) : "Not attempted"}
{#if syncNotice}
{syncNotice.title} {#if status?.pending} Retry {status.retryAt ? timeLabel(status.retryAt) : "pending"}.{/if} {#if unsavedChanges || (syncNotice.saveFailed && statusError)}

Review the YAML before reloading or restarting.

{/if} {#if syncNotice.detail}
Error details

{syncNotice.detail}

{/if} {#if syncNotice.restore}
Restore tunnel

Add under tunnels:, restore other options, then synchronize.

{syncNotice.restore}
{/if}
{/if} {#if statusError || listError}
{#if statusError}
Status unavailable.{#if status} Snapshot {timeLabel(statusCheckedAt)}.{/if}
Status error

{statusError}

{/if} {#if listError}
Hostnames unavailable.{#if registered} Snapshot {timeLabel(listCheckedAt)}.{/if}
Hostname error

{listError}

{/if}
{/if}
{#if registered === null && refreshing && !tunnelRows.length}
Loading tunnels…
{:else if tunnelRows.length === 0}
{#if statusError || listError}No data. Refresh to retry.{:else}No tunnels. Add a target, then synchronize.{/if}
{:else} {#each tunnelRows as tunnel, index (tunnel.hostname || `unassigned:${index}`)} {#if confirmation?.tunnel.hostname === tunnel.hostname} {/if} {/each}
HostnameLocal targetPublicationActions
{tunnel.hostname || "Awaiting hostname"} {tunnel.target || "None"}{#if tunnel.headerHost}Host: {tunnel.headerHost}{/if} {publicationLabel(tunnel)} {#if tunnel.published}{tunnel.publishedEndpoints} {tunnel.publishedEndpoints === 1 ? "endpoint" : "endpoints"}{/if} {#if tunnel.error}
Error

{tunnel.error}

{/if}

{confirmation.action === "unpublish" ? "Remove target from YAML; keep hostname." : "Remove target from YAML; release hostname for others to claim."}

{/if}

Publication: last acknowledgement; target health unchecked.

Configuration

Configured tunnels
{status ? configuredCount : "Unavailable"}
Disk state
{!synchronization?.attemptedAt ? "Not checked" : synchronization.saved ? "Saved" : "Unsaved"}
Edit tunnels
{"tunnels:\n  - target: http://127.0.0.1:8080\n    # hostname: app.example.com"}

Edit your existing YAML, then synchronize. Keep credentials intact. Omit hostname for automatic assignment; otherwise use a reserved or verified hostname.

t.hostname).filter(Boolean) ?? null} loadingHostnames={checkingHostnames} hostnameError={listError} onRefreshHostnames={() => loadHostnames(true)} />