fix: shorten bonjour gateway service type
This commit is contained in:
@@ -15,6 +15,7 @@ Docs: https://docs.clawd.bot
|
|||||||
- Plugins/UI: let channel plugin metadata drive UI labels/icons and cron channel options. (#1306) — thanks @steipete.
|
- Plugins/UI: let channel plugin metadata drive UI labels/icons and cron channel options. (#1306) — thanks @steipete.
|
||||||
- Zalouser: add channel dock metadata, config schema, setup wiring, probe, and status issues. (#1219) — thanks @suminhthanh.
|
- Zalouser: add channel dock metadata, config schema, setup wiring, probe, and status issues. (#1219) — thanks @suminhthanh.
|
||||||
### Fixes
|
### Fixes
|
||||||
|
- Discovery: shorten Bonjour DNS-SD service type to `_clawdbot-gw._tcp` and update discovery clients/docs.
|
||||||
- Web search: infer Perplexity base URL from API key source (direct vs OpenRouter).
|
- Web search: infer Perplexity base URL from API key source (direct vs OpenRouter).
|
||||||
- TUI: keep thinking blocks ordered before content during streaming and isolate per-run assembly. (#1202) — thanks @aaronveklabs.
|
- TUI: keep thinking blocks ordered before content during streaming and isolate per-run assembly. (#1202) — thanks @aaronveklabs.
|
||||||
- TUI: align custom editor initialization with the latest pi-tui API. (#1298) — thanks @sibbl.
|
- TUI: align custom editor initialization with the latest pi-tui API. (#1298) — thanks @sibbl.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
## Clawdbot Node (Android) (internal)
|
## Clawdbot Node (Android) (internal)
|
||||||
|
|
||||||
Modern Android node app: connects to the **Gateway WebSocket** (`_clawdbot-gateway._tcp`) and exposes **Canvas + Chat + Camera**.
|
Modern Android node app: connects to the **Gateway WebSocket** (`_clawdbot-gw._tcp`) and exposes **Canvas + Chat + Camera**.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- The node keeps the connection alive via a **foreground service** (persistent notification with a Disconnect action).
|
- The node keeps the connection alive via a **foreground service** (persistent notification with a Disconnect action).
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class GatewayDiscovery(
|
|||||||
private val nsd = context.getSystemService(NsdManager::class.java)
|
private val nsd = context.getSystemService(NsdManager::class.java)
|
||||||
private val connectivity = context.getSystemService(ConnectivityManager::class.java)
|
private val connectivity = context.getSystemService(ConnectivityManager::class.java)
|
||||||
private val dns = DnsResolver.getInstance()
|
private val dns = DnsResolver.getInstance()
|
||||||
private val serviceType = "_clawdbot-gateway._tcp."
|
private val serviceType = "_clawdbot-gw._tcp."
|
||||||
private val wideAreaDomain = "clawdbot.internal."
|
private val wideAreaDomain = "clawdbot.internal."
|
||||||
private val logTag = "Clawdbot/GatewayDiscovery"
|
private val logTag = "Clawdbot/GatewayDiscovery"
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
</dict>
|
</dict>
|
||||||
<key>NSBonjourServices</key>
|
<key>NSBonjourServices</key>
|
||||||
<array>
|
<array>
|
||||||
<string>_clawdbot-gateway._tcp</string>
|
<string>_clawdbot-gw._tcp</string>
|
||||||
</array>
|
</array>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>Clawdbot can capture photos or short video clips when requested via the gateway.</string>
|
<string>Clawdbot can capture photos or short video clips when requested via the gateway.</string>
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import Testing
|
|||||||
@Test func stableIDForServiceDecodesAndNormalizesName() {
|
@Test func stableIDForServiceDecodesAndNormalizesName() {
|
||||||
let endpoint = NWEndpoint.service(
|
let endpoint = NWEndpoint.service(
|
||||||
name: "Clawdbot\\032Gateway \\032 Node\n",
|
name: "Clawdbot\\032Gateway \\032 Node\n",
|
||||||
type: "_clawdbot-gateway._tcp",
|
type: "_clawdbot-gw._tcp",
|
||||||
domain: "local.",
|
domain: "local.",
|
||||||
interface: nil)
|
interface: nil)
|
||||||
|
|
||||||
#expect(GatewayEndpointID.stableID(endpoint) == "_clawdbot-gateway._tcp|local.|Clawdbot Gateway Node")
|
#expect(GatewayEndpointID.stableID(endpoint) == "_clawdbot-gw._tcp|local.|Clawdbot Gateway Node")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test func stableIDForNonServiceUsesEndpointDescription() {
|
@Test func stableIDForNonServiceUsesEndpointDescription() {
|
||||||
@@ -22,7 +22,7 @@ import Testing
|
|||||||
@Test func prettyDescriptionDecodesBonjourEscapes() {
|
@Test func prettyDescriptionDecodesBonjourEscapes() {
|
||||||
let endpoint = NWEndpoint.service(
|
let endpoint = NWEndpoint.service(
|
||||||
name: "Clawdbot\\032Gateway",
|
name: "Clawdbot\\032Gateway",
|
||||||
type: "_clawdbot-gateway._tcp",
|
type: "_clawdbot-gw._tcp",
|
||||||
domain: "local.",
|
domain: "local.",
|
||||||
interface: nil)
|
interface: nil)
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ targets:
|
|||||||
NSAppTransportSecurity:
|
NSAppTransportSecurity:
|
||||||
NSAllowsArbitraryLoadsInWebContent: true
|
NSAllowsArbitraryLoadsInWebContent: true
|
||||||
NSBonjourServices:
|
NSBonjourServices:
|
||||||
- _clawdbot-gateway._tcp
|
- _clawdbot-gw._tcp
|
||||||
NSCameraUsageDescription: Clawdbot can capture photos or short video clips when requested via the gateway.
|
NSCameraUsageDescription: Clawdbot can capture photos or short video clips when requested via the gateway.
|
||||||
NSLocationWhenInUseUsageDescription: Clawdbot uses your location when you allow location sharing.
|
NSLocationWhenInUseUsageDescription: Clawdbot uses your location when you allow location sharing.
|
||||||
NSLocationAlwaysAndWhenInUseUsageDescription: Clawdbot can share your location in the background when you enable Always.
|
NSLocationAlwaysAndWhenInUseUsageDescription: Clawdbot can share your location in the background when you enable Always.
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ enum WideAreaGatewayDiscovery {
|
|||||||
|
|
||||||
let domain = ClawdbotBonjour.wideAreaGatewayServiceDomain
|
let domain = ClawdbotBonjour.wideAreaGatewayServiceDomain
|
||||||
let domainTrimmed = domain.trimmingCharacters(in: CharacterSet(charactersIn: "."))
|
let domainTrimmed = domain.trimmingCharacters(in: CharacterSet(charactersIn: "."))
|
||||||
let probeName = "_clawdbot-gateway._tcp.\(domainTrimmed)"
|
let probeName = "_clawdbot-gw._tcp.\(domainTrimmed)"
|
||||||
guard let ptrLines = context.dig(
|
guard let ptrLines = context.dig(
|
||||||
["+short", "+time=1", "+tries=1", "@\(nameserver)", probeName, "PTR"],
|
["+short", "+time=1", "+tries=1", "@\(nameserver)", probeName, "PTR"],
|
||||||
min(defaultTimeoutSeconds, remaining()))?.split(whereSeparator: \.isNewline),
|
min(defaultTimeoutSeconds, remaining()))?.split(whereSeparator: \.isNewline),
|
||||||
@@ -66,7 +66,7 @@ enum WideAreaGatewayDiscovery {
|
|||||||
let ptr = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
let ptr = raw.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
if ptr.isEmpty { continue }
|
if ptr.isEmpty { continue }
|
||||||
let ptrName = ptr.hasSuffix(".") ? String(ptr.dropLast()) : ptr
|
let ptrName = ptr.hasSuffix(".") ? String(ptr.dropLast()) : ptr
|
||||||
let suffix = "._clawdbot-gateway._tcp.\(domainTrimmed)"
|
let suffix = "._clawdbot-gw._tcp.\(domainTrimmed)"
|
||||||
let rawInstanceName = ptrName.hasSuffix(suffix)
|
let rawInstanceName = ptrName.hasSuffix(suffix)
|
||||||
? String(ptrName.dropLast(suffix.count))
|
? String(ptrName.dropLast(suffix.count))
|
||||||
: ptrName
|
: ptrName
|
||||||
@@ -156,7 +156,7 @@ enum WideAreaGatewayDiscovery {
|
|||||||
{
|
{
|
||||||
let domain = ClawdbotBonjour.wideAreaGatewayServiceDomain
|
let domain = ClawdbotBonjour.wideAreaGatewayServiceDomain
|
||||||
let domainTrimmed = domain.trimmingCharacters(in: CharacterSet(charactersIn: "."))
|
let domainTrimmed = domain.trimmingCharacters(in: CharacterSet(charactersIn: "."))
|
||||||
let probeName = "_clawdbot-gateway._tcp.\(domainTrimmed)"
|
let probeName = "_clawdbot-gw._tcp.\(domainTrimmed)"
|
||||||
|
|
||||||
let ips = candidates
|
let ips = candidates
|
||||||
candidates.removeAll(keepingCapacity: true)
|
candidates.removeAll(keepingCapacity: true)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ struct WideAreaGatewayDiscoveryTests {
|
|||||||
let nameserver = args.first(where: { $0.hasPrefix("@") }) ?? ""
|
let nameserver = args.first(where: { $0.hasPrefix("@") }) ?? ""
|
||||||
if recordType == "PTR" {
|
if recordType == "PTR" {
|
||||||
if nameserver == "@100.123.224.76" {
|
if nameserver == "@100.123.224.76" {
|
||||||
return "steipetacstudio-gateway._clawdbot-gateway._tcp.clawdbot.internal.\n"
|
return "steipetacstudio-gateway._clawdbot-gw._tcp.clawdbot.internal.\n"
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Foundation
|
|||||||
|
|
||||||
public enum ClawdbotBonjour {
|
public enum ClawdbotBonjour {
|
||||||
// v0: internal-only, subject to rename.
|
// v0: internal-only, subject to rename.
|
||||||
public static let gatewayServiceType = "_clawdbot-gateway._tcp"
|
public static let gatewayServiceType = "_clawdbot-gw._tcp"
|
||||||
public static let gatewayServiceDomain = "local."
|
public static let gatewayServiceDomain = "local."
|
||||||
public static let wideAreaGatewayServiceDomain = "clawdbot.internal."
|
public static let wideAreaGatewayServiceDomain = "clawdbot.internal."
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ clawdbot gateway call logs.tail --params '{"sinceMs": 60000}'
|
|||||||
|
|
||||||
## Discover gateways (Bonjour)
|
## Discover gateways (Bonjour)
|
||||||
|
|
||||||
`gateway discover` scans for Gateway beacons (`_clawdbot-gateway._tcp`).
|
`gateway discover` scans for Gateway beacons (`_clawdbot-gw._tcp`).
|
||||||
|
|
||||||
- Multicast DNS-SD: `local.`
|
- Multicast DNS-SD: `local.`
|
||||||
- Unicast DNS-SD (Wide-Area Bonjour): `clawdbot.internal.` (requires split DNS + DNS server; see [/gateway/bonjour](/gateway/bonjour))
|
- Unicast DNS-SD (Wide-Area Bonjour): `clawdbot.internal.` (requires split DNS + DNS server; see [/gateway/bonjour](/gateway/bonjour))
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ boundary. You can keep the same discovery UX by switching to **unicast DNS‑SD*
|
|||||||
High‑level steps:
|
High‑level steps:
|
||||||
|
|
||||||
1) Run a DNS server on the gateway host (reachable over Tailnet).
|
1) Run a DNS server on the gateway host (reachable over Tailnet).
|
||||||
2) Publish DNS‑SD records for `_clawdbot-bridge._tcp` under a dedicated zone
|
2) Publish DNS‑SD records for `_clawdbot-gw._tcp` under a dedicated zone
|
||||||
(example: `clawdbot.internal.`).
|
(example: `clawdbot.internal.`).
|
||||||
3) Configure Tailscale **split DNS** so `clawdbot.internal` resolves via that
|
3) Configure Tailscale **split DNS** so `clawdbot.internal` resolves via that
|
||||||
DNS server for clients (including iOS).
|
DNS server for clients (including iOS).
|
||||||
@@ -49,8 +49,8 @@ This installs CoreDNS and configures it to:
|
|||||||
Validate from a tailnet‑connected machine:
|
Validate from a tailnet‑connected machine:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dns-sd -B _clawdbot-bridge._tcp clawdbot.internal.
|
dns-sd -B _clawdbot-gw._tcp clawdbot.internal.
|
||||||
dig @<TAILNET_IPV4> -p 53 _clawdbot-bridge._tcp.clawdbot.internal PTR +short
|
dig @<TAILNET_IPV4> -p 53 _clawdbot-gw._tcp.clawdbot.internal PTR +short
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tailscale DNS settings
|
### Tailscale DNS settings
|
||||||
@@ -61,7 +61,7 @@ In the Tailscale admin console:
|
|||||||
- Add split DNS so the domain `clawdbot.internal` uses that nameserver.
|
- Add split DNS so the domain `clawdbot.internal` uses that nameserver.
|
||||||
|
|
||||||
Once clients accept tailnet DNS, iOS nodes can browse
|
Once clients accept tailnet DNS, iOS nodes can browse
|
||||||
`_clawdbot-bridge._tcp` in `clawdbot.internal.` without multicast.
|
`_clawdbot-gw._tcp` in `clawdbot.internal.` without multicast.
|
||||||
|
|
||||||
### Bridge listener security (recommended)
|
### Bridge listener security (recommended)
|
||||||
|
|
||||||
@@ -74,11 +74,11 @@ For tailnet‑only setups:
|
|||||||
|
|
||||||
## What advertises
|
## What advertises
|
||||||
|
|
||||||
Only the Gateway (when the **bridge is enabled**) advertises `_clawdbot-bridge._tcp`.
|
Only the Gateway advertises `_clawdbot-gw._tcp`.
|
||||||
|
|
||||||
## Service types
|
## Service types
|
||||||
|
|
||||||
- `_clawdbot-bridge._tcp` — bridge transport beacon (used by macOS/iOS/Android nodes).
|
- `_clawdbot-gw._tcp` — gateway transport beacon (used by macOS/iOS/Android nodes).
|
||||||
|
|
||||||
## TXT keys (non‑secret hints)
|
## TXT keys (non‑secret hints)
|
||||||
|
|
||||||
@@ -101,11 +101,11 @@ Useful built‑in tools:
|
|||||||
|
|
||||||
- Browse instances:
|
- Browse instances:
|
||||||
```bash
|
```bash
|
||||||
dns-sd -B _clawdbot-bridge._tcp local.
|
dns-sd -B _clawdbot-gw._tcp local.
|
||||||
```
|
```
|
||||||
- Resolve one instance (replace `<instance>`):
|
- Resolve one instance (replace `<instance>`):
|
||||||
```bash
|
```bash
|
||||||
dns-sd -L "<instance>" _clawdbot-bridge._tcp local.
|
dns-sd -L "<instance>" _clawdbot-gw._tcp local.
|
||||||
```
|
```
|
||||||
|
|
||||||
If browsing works but resolving fails, you’re usually hitting a LAN policy or
|
If browsing works but resolving fails, you’re usually hitting a LAN policy or
|
||||||
@@ -122,7 +122,7 @@ The Gateway writes a rolling log file (printed on startup as
|
|||||||
|
|
||||||
## Debugging on iOS node
|
## Debugging on iOS node
|
||||||
|
|
||||||
The iOS node uses `NWBrowser` to discover `_clawdbot-bridge._tcp`.
|
The iOS node uses `NWBrowser` to discover `_clawdbot-gw._tcp`.
|
||||||
|
|
||||||
To capture logs:
|
To capture logs:
|
||||||
- Settings → Bridge → Advanced → **Discovery Debug Logs**
|
- Settings → Bridge → Advanced → **Discovery Debug Logs**
|
||||||
|
|||||||
@@ -2988,7 +2988,7 @@ Auto-generated certs require `openssl` on PATH; if generation fails, the bridge
|
|||||||
|
|
||||||
### `discovery.wideArea` (Wide-Area Bonjour / unicast DNS‑SD)
|
### `discovery.wideArea` (Wide-Area Bonjour / unicast DNS‑SD)
|
||||||
|
|
||||||
When enabled, the Gateway writes a unicast DNS-SD zone for `_clawdbot-bridge._tcp` under `~/.clawdbot/dns/` using the standard discovery domain `clawdbot.internal.`
|
When enabled, the Gateway writes a unicast DNS-SD zone for `_clawdbot-gw._tcp` under `~/.clawdbot/dns/` using the standard discovery domain `clawdbot.internal.`
|
||||||
|
|
||||||
To make iOS/Android discover across networks (Vienna ⇄ London), pair this with:
|
To make iOS/Android discover across networks (Vienna ⇄ London), pair this with:
|
||||||
- a DNS server on the gateway host serving `clawdbot.internal.` (CoreDNS is recommended)
|
- a DNS server on the gateway host serving `clawdbot.internal.` (CoreDNS is recommended)
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ Troubleshooting and beacon details: [Bonjour](/gateway/bonjour).
|
|||||||
#### Service beacon details
|
#### Service beacon details
|
||||||
|
|
||||||
- Service types:
|
- Service types:
|
||||||
- `_clawdbot-bridge._tcp` (bridge transport beacon)
|
- `_clawdbot-gw._tcp` (gateway transport beacon)
|
||||||
- TXT keys (non-secret):
|
- TXT keys (non-secret):
|
||||||
- `role=gateway`
|
- `role=gateway`
|
||||||
- `lanHost=<hostname>.local`
|
- `lanHost=<hostname>.local`
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ For tailnet-only setups (recommended for Vienna ⇄ London), bind the gateway to
|
|||||||
From the gateway machine:
|
From the gateway machine:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dns-sd -B _clawdbot-gateway._tcp local.
|
dns-sd -B _clawdbot-gw._tcp local.
|
||||||
```
|
```
|
||||||
|
|
||||||
More debugging notes: [Bonjour](/gateway/bonjour).
|
More debugging notes: [Bonjour](/gateway/bonjour).
|
||||||
@@ -61,7 +61,7 @@ More debugging notes: [Bonjour](/gateway/bonjour).
|
|||||||
|
|
||||||
Android NSD/mDNS discovery won’t cross networks. If your Android node and the gateway are on different networks but connected via Tailscale, use Wide-Area Bonjour / unicast DNS-SD instead:
|
Android NSD/mDNS discovery won’t cross networks. If your Android node and the gateway are on different networks but connected via Tailscale, use Wide-Area Bonjour / unicast DNS-SD instead:
|
||||||
|
|
||||||
1) Set up a DNS-SD zone (example `clawdbot.internal.`) on the gateway host and publish `_clawdbot-gateway._tcp` records.
|
1) Set up a DNS-SD zone (example `clawdbot.internal.`) on the gateway host and publish `_clawdbot-gw._tcp` records.
|
||||||
2) Configure Tailscale split DNS for `clawdbot.internal` pointing at that DNS server.
|
2) Configure Tailscale split DNS for `clawdbot.internal` pointing at that DNS server.
|
||||||
|
|
||||||
Details and example CoreDNS config: [Bonjour](/gateway/bonjour).
|
Details and example CoreDNS config: [Bonjour](/gateway/bonjour).
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ describe("bonjour-discovery", () => {
|
|||||||
if (domain === "local.") {
|
if (domain === "local.") {
|
||||||
return {
|
return {
|
||||||
stdout: [
|
stdout: [
|
||||||
"Add 2 3 local. _clawdbot-gateway._tcp. Peter\\226\\128\\153s Mac Studio Gateway",
|
"Add 2 3 local. _clawdbot-gw._tcp. Peter\\226\\128\\153s Mac Studio Gateway",
|
||||||
"Add 2 3 local. _clawdbot-gateway._tcp. Laptop Gateway",
|
"Add 2 3 local. _clawdbot-gw._tcp. Laptop Gateway",
|
||||||
"",
|
"",
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
stderr: "",
|
stderr: "",
|
||||||
@@ -30,7 +30,7 @@ describe("bonjour-discovery", () => {
|
|||||||
if (domain === WIDE_AREA_DISCOVERY_DOMAIN) {
|
if (domain === WIDE_AREA_DISCOVERY_DOMAIN) {
|
||||||
return {
|
return {
|
||||||
stdout: [
|
stdout: [
|
||||||
`Add 2 3 ${WIDE_AREA_DISCOVERY_DOMAIN} _clawdbot-gateway._tcp. Tailnet Gateway`,
|
`Add 2 3 ${WIDE_AREA_DISCOVERY_DOMAIN} _clawdbot-gw._tcp. Tailnet Gateway`,
|
||||||
"",
|
"",
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
stderr: "",
|
stderr: "",
|
||||||
@@ -65,7 +65,7 @@ describe("bonjour-discovery", () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
stdout: [
|
stdout: [
|
||||||
`${instance}._clawdbot-gateway._tcp. can be reached at ${host}:18789`,
|
`${instance}._clawdbot-gw._tcp. can be reached at ${host}:18789`,
|
||||||
txtParts.join(" "),
|
txtParts.join(" "),
|
||||||
"",
|
"",
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
@@ -112,7 +112,7 @@ describe("bonjour-discovery", () => {
|
|||||||
const domain = argv[3] ?? "";
|
const domain = argv[3] ?? "";
|
||||||
if (argv[0] === "dns-sd" && argv[1] === "-B" && domain === "local.") {
|
if (argv[0] === "dns-sd" && argv[1] === "-B" && domain === "local.") {
|
||||||
return {
|
return {
|
||||||
stdout: ["Add 2 3 local. _clawdbot-gateway._tcp. Studio Gateway", ""].join("\n"),
|
stdout: ["Add 2 3 local. _clawdbot-gw._tcp. Studio Gateway", ""].join("\n"),
|
||||||
stderr: "",
|
stderr: "",
|
||||||
code: 0,
|
code: 0,
|
||||||
signal: null,
|
signal: null,
|
||||||
@@ -123,7 +123,7 @@ describe("bonjour-discovery", () => {
|
|||||||
if (argv[0] === "dns-sd" && argv[1] === "-L") {
|
if (argv[0] === "dns-sd" && argv[1] === "-L") {
|
||||||
return {
|
return {
|
||||||
stdout: [
|
stdout: [
|
||||||
"Studio Gateway._clawdbot-gateway._tcp. can be reached at studio.local:18789",
|
"Studio Gateway._clawdbot-gw._tcp. can be reached at studio.local:18789",
|
||||||
"txtvers=1 displayName=Peter\\226\\128\\153s\\032Mac\\032Studio lanHost=studio.local gatewayPort=18789 sshPort=22",
|
"txtvers=1 displayName=Peter\\226\\128\\153s\\032Mac\\032Studio lanHost=studio.local gatewayPort=18789 sshPort=22",
|
||||||
"",
|
"",
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
@@ -203,10 +203,10 @@ describe("bonjour-discovery", () => {
|
|||||||
if (
|
if (
|
||||||
server === "100.123.224.76" &&
|
server === "100.123.224.76" &&
|
||||||
qtype === "PTR" &&
|
qtype === "PTR" &&
|
||||||
qname === "_clawdbot-gateway._tcp.clawdbot.internal"
|
qname === "_clawdbot-gw._tcp.clawdbot.internal"
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
stdout: `studio-gateway._clawdbot-gateway._tcp.clawdbot.internal.\n`,
|
stdout: `studio-gateway._clawdbot-gw._tcp.clawdbot.internal.\n`,
|
||||||
stderr: "",
|
stderr: "",
|
||||||
code: 0,
|
code: 0,
|
||||||
signal: null,
|
signal: null,
|
||||||
@@ -217,7 +217,7 @@ describe("bonjour-discovery", () => {
|
|||||||
if (
|
if (
|
||||||
server === "100.123.224.76" &&
|
server === "100.123.224.76" &&
|
||||||
qtype === "SRV" &&
|
qtype === "SRV" &&
|
||||||
qname === "studio-gateway._clawdbot-gateway._tcp.clawdbot.internal"
|
qname === "studio-gateway._clawdbot-gw._tcp.clawdbot.internal"
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
stdout: `0 0 18789 studio.clawdbot.internal.\n`,
|
stdout: `0 0 18789 studio.clawdbot.internal.\n`,
|
||||||
@@ -231,7 +231,7 @@ describe("bonjour-discovery", () => {
|
|||||||
if (
|
if (
|
||||||
server === "100.123.224.76" &&
|
server === "100.123.224.76" &&
|
||||||
qtype === "TXT" &&
|
qtype === "TXT" &&
|
||||||
qname === "studio-gateway._clawdbot-gateway._tcp.clawdbot.internal"
|
qname === "studio-gateway._clawdbot-gw._tcp.clawdbot.internal"
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
stdout: [
|
stdout: [
|
||||||
|
|||||||
@@ -166,9 +166,9 @@ function parseDnsSdBrowse(stdout: string): string[] {
|
|||||||
const instances = new Set<string>();
|
const instances = new Set<string>();
|
||||||
for (const raw of stdout.split("\n")) {
|
for (const raw of stdout.split("\n")) {
|
||||||
const line = raw.trim();
|
const line = raw.trim();
|
||||||
if (!line || !line.includes("_clawdbot-gateway._tcp")) continue;
|
if (!line || !line.includes("_clawdbot-gw._tcp")) continue;
|
||||||
if (!line.includes("Add")) continue;
|
if (!line.includes("Add")) continue;
|
||||||
const match = line.match(/_clawdbot-gateway\._tcp\.?\s+(.+)$/);
|
const match = line.match(/_clawdbot-gw\._tcp\.?\s+(.+)$/);
|
||||||
if (match?.[1]) {
|
if (match?.[1]) {
|
||||||
instances.add(decodeDnsSdEscapes(match[1].trim()));
|
instances.add(decodeDnsSdEscapes(match[1].trim()));
|
||||||
}
|
}
|
||||||
@@ -225,13 +225,13 @@ async function discoverViaDnsSd(
|
|||||||
timeoutMs: number,
|
timeoutMs: number,
|
||||||
run: typeof runCommandWithTimeout,
|
run: typeof runCommandWithTimeout,
|
||||||
): Promise<GatewayBonjourBeacon[]> {
|
): Promise<GatewayBonjourBeacon[]> {
|
||||||
const browse = await run(["dns-sd", "-B", "_clawdbot-gateway._tcp", domain], {
|
const browse = await run(["dns-sd", "-B", "_clawdbot-gw._tcp", domain], {
|
||||||
timeoutMs,
|
timeoutMs,
|
||||||
});
|
});
|
||||||
const instances = parseDnsSdBrowse(browse.stdout);
|
const instances = parseDnsSdBrowse(browse.stdout);
|
||||||
const results: GatewayBonjourBeacon[] = [];
|
const results: GatewayBonjourBeacon[] = [];
|
||||||
for (const instance of instances) {
|
for (const instance of instances) {
|
||||||
const resolved = await run(["dns-sd", "-L", instance, "_clawdbot-gateway._tcp", domain], {
|
const resolved = await run(["dns-sd", "-L", instance, "_clawdbot-gw._tcp", domain], {
|
||||||
timeoutMs,
|
timeoutMs,
|
||||||
});
|
});
|
||||||
const parsed = parseDnsSdResolve(resolved.stdout, instance);
|
const parsed = parseDnsSdResolve(resolved.stdout, instance);
|
||||||
@@ -268,7 +268,7 @@ async function discoverWideAreaViaTailnetDns(
|
|||||||
// Keep scans bounded: this is a fallback and should not block long.
|
// Keep scans bounded: this is a fallback and should not block long.
|
||||||
ips = ips.slice(0, 40);
|
ips = ips.slice(0, 40);
|
||||||
|
|
||||||
const probeName = `_clawdbot-gateway._tcp.${domain.replace(/\.$/, "")}`;
|
const probeName = `_clawdbot-gw._tcp.${domain.replace(/\.$/, "")}`;
|
||||||
|
|
||||||
const concurrency = 6;
|
const concurrency = 6;
|
||||||
let nextIndex = 0;
|
let nextIndex = 0;
|
||||||
@@ -312,7 +312,7 @@ async function discoverWideAreaViaTailnetDns(
|
|||||||
if (budget <= 0) break;
|
if (budget <= 0) break;
|
||||||
const ptrName = ptr.trim().replace(/\.$/, "");
|
const ptrName = ptr.trim().replace(/\.$/, "");
|
||||||
if (!ptrName) continue;
|
if (!ptrName) continue;
|
||||||
const instanceName = ptrName.replace(/\.?_clawdbot-gateway\._tcp\..*$/, "");
|
const instanceName = ptrName.replace(/\.?_clawdbot-gw\._tcp\..*$/, "");
|
||||||
|
|
||||||
const srv = await run(["dig", "+short", "+time=1", "+tries=1", nameserverArg, ptrName, "SRV"], {
|
const srv = await run(["dig", "+short", "+time=1", "+tries=1", nameserverArg, ptrName, "SRV"], {
|
||||||
timeoutMs: Math.max(1, Math.min(350, budget)),
|
timeoutMs: Math.max(1, Math.min(350, budget)),
|
||||||
@@ -371,9 +371,9 @@ function parseAvahiBrowse(stdout: string): GatewayBonjourBeacon[] {
|
|||||||
for (const raw of stdout.split("\n")) {
|
for (const raw of stdout.split("\n")) {
|
||||||
const line = raw.trimEnd();
|
const line = raw.trimEnd();
|
||||||
if (!line) continue;
|
if (!line) continue;
|
||||||
if (line.startsWith("=") && line.includes("_clawdbot-gateway._tcp")) {
|
if (line.startsWith("=") && line.includes("_clawdbot-gw._tcp")) {
|
||||||
if (current) results.push(current);
|
if (current) results.push(current);
|
||||||
const marker = " _clawdbot-gateway._tcp";
|
const marker = " _clawdbot-gw._tcp";
|
||||||
const idx = line.indexOf(marker);
|
const idx = line.indexOf(marker);
|
||||||
const left = idx >= 0 ? line.slice(0, idx).trim() : line;
|
const left = idx >= 0 ? line.slice(0, idx).trim() : line;
|
||||||
const parts = left.split(/\s+/);
|
const parts = left.split(/\s+/);
|
||||||
@@ -429,7 +429,7 @@ async function discoverViaAvahi(
|
|||||||
timeoutMs: number,
|
timeoutMs: number,
|
||||||
run: typeof runCommandWithTimeout,
|
run: typeof runCommandWithTimeout,
|
||||||
): Promise<GatewayBonjourBeacon[]> {
|
): Promise<GatewayBonjourBeacon[]> {
|
||||||
const args = ["avahi-browse", "-rt", "_clawdbot-gateway._tcp"];
|
const args = ["avahi-browse", "-rt", "_clawdbot-gw._tcp"];
|
||||||
if (domain && domain !== "local.") {
|
if (domain && domain !== "local.") {
|
||||||
// avahi-browse wants a plain domain (no trailing dot)
|
// avahi-browse wants a plain domain (no trailing dot)
|
||||||
args.push("-d", domain.replace(/\.$/, ""));
|
args.push("-d", domain.replace(/\.$/, ""));
|
||||||
|
|||||||
@@ -116,7 +116,8 @@ describe("gateway bonjour advertiser", () => {
|
|||||||
|
|
||||||
expect(createService).toHaveBeenCalledTimes(1);
|
expect(createService).toHaveBeenCalledTimes(1);
|
||||||
const [gatewayCall] = createService.mock.calls as Array<[Record<string, unknown>]>;
|
const [gatewayCall] = createService.mock.calls as Array<[Record<string, unknown>]>;
|
||||||
expect(gatewayCall?.[0]?.type).toBe("clawdbot-gateway");
|
expect(gatewayCall?.[0]?.type).toBe("clawdbot-gw");
|
||||||
|
expect(String(gatewayCall?.[0]?.type ?? "").length).toBeLessThanOrEqual(15);
|
||||||
expect(gatewayCall?.[0]?.port).toBe(18789);
|
expect(gatewayCall?.[0]?.port).toBe(18789);
|
||||||
expect(gatewayCall?.[0]?.domain).toBe("local");
|
expect(gatewayCall?.[0]?.domain).toBe("local");
|
||||||
expect(gatewayCall?.[0]?.hostname).toBe("test-host");
|
expect(gatewayCall?.[0]?.hostname).toBe("test-host");
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ export async function startGatewayBonjourAdvertiser(
|
|||||||
|
|
||||||
const gateway = responder.createService({
|
const gateway = responder.createService({
|
||||||
name: safeServiceName(instanceName),
|
name: safeServiceName(instanceName),
|
||||||
type: "clawdbot-gateway",
|
type: "clawdbot-gw",
|
||||||
protocol: Protocol.TCP,
|
protocol: Protocol.TCP,
|
||||||
port: opts.gatewayPort,
|
port: opts.gatewayPort,
|
||||||
domain: "local",
|
domain: "local",
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ describe("wide-area DNS-SD zone rendering", () => {
|
|||||||
expect(txt).toContain(`$ORIGIN ${WIDE_AREA_DISCOVERY_DOMAIN}`);
|
expect(txt).toContain(`$ORIGIN ${WIDE_AREA_DISCOVERY_DOMAIN}`);
|
||||||
expect(txt).toContain(`studio-london IN A 100.123.224.76`);
|
expect(txt).toContain(`studio-london IN A 100.123.224.76`);
|
||||||
expect(txt).toContain(`studio-london IN AAAA fd7a:115c:a1e0::8801:e04c`);
|
expect(txt).toContain(`studio-london IN AAAA fd7a:115c:a1e0::8801:e04c`);
|
||||||
expect(txt).toContain(`_clawdbot-gateway._tcp IN PTR studio-london._clawdbot-gateway._tcp`);
|
expect(txt).toContain(`_clawdbot-gw._tcp IN PTR studio-london._clawdbot-gw._tcp`);
|
||||||
expect(txt).toContain(`studio-london._clawdbot-gateway._tcp IN SRV 0 0 18789 studio-london`);
|
expect(txt).toContain(`studio-london._clawdbot-gw._tcp IN SRV 0 0 18789 studio-london`);
|
||||||
expect(txt).toContain(`displayName=Mac Studio (Clawdbot)`);
|
expect(txt).toContain(`displayName=Mac Studio (Clawdbot)`);
|
||||||
expect(txt).toContain(`gatewayPort=18789`);
|
expect(txt).toContain(`gatewayPort=18789`);
|
||||||
expect(txt).toContain(`sshPort=22`);
|
expect(txt).toContain(`sshPort=22`);
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export type WideAreaGatewayZoneOpts = {
|
|||||||
function renderZone(opts: WideAreaGatewayZoneOpts & { serial: number }): string {
|
function renderZone(opts: WideAreaGatewayZoneOpts & { serial: number }): string {
|
||||||
const hostname = os.hostname().split(".")[0] ?? "clawdbot";
|
const hostname = os.hostname().split(".")[0] ?? "clawdbot";
|
||||||
const hostLabel = dnsLabel(opts.hostLabel ?? hostname, "clawdbot");
|
const hostLabel = dnsLabel(opts.hostLabel ?? hostname, "clawdbot");
|
||||||
const instanceLabel = dnsLabel(opts.instanceLabel ?? `${hostname}-gateway`, "clawdbot-gateway");
|
const instanceLabel = dnsLabel(opts.instanceLabel ?? `${hostname}-gateway`, "clawdbot-gw");
|
||||||
|
|
||||||
const txt = [
|
const txt = [
|
||||||
`displayName=${opts.displayName.trim() || hostname}`,
|
`displayName=${opts.displayName.trim() || hostname}`,
|
||||||
@@ -119,11 +119,9 @@ function renderZone(opts: WideAreaGatewayZoneOpts & { serial: number }): string
|
|||||||
records.push(`${hostLabel} IN AAAA ${opts.tailnetIPv6}`);
|
records.push(`${hostLabel} IN AAAA ${opts.tailnetIPv6}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
records.push(`_clawdbot-gateway._tcp IN PTR ${instanceLabel}._clawdbot-gateway._tcp`);
|
records.push(`_clawdbot-gw._tcp IN PTR ${instanceLabel}._clawdbot-gw._tcp`);
|
||||||
records.push(
|
records.push(`${instanceLabel}._clawdbot-gw._tcp IN SRV 0 0 ${opts.gatewayPort} ${hostLabel}`);
|
||||||
`${instanceLabel}._clawdbot-gateway._tcp IN SRV 0 0 ${opts.gatewayPort} ${hostLabel}`,
|
records.push(`${instanceLabel}._clawdbot-gw._tcp IN TXT ${txt.map(txtQuote).join(" ")}`);
|
||||||
);
|
|
||||||
records.push(`${instanceLabel}._clawdbot-gateway._tcp IN TXT ${txt.map(txtQuote).join(" ")}`);
|
|
||||||
|
|
||||||
const contentBody = `${records.join("\n")}\n`;
|
const contentBody = `${records.join("\n")}\n`;
|
||||||
const hashBody = `${records
|
const hashBody = `${records
|
||||||
|
|||||||
Reference in New Issue
Block a user