@le-space/rootfs 0.1.52 → 0.2.0

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,224 @@
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import os
4
+ import subprocess
5
+ import time
6
+ import urllib.error
7
+ import urllib.request
8
+ from typing import Iterable
9
+
10
+
11
+ ENV_FILE = os.environ.get("ENV_FILE", "/etc/default/orbitdb-relay-pinner")
12
+ READY_FILE = os.environ.get("READY_FILE", "/etc/default/orbitdb-relay-pinner.ready")
13
+ AUTOTLS_READY_FILE = os.environ.get(
14
+ "AUTOTLS_READY_FILE", "/etc/default/orbitdb-relay-pinner.autotls-ready"
15
+ )
16
+ AUTOTLS_ZONE_FILE = os.environ.get("AUTOTLS_ZONE_FILE", "/etc/default/orbitdb-relay-pinner.autotls-zone")
17
+ AUTOTLS_HOSTS_FILE = os.environ.get("AUTOTLS_HOSTS_FILE", "/etc/default/orbitdb-relay-pinner.autotls-hosts")
18
+ AUTOTLS_CADDY_READY_FILE = os.environ.get(
19
+ "AUTOTLS_CADDY_READY_FILE", "/etc/default/orbitdb-relay-pinner.caddy-ready"
20
+ )
21
+ SERVICE_NAME = os.environ.get("SERVICE_NAME", "orbitdb-relay-pinner.service")
22
+ CADDY_SERVICE = os.environ.get("CADDY_SERVICE", "caddy.service")
23
+ CADDYFILE = os.environ.get("CADDYFILE", "/etc/caddy/Caddyfile")
24
+ METRICS_PORT = int(os.environ.get("METRICS_PORT", "9090"))
25
+ WAIT_TIMEOUT_SECONDS = int(os.environ.get("AUTOTLS_WAIT_TIMEOUT_SECONDS", "900"))
26
+ WAIT_INTERVAL_SECONDS = float(os.environ.get("AUTOTLS_WAIT_INTERVAL_SECONDS", "5"))
27
+
28
+
29
+ def fetch_json(path: str) -> dict:
30
+ url = f"http://127.0.0.1:{METRICS_PORT}{path}"
31
+ with urllib.request.urlopen(url, timeout=5) as response:
32
+ return json.loads(response.read().decode("utf-8"))
33
+
34
+
35
+ def parse_env_file(path: str) -> dict[str, str]:
36
+ values: dict[str, str] = {}
37
+ if not os.path.exists(path):
38
+ return values
39
+
40
+ with open(path, encoding="utf-8") as handle:
41
+ for line in handle:
42
+ stripped = line.strip()
43
+ if not stripped or stripped.startswith("#") or "=" not in stripped:
44
+ continue
45
+ key, value = stripped.split("=", 1)
46
+ values[key.strip()] = value.strip()
47
+ return values
48
+
49
+
50
+ def write_env_var(path: str, key: str, value: str) -> None:
51
+ lines: list[str] = []
52
+ replaced = False
53
+
54
+ if os.path.exists(path):
55
+ with open(path, encoding="utf-8") as handle:
56
+ lines = handle.readlines()
57
+
58
+ with open(path, "w", encoding="utf-8") as handle:
59
+ for line in lines:
60
+ stripped = line.lstrip()
61
+ if stripped.startswith(f"{key}=") or stripped.startswith(f"#{key}="):
62
+ handle.write(f"{key}={value}\n")
63
+ replaced = True
64
+ else:
65
+ handle.write(line)
66
+
67
+ if not replaced:
68
+ handle.write(f"{key}={value}\n")
69
+
70
+
71
+ def normalize_addr(addr: str) -> str:
72
+ parts = addr.strip().split("/")
73
+ if len(parts) >= 3 and parts[-2] == "p2p":
74
+ return "/".join(parts[:-2])
75
+ return addr.strip()
76
+
77
+
78
+ def dedupe(sequence: Iterable[str]) -> list[str]:
79
+ seen: set[str] = set()
80
+ values: list[str] = []
81
+ for item in sequence:
82
+ if item and item not in seen:
83
+ seen.add(item)
84
+ values.append(item)
85
+ return values
86
+
87
+
88
+ def ipv4_domain(ipv4: str, zone: str) -> str:
89
+ return f"{ipv4.replace('.', '-')}.{zone}"
90
+
91
+
92
+ def ipv6_domain(ipv6: str, zone: str) -> str:
93
+ subdomain = ipv6.replace(":", "-")
94
+ if subdomain.startswith("-"):
95
+ subdomain = f"0{subdomain}"
96
+ if subdomain.endswith("-"):
97
+ subdomain = f"{subdomain}0"
98
+ return f"{subdomain}.{zone}"
99
+
100
+
101
+ def wait_for_autotls_zone() -> str:
102
+ deadline = time.monotonic() + WAIT_TIMEOUT_SECONDS
103
+ last_error = "metrics endpoint never became ready"
104
+
105
+ while time.monotonic() < deadline:
106
+ try:
107
+ health = fetch_json("/health")
108
+ payload = fetch_json("/multiaddrs")
109
+ zone = health.get("autoTlsServingZone")
110
+ all_addrs = payload.get("all")
111
+ if (
112
+ isinstance(zone, str)
113
+ and zone
114
+ and isinstance(all_addrs, list)
115
+ and any(isinstance(addr, str) and "/tls/ws" in addr for addr in all_addrs)
116
+ ):
117
+ return zone
118
+ last_error = "AutoTLS secure websocket multiaddrs not advertised yet"
119
+ except (urllib.error.URLError, TimeoutError, json.JSONDecodeError) as error:
120
+ last_error = str(error)
121
+
122
+ time.sleep(WAIT_INTERVAL_SECONDS)
123
+
124
+ raise RuntimeError(last_error)
125
+
126
+
127
+ def build_secure_addrs(env_values: dict[str, str], zone: str) -> list[str]:
128
+ ws_port = env_values.get("EXTERNAL_RELAY_WS_PORT", "").strip()
129
+ if not ws_port:
130
+ raise RuntimeError("missing EXTERNAL_RELAY_WS_PORT in environment file")
131
+
132
+ addrs: list[str] = []
133
+
134
+ public_ipv4 = env_values.get("PUBLIC_IPV4", "").strip()
135
+ if public_ipv4:
136
+ host4 = ipv4_domain(public_ipv4, zone)
137
+ addrs.append(f"/ip4/{public_ipv4}/tcp/{ws_port}/tls/sni/{host4}/ws")
138
+ addrs.append(f"/dns4/{host4}/tcp/{ws_port}/tls/ws")
139
+
140
+ public_ipv6 = env_values.get("PUBLIC_IPV6", "").strip()
141
+ if public_ipv6:
142
+ host6 = ipv6_domain(public_ipv6, zone)
143
+ addrs.append(f"/ip6/{public_ipv6}/tcp/{ws_port}/tls/sni/{host6}/ws")
144
+ addrs.append(f"/dns6/{host6}/tcp/{ws_port}/tls/ws")
145
+
146
+ if not addrs:
147
+ raise RuntimeError("missing PUBLIC_IPV4/PUBLIC_IPV6 in environment file")
148
+
149
+ return addrs
150
+
151
+
152
+ def secure_hosts(env_values: dict[str, str], zone: str) -> list[str]:
153
+ hosts: list[str] = []
154
+ public_ipv4 = env_values.get("PUBLIC_IPV4", "").strip()
155
+ if public_ipv4:
156
+ hosts.append(ipv4_domain(public_ipv4, zone))
157
+
158
+ public_ipv6 = env_values.get("PUBLIC_IPV6", "").strip()
159
+ if public_ipv6:
160
+ hosts.append(ipv6_domain(public_ipv6, zone))
161
+
162
+ return dedupe(hosts)
163
+
164
+
165
+ def metrics_https_public_host(env_values: dict[str, str], zone: str) -> str | None:
166
+ public_ipv4 = env_values.get("PUBLIC_IPV4", "").strip()
167
+ if public_ipv4:
168
+ return ipv4_domain(public_ipv4, zone)
169
+
170
+ public_ipv6 = env_values.get("PUBLIC_IPV6", "").strip()
171
+ if public_ipv6:
172
+ return ipv6_domain(public_ipv6, zone)
173
+
174
+ return None
175
+
176
+
177
+ def main() -> None:
178
+ if not os.path.exists(READY_FILE):
179
+ raise SystemExit(f"missing ready file: {READY_FILE}")
180
+
181
+ env_values = parse_env_file(ENV_FILE)
182
+ zone = wait_for_autotls_zone()
183
+ secure_addrs = build_secure_addrs(env_values, zone)
184
+ exact_hosts = secure_hosts(env_values, zone)
185
+ metrics_host = metrics_https_public_host(env_values, zone)
186
+ current_value = env_values.get("VITE_APPEND_ANNOUNCE", "")
187
+ current_metrics_host = env_values.get("METRICS_HTTPS_PUBLIC_HOST", "").strip()
188
+ merged = dedupe(
189
+ [normalize_addr(addr) for addr in current_value.split(",") if addr.strip()] + secure_addrs
190
+ )
191
+ announce_changed = current_value.split(",") != merged
192
+ metrics_host_changed = bool(metrics_host) and current_metrics_host != metrics_host
193
+
194
+ announce_value = ",".join(merged)
195
+ write_env_var(ENV_FILE, "VITE_APPEND_ANNOUNCE", announce_value)
196
+ write_env_var(ENV_FILE, "AUTOTLS_SERVING_ZONE", zone)
197
+ if metrics_host:
198
+ write_env_var(ENV_FILE, "METRICS_HTTPS_PUBLIC_HOST", metrics_host)
199
+
200
+ with open(AUTOTLS_HOSTS_FILE, "w", encoding="utf-8") as handle:
201
+ for host in exact_hosts:
202
+ handle.write(f"{host}\n")
203
+ with open(AUTOTLS_ZONE_FILE, "w", encoding="utf-8") as handle:
204
+ handle.write(f"{zone}\n")
205
+
206
+ if announce_changed or metrics_host_changed:
207
+ subprocess.run(["systemctl", "restart", SERVICE_NAME], check=True)
208
+ else:
209
+ print("AutoTLS secure external announce addresses already present")
210
+
211
+ if env_values.get("PROXY_HOSTNAME", "").strip():
212
+ open(AUTOTLS_CADDY_READY_FILE, "a", encoding="utf-8").close()
213
+ if os.path.exists(CADDYFILE):
214
+ subprocess.run(["systemctl", "enable", CADDY_SERVICE], check=False)
215
+ subprocess.run(["systemctl", "restart", CADDY_SERVICE], check=False)
216
+ elif os.path.exists(AUTOTLS_CADDY_READY_FILE):
217
+ os.remove(AUTOTLS_CADDY_READY_FILE)
218
+
219
+ open(AUTOTLS_READY_FILE, "a", encoding="utf-8").close()
220
+ print(f"Updated VITE_APPEND_ANNOUNCE={announce_value}")
221
+
222
+
223
+ if __name__ == "__main__":
224
+ main()
@@ -0,0 +1,24 @@
1
+ [Unit]
2
+ Description=Persist AutoTLS secure announce addresses for orbitdb-relay-pinner
3
+ After=orbitdb-relay-pinner.service
4
+ Wants=orbitdb-relay-pinner.service
5
+ ConditionPathExists=/etc/default/orbitdb-relay-pinner.ready
6
+ ConditionPathExists=!/etc/default/orbitdb-relay-pinner.autotls-ready
7
+
8
+ [Service]
9
+ Type=oneshot
10
+ Environment=ENV_FILE=/etc/default/orbitdb-relay-pinner
11
+ Environment=READY_FILE=/etc/default/orbitdb-relay-pinner.ready
12
+ Environment=AUTOTLS_READY_FILE=/etc/default/orbitdb-relay-pinner.autotls-ready
13
+ Environment=AUTOTLS_ZONE_FILE=/etc/default/orbitdb-relay-pinner.autotls-zone
14
+ Environment=AUTOTLS_HOSTS_FILE=/etc/default/orbitdb-relay-pinner.autotls-hosts
15
+ Environment=AUTOTLS_CADDY_READY_FILE=/etc/default/orbitdb-relay-pinner.caddy-ready
16
+ Environment=SERVICE_NAME=orbitdb-relay-pinner.service
17
+ Environment=CADDY_SERVICE=caddy.service
18
+ Environment=CADDYFILE=/etc/caddy/Caddyfile
19
+ ExecStart=/usr/bin/python3 /usr/local/sbin/orbitdb-relay-pinner-autotls-refresh.py
20
+ StandardOutput=journal
21
+ StandardError=journal
22
+
23
+ [Install]
24
+ WantedBy=multi-user.target
@@ -0,0 +1,2 @@
1
+ [Unit]
2
+ ConditionPathExists=/etc/default/orbitdb-relay-pinner.ready
@@ -0,0 +1,21 @@
1
+ [Unit]
2
+ Description=Temporary orbitdb-relay-pinner setup endpoint
3
+ Wants=network-online.target
4
+ After=network-online.target
5
+ ConditionPathExists=!/etc/default/orbitdb-relay-pinner.ready
6
+
7
+ [Service]
8
+ Environment=ENV_FILE=/etc/default/orbitdb-relay-pinner
9
+ Environment=READY_FILE=/etc/default/orbitdb-relay-pinner.ready
10
+ Environment=SERVICE_NAME=orbitdb-relay-pinner.service
11
+ Environment=BOOTSTRAP_SERVICE=orbitdb-relay-pinner-bootstrap.service
12
+ Environment=METADATA_FILE=/run/orbitdb-relay-pinner-setup-metadata.json
13
+ Environment=METADATA_ERROR_FILE=/run/orbitdb-relay-pinner-setup-metadata.error
14
+ ExecStart=/usr/bin/python3 /usr/local/sbin/orbitdb-relay-pinner-setup-server.py
15
+ StandardOutput=journal
16
+ StandardError=journal
17
+ Restart=on-failure
18
+ RestartSec=2s
19
+
20
+ [Install]
21
+ WantedBy=multi-user.target
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ INSTALL_DIR="${INSTALL_DIR:-/opt/orbitdb-relay-pinner}"
5
+ SERVICE_USER="${SERVICE_USER:-orbitdb-relay}"
6
+ DATA_DIR="${DATA_DIR:-/var/lib/orbitdb-relay-pinner}"
7
+ ENV_FILE="${ENV_FILE:-/etc/default/orbitdb-relay-pinner}"
8
+ NODE_MIN_MAJOR="${NODE_MIN_MAJOR:-22}"
9
+ CADDY_READY_FILE="${CADDY_READY_FILE:-/etc/default/orbitdb-relay-pinner.caddy-ready}"
10
+
11
+ if [ ! -d "${INSTALL_DIR}" ]; then
12
+ echo "Missing ${INSTALL_DIR}; the rootfs build did not copy orbitdb-relay-pinner."
13
+ exit 1
14
+ fi
15
+
16
+ export DEBIAN_FRONTEND=noninteractive
17
+ apt-get update
18
+ apt-get install -y ca-certificates curl gnupg python3 build-essential caddy
19
+
20
+ if ! command -v node >/dev/null 2>&1 || [ "$(node -p "process.versions.node.split('.')[0]" 2>/dev/null || echo 0)" -lt "${NODE_MIN_MAJOR}" ]; then
21
+ curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
22
+ apt-get install -y nodejs
23
+ fi
24
+
25
+ rm -rf /var/lib/apt/lists/*
26
+
27
+ if ! command -v node >/dev/null 2>&1; then
28
+ echo "Node.js ${NODE_MIN_MAJOR}+ installation failed." >&2
29
+ exit 1
30
+ fi
31
+
32
+ if ! command -v npm >/dev/null 2>&1; then
33
+ echo "npm is unavailable after installing Node.js." >&2
34
+ exit 1
35
+ fi
36
+
37
+ mkdir -p "${DATA_DIR}" "$(dirname "${ENV_FILE}")"
38
+
39
+ if ! id "${SERVICE_USER}" >/dev/null 2>&1; then
40
+ useradd --system --home "${DATA_DIR}" --create-home --shell /usr/sbin/nologin "${SERVICE_USER}"
41
+ fi
42
+
43
+ chown -R "${SERVICE_USER}:${SERVICE_USER}" "${DATA_DIR}" "${INSTALL_DIR}"
44
+
45
+ install_command="cd '${INSTALL_DIR}' && npm install --omit=dev"
46
+ if [ -f "${INSTALL_DIR}/pnpm-lock.yaml" ] && command -v corepack >/dev/null 2>&1; then
47
+ package_manager="$(
48
+ node -e "const fs=require('fs'); const pkg=JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); process.stdout.write(pkg.packageManager || '')" \
49
+ "${INSTALL_DIR}/package.json" 2>/dev/null || true
50
+ )"
51
+ if [ -n "${package_manager}" ]; then
52
+ install_command="cd '${INSTALL_DIR}' && export COREPACK_HOME='${DATA_DIR}/.cache/corepack' && mkdir -p \"\$COREPACK_HOME\" && corepack prepare '${package_manager}' --activate && corepack pnpm install --prod --frozen-lockfile"
53
+ fi
54
+ fi
55
+
56
+ if command -v runuser >/dev/null 2>&1; then
57
+ runuser -u "${SERVICE_USER}" -- env HOME="${DATA_DIR}" bash -lc "${install_command}"
58
+ elif command -v sudo >/dev/null 2>&1; then
59
+ sudo -u "${SERVICE_USER}" env HOME="${DATA_DIR}" bash -lc "${install_command}"
60
+ else
61
+ echo "Need runuser or sudo to install dependencies as ${SERVICE_USER}." >&2
62
+ exit 1
63
+ fi
64
+
65
+ # Drop toolchains and caches after dependency installation so the prebaked
66
+ # qcow2 reflects the runtime footprint rather than the build footprint.
67
+ apt-get purge -y build-essential
68
+ apt-get autoremove -y
69
+ apt-get clean
70
+ rm -rf /var/lib/apt/lists/*
71
+ rm -rf "${DATA_DIR}/.cache/corepack" "${DATA_DIR}/.npm" "${DATA_DIR}/.cache/npm"
72
+ rm -rf "${INSTALL_DIR}/node_modules/.cache"
73
+
74
+ mkdir -p "${INSTALL_DIR}/node_modules"
75
+ ln -sfn "${INSTALL_DIR}" "${INSTALL_DIR}/node_modules/orbitdb-relay-pinner"
76
+
77
+ cat > /usr/local/bin/orbitdb-relay-pinner <<'EOF'
78
+ #!/usr/bin/env bash
79
+ set -euo pipefail
80
+ exec /usr/bin/node /opt/orbitdb-relay-pinner/dist/cli.js "$@"
81
+ EOF
82
+ chmod 0755 /usr/local/bin/orbitdb-relay-pinner
83
+
84
+ if [ -f "${INSTALL_DIR}/deploy/orbitdb-relay-pinner.env.example" ] && [ ! -f "${ENV_FILE}" ]; then
85
+ cp "${INSTALL_DIR}/deploy/orbitdb-relay-pinner.env.example" "${ENV_FILE}"
86
+ fi
87
+
88
+ touch "${ENV_FILE}"
89
+ chmod 0640 "${ENV_FILE}"
90
+ chown "root:${SERVICE_USER}" "${ENV_FILE}"
91
+ rm -f "${CADDY_READY_FILE}"
92
+
93
+ mkdir -p /etc/caddy /etc/systemd/system/caddy.service.d
94
+ cat > /etc/systemd/system/caddy.service.d/orbitdb-relay-pinner.conf <<EOF
95
+ [Unit]
96
+ ConditionPathExists=${CADDY_READY_FILE}
97
+ EOF
98
+
99
+ write_env_var() {
100
+ local key="$1"
101
+ local value="$2"
102
+
103
+ if grep -Eq "^[#[:space:]]*${key}=" "${ENV_FILE}"; then
104
+ sed -i "s|^[#[:space:]]*${key}=.*|${key}=${value}|" "${ENV_FILE}"
105
+ else
106
+ printf '%s=%s\n' "${key}" "${value}" >> "${ENV_FILE}"
107
+ fi
108
+ }
109
+
110
+ write_env_var "DATASTORE_PATH" "${DATA_DIR}"
111
+ write_env_var "METRICS_PORT" "9090"
112
+ write_env_var "METRICS_HTTPS_ENABLED" "0"
113
+ write_env_var "RELAY_TCP_PORT" "9091"
114
+ write_env_var "RELAY_WS_PORT" "9092"
115
+ write_env_var "RELAY_WEBRTC_PORT" "9093"
116
+ write_env_var "RELAY_QUIC_PORT" "9094"
117
+ write_env_var "RELAY_DISABLE_WEBRTC" "1"
118
+ write_env_var "RELAY_DISABLE_BOOTSTRAP" "1"
119
+ write_env_var "disableAutoTLS" "1"
120
+ write_env_var "ENABLE_GENERAL_LOGS" "1"
121
+ write_env_var "DEBUG" "'libp2p:websockets:listener'"
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ENV_FILE="${ENV_FILE:-/etc/default/orbitdb-relay-pinner}"
5
+ READY_FILE="${READY_FILE:-/etc/default/orbitdb-relay-pinner.ready}"
6
+ AUTOTLS_READY_FILE="${AUTOTLS_READY_FILE:-/etc/default/orbitdb-relay-pinner.autotls-ready}"
7
+ AUTOTLS_ZONE_FILE="${AUTOTLS_ZONE_FILE:-/etc/default/orbitdb-relay-pinner.autotls-zone}"
8
+ AUTOTLS_HOSTS_FILE="${AUTOTLS_HOSTS_FILE:-/etc/default/orbitdb-relay-pinner.autotls-hosts}"
9
+ SERVICE_NAME="${SERVICE_NAME:-orbitdb-relay-pinner.service}"
10
+ CADDY_SERVICE="${CADDY_SERVICE:-caddy.service}"
11
+ CADDY_READY_FILE="${CADDY_READY_FILE:-/etc/default/orbitdb-relay-pinner.caddy-ready}"
12
+ CADDYFILE="${CADDYFILE:-/etc/caddy/Caddyfile}"
13
+ CADDY_UPSTREAM_WSS_PORT="${CADDY_UPSTREAM_WSS_PORT:-9092}"
14
+ CADDY_UPSTREAM_HOST="${CADDY_UPSTREAM_HOST:-127.0.0.1}"
15
+ CADDY_UPSTREAM_METRICS_PORT="${CADDY_UPSTREAM_METRICS_PORT:-9090}"
16
+ AUTOTLS_REFRESH_SERVICE="${AUTOTLS_REFRESH_SERVICE:-orbitdb-relay-pinner-autotls-refresh.service}"
17
+ PUBLIC_IPV4=""
18
+ PUBLIC_IPV6=""
19
+ TCP_PORT=""
20
+ WS_PORT=""
21
+ PROXY_HOSTNAME=""
22
+ METRICS_PORT=""
23
+ METRICS_HTTPS_PORT=""
24
+ WEBRTC_PORT=""
25
+ QUIC_PORT=""
26
+ START_SERVICE=1
27
+
28
+ usage() {
29
+ cat <<'EOF'
30
+ Usage:
31
+ orbitdb-relay-pinner-configure.sh \
32
+ --public-ipv4 <ip> \
33
+ [--public-ipv6 <ipv6>] \
34
+ --tcp-port <host-port> \
35
+ --ws-port <host-port> \
36
+ [--proxy-hostname <hostname>] \
37
+ [--metrics-port <host-port>] \
38
+ [--metrics-https-port <host-port>] \
39
+ [--webrtc-port <host-port>] \
40
+ [--quic-port <host-port>] \
41
+ [--no-start]
42
+
43
+ Writes VITE_APPEND_ANNOUNCE for the externally assigned Aleph host ports,
44
+ marks the relay as ready, and optionally starts the relay plus Caddy services.
45
+ EOF
46
+ }
47
+
48
+ write_env_var() {
49
+ local key="$1"
50
+ local value="$2"
51
+
52
+ if grep -Eq "^[#[:space:]]*${key}=" "${ENV_FILE}"; then
53
+ sed -i "s|^[#[:space:]]*${key}=.*|${key}=${value}|" "${ENV_FILE}"
54
+ else
55
+ printf '%s=%s\n' "${key}" "${value}" >> "${ENV_FILE}"
56
+ fi
57
+ }
58
+
59
+ write_caddyfile() {
60
+ local hostname="$1"
61
+ mkdir -p "$(dirname "${CADDYFILE}")"
62
+ cat > "${CADDYFILE}" <<EOF
63
+ {
64
+ auto_https disable_redirects
65
+ }
66
+
67
+ ${hostname} {
68
+ # Proxy the full orbitdb-relay-pinner HTTP API surface to the metrics/HTTP port.
69
+ # Everything else falls through to the relay WebSocket backend for browser libp2p.
70
+ handle /health {
71
+ reverse_proxy 127.0.0.1:${CADDY_UPSTREAM_METRICS_PORT}
72
+ }
73
+
74
+ handle /multiaddrs {
75
+ reverse_proxy 127.0.0.1:${CADDY_UPSTREAM_METRICS_PORT}
76
+ }
77
+
78
+ handle /multiaddresses {
79
+ reverse_proxy 127.0.0.1:${CADDY_UPSTREAM_METRICS_PORT}
80
+ }
81
+
82
+ handle /metrics {
83
+ reverse_proxy 127.0.0.1:${CADDY_UPSTREAM_METRICS_PORT}
84
+ }
85
+
86
+ handle /pinning/* {
87
+ reverse_proxy 127.0.0.1:${CADDY_UPSTREAM_METRICS_PORT}
88
+ }
89
+
90
+ handle /ipfs/* {
91
+ reverse_proxy 127.0.0.1:${CADDY_UPSTREAM_METRICS_PORT}
92
+ }
93
+
94
+ reverse_proxy ${CADDY_UPSTREAM_HOST}:${CADDY_UPSTREAM_WSS_PORT}
95
+ }
96
+ EOF
97
+ }
98
+
99
+ while [ "$#" -gt 0 ]; do
100
+ case "$1" in
101
+ --public-ipv4)
102
+ PUBLIC_IPV4="${2:-}"
103
+ shift 2
104
+ ;;
105
+ --public-ipv6)
106
+ PUBLIC_IPV6="${2:-}"
107
+ shift 2
108
+ ;;
109
+ --tcp-port)
110
+ TCP_PORT="${2:-}"
111
+ shift 2
112
+ ;;
113
+ --ws-port)
114
+ WS_PORT="${2:-}"
115
+ shift 2
116
+ ;;
117
+ --proxy-hostname)
118
+ PROXY_HOSTNAME="${2:-}"
119
+ shift 2
120
+ ;;
121
+ --metrics-port)
122
+ METRICS_PORT="${2:-}"
123
+ shift 2
124
+ ;;
125
+ --metrics-https-port)
126
+ METRICS_HTTPS_PORT="${2:-}"
127
+ shift 2
128
+ ;;
129
+ --webrtc-port)
130
+ WEBRTC_PORT="${2:-}"
131
+ shift 2
132
+ ;;
133
+ --quic-port)
134
+ QUIC_PORT="${2:-}"
135
+ shift 2
136
+ ;;
137
+ --no-start)
138
+ START_SERVICE=0
139
+ shift
140
+ ;;
141
+ -h|--help)
142
+ usage
143
+ exit 0
144
+ ;;
145
+ *)
146
+ echo "Unknown argument: $1" >&2
147
+ usage >&2
148
+ exit 1
149
+ ;;
150
+ esac
151
+ done
152
+
153
+ if [ -z "${PUBLIC_IPV4}" ] || [ -z "${TCP_PORT}" ] || [ -z "${WS_PORT}" ]; then
154
+ usage >&2
155
+ exit 1
156
+ fi
157
+
158
+ touch "${ENV_FILE}"
159
+ rm -f "${AUTOTLS_READY_FILE}" "${AUTOTLS_ZONE_FILE}" "${AUTOTLS_HOSTS_FILE}"
160
+
161
+ announce=(
162
+ "/ip4/${PUBLIC_IPV4}/tcp/${TCP_PORT}"
163
+ )
164
+
165
+ if [ -n "${PUBLIC_IPV6}" ]; then
166
+ announce+=(
167
+ "/ip6/${PUBLIC_IPV6}/tcp/${TCP_PORT}"
168
+ )
169
+ fi
170
+
171
+ if [ -n "${PROXY_HOSTNAME}" ]; then
172
+ announce+=("/dns4/${PROXY_HOSTNAME}/tcp/443/tls/ws")
173
+ announce+=("/dns6/${PROXY_HOSTNAME}/tcp/443/tls/ws")
174
+ else
175
+ announce+=("/ip4/${PUBLIC_IPV4}/tcp/${WS_PORT}/ws")
176
+ if [ -n "${PUBLIC_IPV6}" ]; then
177
+ announce+=("/ip6/${PUBLIC_IPV6}/tcp/${WS_PORT}/ws")
178
+ fi
179
+ fi
180
+
181
+ if [ -n "${WEBRTC_PORT}" ]; then
182
+ announce+=("/ip4/${PUBLIC_IPV4}/udp/${WEBRTC_PORT}/webrtc-direct")
183
+ if [ -n "${PUBLIC_IPV6}" ]; then
184
+ announce+=("/ip6/${PUBLIC_IPV6}/udp/${WEBRTC_PORT}/webrtc-direct")
185
+ fi
186
+ fi
187
+
188
+ if [ -n "${QUIC_PORT}" ]; then
189
+ announce+=("/ip4/${PUBLIC_IPV4}/udp/${QUIC_PORT}/quic-v1")
190
+ if [ -n "${PUBLIC_IPV6}" ]; then
191
+ announce+=("/ip6/${PUBLIC_IPV6}/udp/${QUIC_PORT}/quic-v1")
192
+ fi
193
+ fi
194
+
195
+ announce_value="$(IFS=,; printf '%s' "${announce[*]}")"
196
+ write_env_var "VITE_APPEND_ANNOUNCE" "${announce_value}"
197
+ write_env_var "PUBLIC_IPV4" "${PUBLIC_IPV4}"
198
+ if [ -n "${PUBLIC_IPV6}" ]; then
199
+ write_env_var "PUBLIC_IPV6" "${PUBLIC_IPV6}"
200
+ fi
201
+ write_env_var "EXTERNAL_RELAY_TCP_PORT" "${TCP_PORT}"
202
+ write_env_var "EXTERNAL_RELAY_WS_PORT" "${WS_PORT}"
203
+ if [ -n "${PROXY_HOSTNAME}" ]; then
204
+ write_env_var "PROXY_HOSTNAME" "${PROXY_HOSTNAME}"
205
+ else
206
+ write_env_var "PROXY_HOSTNAME" ""
207
+ fi
208
+ if [ -n "${METRICS_PORT}" ]; then
209
+ write_env_var "EXTERNAL_METRICS_PORT" "${METRICS_PORT}"
210
+ fi
211
+ if [ -n "${METRICS_HTTPS_PORT}" ]; then
212
+ write_env_var "EXTERNAL_METRICS_HTTPS_PORT" "${METRICS_HTTPS_PORT}"
213
+ fi
214
+ if [ -n "${WEBRTC_PORT}" ]; then
215
+ write_env_var "EXTERNAL_RELAY_WEBRTC_PORT" "${WEBRTC_PORT}"
216
+ fi
217
+ if [ -n "${QUIC_PORT}" ]; then
218
+ write_env_var "EXTERNAL_RELAY_QUIC_PORT" "${QUIC_PORT}"
219
+ fi
220
+ touch "${READY_FILE}"
221
+
222
+ if [ "${START_SERVICE}" -eq 1 ]; then
223
+ systemctl daemon-reload
224
+ systemctl enable "${SERVICE_NAME}"
225
+ systemctl restart "${SERVICE_NAME}"
226
+ systemctl enable "${AUTOTLS_REFRESH_SERVICE}"
227
+ if [ -n "${PROXY_HOSTNAME}" ]; then
228
+ write_caddyfile "${PROXY_HOSTNAME}"
229
+ touch "${CADDY_READY_FILE}"
230
+ systemctl enable "${CADDY_SERVICE}"
231
+ systemctl restart "${CADDY_SERVICE}"
232
+ else
233
+ rm -f "${CADDY_READY_FILE}"
234
+ systemctl stop "${CADDY_SERVICE}" || true
235
+ fi
236
+ systemctl restart --no-block "${AUTOTLS_REFRESH_SERVICE}"
237
+ fi
238
+
239
+ printf 'Configured VITE_APPEND_ANNOUNCE=%s\n' "${announce_value}"
240
+ printf 'Ready file: %s\n' "${READY_FILE}"