@le-space/rootfs 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,23 @@
1
+ [Unit]
2
+ Description=Persist AutoTLS secure announce addresses for uc-go-peer
3
+ After=uc-go-peer.service
4
+ Wants=uc-go-peer.service
5
+ ConditionPathExists=/etc/default/uc-go-peer.ready
6
+ ConditionPathExists=!/etc/default/uc-go-peer.autotls-ready
7
+
8
+ [Service]
9
+ Type=oneshot
10
+ Environment=ENV_FILE=/etc/default/uc-go-peer
11
+ Environment=READY_FILE=/etc/default/uc-go-peer.ready
12
+ Environment=AUTOTLS_READY_FILE=/etc/default/uc-go-peer.autotls-ready
13
+ Environment=AUTOTLS_ZONE_FILE=/etc/default/uc-go-peer.autotls-zone
14
+ Environment=AUTOTLS_HOSTS_FILE=/etc/default/uc-go-peer.autotls-hosts
15
+ Environment=AUTOTLS_CADDY_READY_FILE=/etc/default/uc-go-peer.caddy-ready
16
+ Environment=SERVICE_NAME=uc-go-peer.service
17
+ Environment=WS_BACKEND_PORT=9097
18
+ ExecStart=/usr/bin/python3 /usr/local/sbin/uc-go-peer-autotls-refresh.py
19
+ StandardOutput=journal
20
+ StandardError=journal
21
+
22
+ [Install]
23
+ WantedBy=multi-user.target
@@ -0,0 +1,17 @@
1
+ [Unit]
2
+ Description=Temporary universal-connectivity go-peer setup endpoint
3
+ Wants=network-online.target
4
+ After=network-online.target
5
+ ConditionPathExists=!/etc/default/uc-go-peer.ready
6
+
7
+ [Service]
8
+ Environment=ENV_FILE=/etc/default/uc-go-peer
9
+ Environment=READY_FILE=/etc/default/uc-go-peer.ready
10
+ ExecStart=/usr/bin/python3 /usr/local/sbin/uc-go-peer-setup-server.py
11
+ StandardOutput=journal
12
+ StandardError=journal
13
+ Restart=on-failure
14
+ RestartSec=2s
15
+
16
+ [Install]
17
+ WantedBy=multi-user.target
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ set -x
4
+
5
+ INSTALL_DIR="${INSTALL_DIR:-/opt/go-peer}"
6
+ SERVICE_USER="${SERVICE_USER:-uc-go-peer}"
7
+ DATA_DIR="${DATA_DIR:-/var/lib/uc-go-peer}"
8
+ ENV_FILE="${ENV_FILE:-/etc/default/uc-go-peer}"
9
+ READY_FILE="${READY_FILE:-/etc/default/uc-go-peer.ready}"
10
+ AUTOTLS_READY_FILE="${AUTOTLS_READY_FILE:-/etc/default/uc-go-peer.autotls-ready}"
11
+ AUTOTLS_ZONE_FILE="${AUTOTLS_ZONE_FILE:-/etc/default/uc-go-peer.autotls-zone}"
12
+ AUTOTLS_HOSTS_FILE="${AUTOTLS_HOSTS_FILE:-/etc/default/uc-go-peer.autotls-hosts}"
13
+ AUTOTLS_CADDY_READY_FILE="${AUTOTLS_CADDY_READY_FILE:-/etc/default/uc-go-peer.caddy-ready}"
14
+ APP_BINARY="${APP_BINARY:-/usr/local/bin/universal-chat-go}"
15
+ PHASE="${1:-all}"
16
+
17
+ if [ ! -d "${INSTALL_DIR}" ]; then
18
+ echo "Missing ${INSTALL_DIR}; the rootfs build did not create the relay support directory."
19
+ exit 1
20
+ fi
21
+
22
+ echo "[uc-go-peer-bootstrap] starting"
23
+ echo "[uc-go-peer-bootstrap] support dir: ${INSTALL_DIR}"
24
+ echo "[uc-go-peer-bootstrap] data dir: ${DATA_DIR}"
25
+ echo "[uc-go-peer-bootstrap] env file: ${ENV_FILE}"
26
+ echo "[uc-go-peer-bootstrap] app binary: ${APP_BINARY}"
27
+
28
+ run_phase_base() {
29
+ export DEBIAN_FRONTEND=noninteractive
30
+ echo "[uc-go-peer-bootstrap] phase=base"
31
+ echo "[uc-go-peer-bootstrap] running apt-get update"
32
+ apt-get update
33
+ echo "[uc-go-peer-bootstrap] installing base packages"
34
+ apt-get install -y ca-certificates curl caddy
35
+ rm -rf /var/lib/apt/lists/*
36
+ }
37
+
38
+ run_phase_build() {
39
+ echo "[uc-go-peer-bootstrap] phase=build"
40
+ if [ ! -x "${APP_BINARY}" ]; then
41
+ echo "[uc-go-peer-bootstrap] missing application binary: ${APP_BINARY}"
42
+ exit 1
43
+ fi
44
+ echo "[uc-go-peer-bootstrap] application binary already provisioned"
45
+ }
46
+
47
+ run_phase_finalize() {
48
+ echo "[uc-go-peer-bootstrap] phase=finalize"
49
+ echo "[uc-go-peer-bootstrap] creating runtime directories"
50
+ mkdir -p "${DATA_DIR}" "$(dirname "${ENV_FILE}")"
51
+
52
+ if ! id "${SERVICE_USER}" >/dev/null 2>&1; then
53
+ echo "[uc-go-peer-bootstrap] creating service user ${SERVICE_USER}"
54
+ useradd --system --home "${DATA_DIR}" --create-home --shell /usr/sbin/nologin "${SERVICE_USER}"
55
+ fi
56
+
57
+ echo "[uc-go-peer-bootstrap] fixing ownership"
58
+ chown -R "${SERVICE_USER}:${SERVICE_USER}" "${DATA_DIR}" "${INSTALL_DIR}"
59
+
60
+ echo "[uc-go-peer-bootstrap] preparing environment file"
61
+ touch "${ENV_FILE}"
62
+ chmod 0640 "${ENV_FILE}"
63
+ chown "root:${SERVICE_USER}" "${ENV_FILE}"
64
+ rm -f "${READY_FILE}" "${AUTOTLS_READY_FILE}" "${AUTOTLS_ZONE_FILE}" "${AUTOTLS_HOSTS_FILE}" "${AUTOTLS_CADDY_READY_FILE}"
65
+
66
+ mkdir -p /etc/caddy /etc/systemd/system/caddy.service.d
67
+ cat > /etc/systemd/system/caddy.service.d/uc-go-peer.conf <<EOF
68
+ [Unit]
69
+ ConditionPathExists=${AUTOTLS_CADDY_READY_FILE}
70
+ EOF
71
+ }
72
+
73
+ write_env_var() {
74
+ local key="$1"
75
+ local value="$2"
76
+
77
+ if grep -Eq "^[#[:space:]]*${key}=" "${ENV_FILE}"; then
78
+ sed -i "s|^[#[:space:]]*${key}=.*|${key}=${value}|" "${ENV_FILE}"
79
+ else
80
+ printf '%s=%s\n' "${key}" "${value}" >> "${ENV_FILE}"
81
+ fi
82
+ }
83
+
84
+ seed_env() {
85
+ write_env_var "GO_PEER_DATA_DIR" "${DATA_DIR}"
86
+ write_env_var "GO_PEER_TCP_PORT" "9095"
87
+ write_env_var "GO_PEER_WS_PORT" "9096"
88
+ write_env_var "GO_PEER_WSS_PORT" "9097"
89
+ write_env_var "GO_PEER_IDENTITY_PATH" "${DATA_DIR}/identity.key"
90
+ write_env_var "GO_PEER_WS_BACKEND_PORT" "9096"
91
+ write_env_var "GO_PEER_AUTOTLS_CERT_DIR" "${DATA_DIR}/p2p-forge-certs"
92
+ write_env_var "LIBP2P_AUTO_PUBLIC_IP" "0"
93
+ }
94
+
95
+ case "${PHASE}" in
96
+ base)
97
+ run_phase_base
98
+ ;;
99
+ build)
100
+ run_phase_build
101
+ ;;
102
+ finalize)
103
+ run_phase_finalize
104
+ seed_env
105
+ ;;
106
+ all)
107
+ run_phase_base
108
+ run_phase_build
109
+ run_phase_finalize
110
+ seed_env
111
+ ;;
112
+ *)
113
+ echo "Unknown phase: ${PHASE}" >&2
114
+ exit 1
115
+ ;;
116
+ esac
117
+
118
+ echo "[uc-go-peer-bootstrap] completed phase ${PHASE}"
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ENV_FILE="${ENV_FILE:-/etc/default/uc-go-peer}"
5
+ READY_FILE="${READY_FILE:-/etc/default/uc-go-peer.ready}"
6
+ AUTOTLS_READY_FILE="${AUTOTLS_READY_FILE:-/etc/default/uc-go-peer.autotls-ready}"
7
+ AUTOTLS_ZONE_FILE="${AUTOTLS_ZONE_FILE:-/etc/default/uc-go-peer.autotls-zone}"
8
+ AUTOTLS_HOSTS_FILE="${AUTOTLS_HOSTS_FILE:-/etc/default/uc-go-peer.autotls-hosts}"
9
+ AUTOTLS_CADDY_READY_FILE="${AUTOTLS_CADDY_READY_FILE:-/etc/default/uc-go-peer.caddy-ready}"
10
+ SERVICE_NAME="${SERVICE_NAME:-uc-go-peer.service}"
11
+ AUTOTLS_REFRESH_SERVICE="${AUTOTLS_REFRESH_SERVICE:-uc-go-peer-autotls-refresh.service}"
12
+ CADDY_SERVICE="${CADDY_SERVICE:-caddy.service}"
13
+ CADDYFILE="${CADDYFILE:-/etc/caddy/Caddyfile}"
14
+ BOOTSTRAP_SERVICE="${BOOTSTRAP_SERVICE:-uc-go-peer-bootstrap.service}"
15
+ P2P_FORGE_DOMAIN="${P2P_FORGE_DOMAIN:-libp2p.direct}"
16
+ PUBLIC_IPV4=""
17
+ PUBLIC_IPV6=""
18
+ TCP_PORT="9095"
19
+ WS_PORT="9097"
20
+ WS_BACKEND_PORT="9096"
21
+ PROXY_HOSTNAME=""
22
+ UDP_PORT=""
23
+ START_SERVICE=1
24
+
25
+ usage() {
26
+ cat <<'EOF'
27
+ Usage:
28
+ uc-go-peer-configure.sh \
29
+ --public-ipv4 <ip> \
30
+ [--public-ipv6 <ipv6>] \
31
+ [--proxy-hostname <hostname>] \
32
+ [--tcp-port <host-port>] \
33
+ [--ws-port <host-port>] \
34
+ [--udp-port <host-port>] \
35
+ [--quic-port <host-port>] \
36
+ [--webtransport-port <host-port>] \
37
+ [--webrtc-port <host-port>] \
38
+ [--no-start]
39
+ EOF
40
+ }
41
+
42
+ write_env_var() {
43
+ local key="$1"
44
+ local value="$2"
45
+
46
+ if grep -Eq "^[#[:space:]]*${key}=" "${ENV_FILE}"; then
47
+ sed -i "s|^[#[:space:]]*${key}=.*|${key}=${value}|" "${ENV_FILE}"
48
+ else
49
+ printf '%s=%s\n' "${key}" "${value}" >> "${ENV_FILE}"
50
+ fi
51
+ }
52
+
53
+ render_proxy_caddyfile() {
54
+ local proxy_hostname="$1"
55
+ cat <<EOF
56
+ {
57
+ auto_https disable_redirects
58
+ }
59
+
60
+ https://${proxy_hostname} {
61
+ tls {
62
+ issuer acme {
63
+ disable_http_challenge
64
+ }
65
+ }
66
+ reverse_proxy http://127.0.0.1:${WS_BACKEND_PORT}
67
+ }
68
+ EOF
69
+ }
70
+
71
+ while [ "$#" -gt 0 ]; do
72
+ case "$1" in
73
+ --public-ipv4)
74
+ PUBLIC_IPV4="${2:-}"
75
+ shift 2
76
+ ;;
77
+ --public-ipv6)
78
+ PUBLIC_IPV6="${2:-}"
79
+ shift 2
80
+ ;;
81
+ --tcp-port)
82
+ TCP_PORT="${2:-}"
83
+ shift 2
84
+ ;;
85
+ --ws-port)
86
+ WS_PORT="${2:-}"
87
+ shift 2
88
+ ;;
89
+ --udp-port)
90
+ UDP_PORT="${2:-}"
91
+ shift 2
92
+ ;;
93
+ --proxy-hostname)
94
+ PROXY_HOSTNAME="${2:-}"
95
+ shift 2
96
+ ;;
97
+ --quic-port)
98
+ UDP_PORT="${2:-}"
99
+ shift 2
100
+ ;;
101
+ --webtransport-port)
102
+ UDP_PORT="${2:-}"
103
+ shift 2
104
+ ;;
105
+ --webrtc-port)
106
+ UDP_PORT="${2:-}"
107
+ shift 2
108
+ ;;
109
+ --no-start)
110
+ START_SERVICE=0
111
+ shift
112
+ ;;
113
+ -h|--help)
114
+ usage
115
+ exit 0
116
+ ;;
117
+ *)
118
+ echo "Unknown argument: $1" >&2
119
+ usage >&2
120
+ exit 1
121
+ ;;
122
+ esac
123
+ done
124
+
125
+ if [ -z "${PUBLIC_IPV4}" ]; then
126
+ usage >&2
127
+ exit 1
128
+ fi
129
+
130
+ touch "${ENV_FILE}"
131
+ rm -f "${AUTOTLS_READY_FILE}" "${AUTOTLS_ZONE_FILE}" "${AUTOTLS_HOSTS_FILE}" "${AUTOTLS_CADDY_READY_FILE}"
132
+
133
+ announce=(
134
+ "/ip4/${PUBLIC_IPV4}/tcp/${TCP_PORT}"
135
+ )
136
+
137
+ if [ -n "${WS_PORT}" ]; then
138
+ announce+=("/ip4/${PUBLIC_IPV4}/tcp/${WS_PORT}/tls/sni/*.${P2P_FORGE_DOMAIN}/ws")
139
+ fi
140
+
141
+ if [ -n "${UDP_PORT}" ]; then
142
+ announce+=(
143
+ "/ip4/${PUBLIC_IPV4}/udp/${UDP_PORT}/quic-v1"
144
+ "/ip4/${PUBLIC_IPV4}/udp/${UDP_PORT}/quic-v1/webtransport"
145
+ "/ip4/${PUBLIC_IPV4}/udp/${UDP_PORT}/webrtc-direct"
146
+ )
147
+ fi
148
+
149
+ if [ -n "${PUBLIC_IPV6}" ]; then
150
+ announce+=("/ip6/${PUBLIC_IPV6}/tcp/${TCP_PORT}")
151
+ if [ -n "${WS_PORT}" ]; then
152
+ announce+=("/ip6/${PUBLIC_IPV6}/tcp/${WS_PORT}/tls/sni/*.${P2P_FORGE_DOMAIN}/ws")
153
+ fi
154
+ if [ -n "${UDP_PORT}" ]; then
155
+ announce+=(
156
+ "/ip6/${PUBLIC_IPV6}/udp/${UDP_PORT}/quic-v1"
157
+ "/ip6/${PUBLIC_IPV6}/udp/${UDP_PORT}/quic-v1/webtransport"
158
+ "/ip6/${PUBLIC_IPV6}/udp/${UDP_PORT}/webrtc-direct"
159
+ )
160
+ fi
161
+ fi
162
+
163
+ announce_value="$(IFS=,; printf '%s' "${announce[*]}")"
164
+ write_env_var "PUBLIC_IPV4" "${PUBLIC_IPV4}"
165
+ if [ -n "${PUBLIC_IPV6}" ]; then
166
+ write_env_var "PUBLIC_IPV6" "${PUBLIC_IPV6}"
167
+ fi
168
+ write_env_var "EXTERNAL_RELAY_TCP_PORT" "${TCP_PORT}"
169
+ write_env_var "EXTERNAL_RELAY_WS_PORT" "${WS_PORT}"
170
+ write_env_var "GO_PEER_WSS_PORT" "${GO_PEER_WSS_PORT:-9097}"
171
+ if [ -n "${PROXY_HOSTNAME}" ]; then
172
+ write_env_var "PROXY_HOSTNAME" "${PROXY_HOSTNAME}"
173
+ mkdir -p "$(dirname "${CADDYFILE}")"
174
+ render_proxy_caddyfile "${PROXY_HOSTNAME}" > "${CADDYFILE}"
175
+ touch "${AUTOTLS_CADDY_READY_FILE}"
176
+ else
177
+ write_env_var "PROXY_HOSTNAME" ""
178
+ rm -f "${AUTOTLS_CADDY_READY_FILE}"
179
+ fi
180
+ if [ -n "${UDP_PORT}" ]; then
181
+ write_env_var "EXTERNAL_RELAY_UDP_PORT" "${UDP_PORT}"
182
+ write_env_var "EXTERNAL_RELAY_QUIC_PORT" "${UDP_PORT}"
183
+ write_env_var "EXTERNAL_RELAY_WEBTRANSPORT_PORT" "${UDP_PORT}"
184
+ write_env_var "EXTERNAL_RELAY_WEBRTC_PORT" "${UDP_PORT}"
185
+ fi
186
+ write_env_var "LIBP2P_ANNOUNCE_ADDRS" "${announce_value}"
187
+ touch "${READY_FILE}"
188
+
189
+ if [ "${START_SERVICE}" -eq 1 ]; then
190
+ systemctl daemon-reload
191
+ systemctl enable "${SERVICE_NAME}"
192
+ systemctl restart "${SERVICE_NAME}"
193
+ systemctl enable "${AUTOTLS_REFRESH_SERVICE}"
194
+ if [ -n "${PROXY_HOSTNAME}" ]; then
195
+ systemctl enable "${CADDY_SERVICE}"
196
+ systemctl restart "${CADDY_SERVICE}"
197
+ else
198
+ systemctl stop "${CADDY_SERVICE}" || true
199
+ fi
200
+ systemctl restart --no-block "${AUTOTLS_REFRESH_SERVICE}"
201
+ fi
202
+
203
+ printf 'Configured LIBP2P_ANNOUNCE_ADDRS=%s\n' "${announce_value}"
204
+ printf 'Ready file: %s\n' "${READY_FILE}"
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import os
4
+ import re
5
+ import subprocess
6
+ import time
7
+
8
+
9
+ ENV_FILE = os.environ.get("ENV_FILE", "/etc/default/uc-go-peer")
10
+ SERVICE_NAME = os.environ.get("SERVICE_NAME", "uc-go-peer.service")
11
+ WAIT_TIMEOUT_SECONDS = int(os.environ.get("DESCRIBE_WAIT_TIMEOUT_SECONDS", "240"))
12
+ WAIT_INTERVAL_SECONDS = float(os.environ.get("DESCRIBE_WAIT_INTERVAL_SECONDS", "2"))
13
+ AUTOTLS_EXTRA_WAIT_SECONDS = int(os.environ.get("DESCRIBE_AUTOTLS_EXTRA_WAIT_SECONDS", "120"))
14
+
15
+ PEER_ID_PATTERNS = [
16
+ re.compile(r"PeerID:\s+(\S+)"),
17
+ re.compile(r"Host created with PeerID:\s+(\S+)"),
18
+ ]
19
+ LISTENING_PATTERN = re.compile(r"Listening on:\s+(\S+)/p2p/(\S+)")
20
+
21
+
22
+ def parse_env_file(path: str) -> dict[str, str]:
23
+ values: dict[str, str] = {}
24
+ if not os.path.exists(path):
25
+ return values
26
+
27
+ with open(path, encoding="utf-8") as handle:
28
+ for line in handle:
29
+ stripped = line.strip()
30
+ if not stripped or stripped.startswith("#") or "=" not in stripped:
31
+ continue
32
+ key, value = stripped.split("=", 1)
33
+ values[key.strip()] = value.strip()
34
+ return values
35
+
36
+
37
+ def dedupe(values: list[str]) -> list[str]:
38
+ seen: set[str] = set()
39
+ result: list[str] = []
40
+ for value in values:
41
+ if value and value not in seen:
42
+ seen.add(value)
43
+ result.append(value)
44
+ return result
45
+
46
+
47
+ def append_peer_id(addr: str, peer_id: str) -> str:
48
+ return addr if "/p2p/" in addr else f"{addr}/p2p/{peer_id}"
49
+
50
+
51
+ def parse_logs() -> tuple[str | None, list[str]]:
52
+ result = subprocess.run(
53
+ ["journalctl", "-u", SERVICE_NAME, "-n", "500", "--no-pager"],
54
+ capture_output=True,
55
+ text=True,
56
+ check=False,
57
+ )
58
+ output = result.stdout or ""
59
+
60
+ peer_id = None
61
+ for pattern in PEER_ID_PATTERNS:
62
+ match = pattern.search(output)
63
+ if match:
64
+ peer_id = match.group(1)
65
+ break
66
+
67
+ listening_addrs = []
68
+ for addr, logged_peer_id in LISTENING_PATTERN.findall(output):
69
+ if peer_id is None:
70
+ peer_id = logged_peer_id
71
+ listening_addrs.append(addr)
72
+
73
+ return peer_id, dedupe(listening_addrs)
74
+
75
+
76
+ def build_probe_multiaddrs(env_values: dict[str, str], peer_id: str, listening_addrs: list[str]) -> dict[str, list[str]]:
77
+ announce_addrs = [
78
+ entry.strip()
79
+ for entry in env_values.get("LIBP2P_ANNOUNCE_ADDRS", "").split(",")
80
+ if entry.strip()
81
+ ]
82
+ probe_multiaddrs: list[str] = []
83
+ direct_tcp_multiaddrs: list[str] = []
84
+ autotls_multiaddrs: list[str] = []
85
+ proxy_multiaddrs: list[str] = []
86
+ webtransport_multiaddrs: list[str] = []
87
+ webrtc_direct_multiaddrs: list[str] = []
88
+
89
+ for addr in announce_addrs:
90
+ if "/tcp/" in addr and "/tls/" not in addr and "/ws" not in addr:
91
+ direct_tcp_multiaddrs.append(append_peer_id(addr, peer_id))
92
+
93
+ ws_port = env_values.get("EXTERNAL_RELAY_WS_PORT", "").strip()
94
+ for addr in listening_addrs:
95
+ if "/tls/" not in addr or not addr.endswith("/ws"):
96
+ continue
97
+
98
+ dns_match = re.search(r"/dns[46]/([^/]+)/tcp/(\d+)/tls/ws$", addr)
99
+ if dns_match:
100
+ host = dns_match.group(1)
101
+ autotls_multiaddrs.append(f"/dns4/{host}/tcp/{ws_port or dns_match.group(2)}/tls/ws/p2p/{peer_id}")
102
+ autotls_multiaddrs.append(f"/dns6/{host}/tcp/{ws_port or dns_match.group(2)}/tls/ws/p2p/{peer_id}")
103
+ continue
104
+
105
+ sni_match = re.search(r"/tls/sni/([^/]+)/ws$", addr)
106
+ if sni_match:
107
+ host = sni_match.group(1)
108
+ if ws_port:
109
+ autotls_multiaddrs.append(f"/dns4/{host}/tcp/{ws_port}/tls/ws/p2p/{peer_id}")
110
+ autotls_multiaddrs.append(f"/dns6/{host}/tcp/{ws_port}/tls/ws/p2p/{peer_id}")
111
+
112
+ proxy_hostname = env_values.get("PROXY_HOSTNAME", "").strip()
113
+ if proxy_hostname:
114
+ proxy_multiaddrs.append(f"/dns4/{proxy_hostname}/tcp/443/tls/ws/p2p/{peer_id}")
115
+ proxy_multiaddrs.append(f"/dns6/{proxy_hostname}/tcp/443/tls/ws/p2p/{peer_id}")
116
+
117
+ public_ipv4 = env_values.get("PUBLIC_IPV4", "").strip()
118
+ public_ipv6 = env_values.get("PUBLIC_IPV6", "").strip()
119
+ udp_port = env_values.get("EXTERNAL_RELAY_UDP_PORT", "").strip()
120
+ if udp_port:
121
+ if public_ipv4:
122
+ webtransport_multiaddrs.append(f"/ip4/{public_ipv4}/udp/{udp_port}/quic-v1/webtransport/p2p/{peer_id}")
123
+ webrtc_direct_multiaddrs.append(f"/ip4/{public_ipv4}/udp/{udp_port}/webrtc-direct/p2p/{peer_id}")
124
+ if public_ipv6:
125
+ webtransport_multiaddrs.append(f"/ip6/{public_ipv6}/udp/{udp_port}/quic-v1/webtransport/p2p/{peer_id}")
126
+ webrtc_direct_multiaddrs.append(f"/ip6/{public_ipv6}/udp/{udp_port}/webrtc-direct/p2p/{peer_id}")
127
+
128
+ probe_multiaddrs.extend(direct_tcp_multiaddrs)
129
+ probe_multiaddrs.extend(autotls_multiaddrs)
130
+ probe_multiaddrs.extend(proxy_multiaddrs)
131
+
132
+ return {
133
+ "direct_tcp_multiaddrs": dedupe(direct_tcp_multiaddrs),
134
+ "autotls_wss_multiaddrs": dedupe(autotls_multiaddrs),
135
+ "proxy_wss_multiaddrs": dedupe(proxy_multiaddrs),
136
+ "webtransport_multiaddrs": dedupe(webtransport_multiaddrs),
137
+ "webrtc_direct_multiaddrs": dedupe(webrtc_direct_multiaddrs),
138
+ "browser_bootstrap_multiaddrs": dedupe(
139
+ autotls_multiaddrs + proxy_multiaddrs + webtransport_multiaddrs + webrtc_direct_multiaddrs
140
+ ),
141
+ "probe_multiaddrs": dedupe(probe_multiaddrs),
142
+ }
143
+
144
+
145
+ def main() -> None:
146
+ started_at = time.monotonic()
147
+ deadline = time.monotonic() + WAIT_TIMEOUT_SECONDS
148
+ peer_id = None
149
+ listening_addrs: list[str] = []
150
+ grouped = {
151
+ "direct_tcp_multiaddrs": [],
152
+ "autotls_wss_multiaddrs": [],
153
+ "proxy_wss_multiaddrs": [],
154
+ "webtransport_multiaddrs": [],
155
+ "webrtc_direct_multiaddrs": [],
156
+ "browser_bootstrap_multiaddrs": [],
157
+ "probe_multiaddrs": [],
158
+ }
159
+
160
+ while time.monotonic() < deadline:
161
+ env_values = parse_env_file(ENV_FILE)
162
+ peer_id, listening_addrs = parse_logs()
163
+ if not peer_id:
164
+ time.sleep(WAIT_INTERVAL_SECONDS)
165
+ continue
166
+
167
+ grouped = build_probe_multiaddrs(env_values, peer_id, listening_addrs)
168
+ proxy_hostname = env_values.get("PROXY_HOSTNAME", "").strip()
169
+ if grouped["autotls_wss_multiaddrs"]:
170
+ break
171
+ if proxy_hostname and grouped["proxy_wss_multiaddrs"] and time.monotonic() - started_at >= AUTOTLS_EXTRA_WAIT_SECONDS:
172
+ break
173
+ if not proxy_hostname and time.monotonic() - started_at >= AUTOTLS_EXTRA_WAIT_SECONDS:
174
+ break
175
+ time.sleep(WAIT_INTERVAL_SECONDS)
176
+
177
+ if not peer_id:
178
+ raise SystemExit("unable to discover relay peer ID from service logs")
179
+
180
+ env_values = parse_env_file(ENV_FILE)
181
+ payload = {
182
+ "peer_id": peer_id,
183
+ "announce_addrs": [
184
+ entry.strip()
185
+ for entry in env_values.get("LIBP2P_ANNOUNCE_ADDRS", "").split(",")
186
+ if entry.strip()
187
+ ],
188
+ "listening_addrs": listening_addrs,
189
+ **grouped,
190
+ }
191
+ print(json.dumps(payload))
192
+
193
+
194
+ if __name__ == "__main__":
195
+ main()