@fased/fased 0.1.9 → 0.1.10
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.
- package/dist/{agents-C4jz1huI.js → agents-CsxQP5xg.js} +8 -8
- package/dist/{attachment-normalize-C-VpVpLd.js → attachment-normalize-CSxeaVcP.js} +1 -1
- package/dist/{attachment-normalize-DijgHIHy.js → attachment-normalize-f9lQXrZo.js} +1 -1
- package/dist/{audio-preflight-CI7BIPwS.js → audio-preflight-BeJd4YPQ.js} +2 -2
- package/dist/{audio-preflight-U5m63Clr.js → audio-preflight-CVU3GLti.js} +2 -2
- package/dist/{audio-preflight-oqlL2Kux.js → audio-preflight-C_g_wQAc.js} +1 -1
- package/dist/{audio-preflight-C6Lb4XU1.js → audio-preflight-DBtzpOL_.js} +1 -1
- package/dist/{audio-preflight-DLGJBu9O.js → audio-preflight-DD1zXhwM.js} +2 -2
- package/dist/{audit-DwbVJt3i.js → audit-BB2lDlNb.js} +1 -1
- package/dist/{audit-BCtx-_dH.js → audit-CDgqnjys.js} +1 -1
- package/dist/{auth-DIBzLQ-S.js → auth-Bfgwy6TQ.js} +5 -5
- package/dist/{auth-choice-CEeiz47B.js → auth-choice-DbEAsUsi.js} +1 -1
- package/dist/{auth-choice-DOqXUqF_.js → auth-choice-DlcbJgp_.js} +1 -1
- package/dist/{auth-choice-prompt-yqlvFI-_.js → auth-choice-prompt-CcOEzXoi.js} +1 -1
- package/dist/{auth-choice-prompt-FJDk83bS.js → auth-choice-prompt-DMlwK9kF.js} +1 -1
- package/dist/build-info.json +3 -3
- package/dist/bundled/boot-md/handler.js +1 -1
- package/dist/bundled/session-memory/handler.js +1 -1
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/{channel-web-CBfPvCCy.js → channel-web-CMZFjZzU.js} +1 -1
- package/dist/{channel-web-DaircVlX.js → channel-web-qHKfuCHE.js} +1 -1
- package/dist/{channels-BygboZOV.js → channels-BoOF2dAy.js} +6 -6
- package/dist/{channels-DgozxTBh.js → channels-DvT6s1kl.js} +6 -6
- package/dist/{channels-cli-DNz7gmOI.js → channels-cli-CNhEFKbn.js} +5 -5
- package/dist/{channels-cli-CY4n5HcO.js → channels-cli-NoUnbEgC.js} +5 -5
- package/dist/cli/daemon-cli.js +1 -1
- package/dist/{cli-CFgX3wef.js → cli-CE8pVYeA.js} +2 -2
- package/dist/{cli-63C0cETT.js → cli-Ctvy4-cz.js} +2 -2
- package/dist/{command-registry-DEiX9EPg.js → command-registry-fqlYdOTI.js} +9 -9
- package/dist/{completion-cli-CRwAyWQL.js → completion-cli-Bd9iPo4A.js} +1 -1
- package/dist/{completion-cli-BzQetGgr.js → completion-cli-CKzB1lB1.js} +2 -2
- package/dist/{config-cli-CPl1vPz0.js → config-cli-CQpqSVFS.js} +1 -1
- package/dist/{config-cli-C7vsh8Fg.js → config-cli-DUNkddbL.js} +1 -1
- package/dist/{configure-BEI_pe6B.js → configure-B2WT8-lE.js} +12 -12
- package/dist/{configure-C8lxNKpB.js → configure-DxDNasaD.js} +12 -12
- package/dist/control-ui/assets/app-wpJSg6bV.js.map +1 -1
- package/dist/{cron-cli-BqD_ZSEC.js → cron-cli-D3Dznk0V.js} +2 -2
- package/dist/{cron-cli-C7i4-Whp.js → cron-cli-DYWW7cv1.js} +2 -2
- package/dist/{daemon-cli-DFYzYoB8.js → daemon-cli-DDUjmEow.js} +1 -1
- package/dist/{daemon-cli-D0AP3mOg.js → daemon-cli-DFjCvbUo.js} +1 -1
- package/dist/daemon-cli.js +1 -1
- package/dist/{deps-D9aJvXyD.js → deps-4rCQ5SBd.js} +1 -1
- package/dist/{deps-ddWLkZAO.js → deps-CBapZnsi.js} +1 -1
- package/dist/entry.js +1 -1
- package/dist/extensionAPI.js +2 -2
- package/dist/{gateway-cli-D_Yd_yWi.js → gateway-cli-BXl9lmch.js} +27 -27
- package/dist/{gateway-cli-BJkYYsGs.js → gateway-cli-CNCA_gcT.js} +27 -27
- package/dist/{health-DCFLGVDU.js → health-C_g99tp_.js} +2 -2
- package/dist/{health-gyJixAyN.js → health-DTTfLYjl.js} +2 -2
- package/dist/{heartbeat-runner-Bn9N5npL.js → heartbeat-runner-D04n14H-.js} +1 -1
- package/dist/{heartbeat-runner-BFUvc4Y_.js → heartbeat-runner-DXh84Wuy.js} +1 -1
- package/dist/{hooks-cli-CGj2FDai.js → hooks-cli-BDpBNlPV.js} +3 -3
- package/dist/{hooks-cli-BGFJQWmK.js → hooks-cli-FhVd5CE-.js} +3 -3
- package/dist/index.js +7 -7
- package/dist/{ipv4-CjO0_Ry3.js → ipv4-Citn8Cno.js} +2 -2
- package/dist/{ipv4-CBSPFYQB.js → ipv4-P47TSJIK.js} +2 -2
- package/dist/{lifecycle-DFvIF7r0.js → lifecycle-B4RibYD4.js} +1 -1
- package/dist/{lifecycle-BBNnNoX_.js → lifecycle-Bgh3V8Jt.js} +1 -1
- package/dist/{list.auth-overview-DySKd1cu.js → list.auth-overview-BQzrIpgk.js} +1 -1
- package/dist/{list.auth-overview-CpmtoN2D.js → list.auth-overview-fBVXihg0.js} +1 -1
- package/dist/llm-slug-generator.js +1 -1
- package/dist/{model-catalog-DfLJlqO8.js → model-catalog-B2mRe6cZ.js} +6 -6
- package/dist/{models-BbfLyMBF.js → models-D8MTK6SG.js} +4 -4
- package/dist/{models-cli-Bu09lWlb.js → models-cli-Bl8xk6-0.js} +4 -4
- package/dist/{models-cli-DpmciZv6.js → models-cli-CXdCnyks.js} +5 -5
- package/dist/{node-cli-D6iG4Fev.js → node-cli-Cqh8buCU.js} +2 -2
- package/dist/{node-cli-GbiqR_FY.js → node-cli-DkgKrYS1.js} +2 -2
- package/dist/{onboard-BgU-rNfl.js → onboard-BD0MOrv7.js} +6 -6
- package/dist/{onboard-CViA2g_j.js → onboard-Dz8z4vBi.js} +6 -6
- package/dist/{onboard-channels-gHaSwzTd.js → onboard-channels-CPh2EJOi.js} +2 -2
- package/dist/{onboard-channels-BeKekf-a.js → onboard-channels-DUa2qkms.js} +2 -2
- package/dist/{onboard-search-DkOnHZm9.js → onboard-search-BnHMgV2k.js} +5 -5
- package/dist/{onboard-search-COuzKj6_.js → onboard-search-CqiDzWQw.js} +5 -5
- package/dist/{onboarding-CowfhJUD.js → onboarding-BC-5Pv9o.js} +7 -7
- package/dist/{onboarding-nabyJljA.js → onboarding-DgTB_zq7.js} +7 -7
- package/dist/{openresponses-http-D42SXipZ.js → openresponses-http-BQa9rI-N.js} +1 -1
- package/dist/{openresponses-http-C66CR36w.js → openresponses-http-BYiaPg-e.js} +2 -2
- package/dist/{openresponses-http-CdO4hFg_.js → openresponses-http-BirGBJTm.js} +2 -2
- package/dist/{openresponses-http-One5KNo8.js → openresponses-http-BsQmM8zM.js} +1 -1
- package/dist/{openresponses-http-BBydvP41.js → openresponses-http-CcawkBY4.js} +2 -2
- package/dist/{parent-default-help-82dOtJ5b.js → parent-default-help-CpfuGajV.js} +1 -1
- package/dist/{parent-default-help-iR__S_d8.js → parent-default-help-DbMf7NQe.js} +1 -1
- package/dist/{paths-Dwg5yVao.js → paths-BxKqczZV.js} +9 -9
- package/dist/{pi-embedded-BqhMzKsb.js → pi-embedded-Bapdl5Mb.js} +5 -5
- package/dist/{pi-embedded-DLEbV2ey.js → pi-embedded-DlBsi5bC.js} +14 -14
- package/dist/{plugin-registry-DgLnDYKc.js → plugin-registry-2aqMgXpl.js} +1 -1
- package/dist/{plugin-registry-DgnprUWu.js → plugin-registry-C7oTQytM.js} +1 -1
- package/dist/plugin-sdk/{audio-preflight-bl33qm06.js → audio-preflight-BbjERcpR.js} +1 -1
- package/dist/plugin-sdk/{audio-preflight-B3yRlgpY.js → audio-preflight-Vg4EuYfg.js} +1 -1
- package/dist/plugin-sdk/channel-plugin-common.js +1 -1
- package/dist/plugin-sdk/{channel-web-BmhttJUt.js → channel-web-JBsCrpEe.js} +1 -1
- package/dist/plugin-sdk/command-status.js +1 -1
- package/dist/plugin-sdk/index.js +2 -2
- package/dist/plugin-sdk/{openresponses-http-BkfMH5vT.js → openresponses-http-dj-a8V7Z.js} +1 -1
- package/dist/plugin-sdk/{openresponses-http-DVsmkLyj.js → openresponses-http-ihpGpya5.js} +1 -1
- package/dist/plugin-sdk/{pi-model-discovery-runtime-Cv_H-NDs.js → pi-model-discovery-runtime-CddkbYKA.js} +1 -1
- package/dist/plugin-sdk/{registry-Dpq_OloZ.js → registry-BhjWgT6A.js} +8 -8
- package/dist/plugin-sdk/{reply-ceu7kXX1.js → reply-CvmqbVx1.js} +5 -5
- package/dist/plugin-sdk/src/brand.d.ts +2 -2
- package/dist/plugin-sdk/{status-BcZw5R2P.js → status-DsTgMPSP.js} +5 -5
- package/dist/plugin-sdk/{web-DE0yQyjF.js → web-CLXmBst6.js} +1 -1
- package/dist/plugin-sdk/{web-D_ze88LT.js → web-DHSHEbWK.js} +2 -2
- package/dist/{plugins-cli-gUow1y1s.js → plugins-cli-CXa9m7-A.js} +5 -5
- package/dist/{plugins-cli-Bw8rkcLm.js → plugins-cli-Dj-p00ob.js} +5 -5
- package/dist/{program-D7MpzAVA.js → program-B3cXsVO0.js} +7 -7
- package/dist/{program-context-BnMmmcLR.js → program-context-Di7qiwOM.js} +24 -24
- package/dist/{prompt-select-styled-CZx9cLwR.js → prompt-select-styled-8KBIPNro.js} +4 -4
- package/dist/{prompt-select-styled-DEZExK7l.js → prompt-select-styled-C5o0Ff06.js} +4 -4
- package/dist/{pw-ai-Cjyb5RBL.js → pw-ai-BzOzOwqG.js} +1 -1
- package/dist/{register.agent-B9A7SDz_.js → register.agent-BYkIkPHf.js} +8 -8
- package/dist/{register.agent-DlatKWlA.js → register.agent-ll5ykI9x.js} +9 -9
- package/dist/{register.configure-Tez2eNbW.js → register.configure-C0GU1zbA.js} +12 -12
- package/dist/{register.configure-yNuOFQX6.js → register.configure-DubkYh7h.js} +12 -12
- package/dist/{register.maintenance-Q6YI5mk9.js → register.maintenance-Corgqhj5.js} +8 -8
- package/dist/{register.maintenance-BzVw1_e5.js → register.maintenance-Pz6desR4.js} +9 -9
- package/dist/{register.message-Cda69Xe6.js → register.message-COK8wYD_.js} +3 -3
- package/dist/{register.message-D9LYOvON.js → register.message-oi2ewJpl.js} +3 -3
- package/dist/{register.onboard-D1a2rQDh.js → register.onboard-C7ysxMn6.js} +14 -14
- package/dist/{register.onboard-BoLP06ui.js → register.onboard-zwRY0Dsh.js} +14 -14
- package/dist/{register.setup-DhppZhR2.js → register.setup-B1YGF1bz.js} +14 -14
- package/dist/{register.setup-D_qD7Gvk.js → register.setup-J2tUgD6C.js} +14 -14
- package/dist/{register.status-health-sessions-Csah3svc.js → register.status-health-sessions-DCazjMZa.js} +6 -6
- package/dist/{register.status-health-sessions-C0IsjCxS.js → register.status-health-sessions-DmjjMrJZ.js} +6 -6
- package/dist/{register.subclis-C4Dh6aQK.js → register.subclis-Bj8pFyAT.js} +16 -16
- package/dist/{reply-C3PsVpeQ.js → reply-DQfPM5ia.js} +6 -6
- package/dist/{run-main-XXpwNNGn.js → run-main-Cf6N-U3j.js} +14 -14
- package/dist/{runtime-helper-grants-BTCk3CzW.js → runtime-helper-grants-DONSfqWx.js} +1 -1
- package/dist/{runtime-helper-grants-C10OJEih.js → runtime-helper-grants-DzP6dgWv.js} +1 -1
- package/dist/{sandbox-cli-2MuBwFbv.js → sandbox-cli-CqUHfq55.js} +2 -2
- package/dist/{sandbox-cli-Bft002rK.js → sandbox-cli-DHHAXWD8.js} +2 -2
- package/dist/{security-cli-6CmGeQ7I.js → security-cli-CAEEYR8C.js} +3 -3
- package/dist/{security-cli-DpQep_pt.js → security-cli-DNlCSlQR.js} +3 -3
- package/dist/{server-OidOCKoj.js → server-B67PKank.js} +2 -2
- package/dist/{server-D62kYBqw.js → server-RYyJ2HTj.js} +2 -2
- package/dist/{server-cron-DYKXfeLU.js → server-cron-D7rN6EY6.js} +2 -2
- package/dist/{server-cron--g6K0O5V.js → server-cron-V9GBrg1S.js} +2 -2
- package/dist/{server-node-events-DSvH0zky.js → server-node-events-CAG2ic6e.js} +3 -3
- package/dist/{server-node-events-Csbl1Sw5.js → server-node-events-eh5LuLg6.js} +3 -3
- package/dist/{status-CEqTBWFX.js → status-BuGyo-1M.js} +4 -4
- package/dist/{status-cY8_nNEN.js → status-CTveTSuF.js} +1 -1
- package/dist/{status-DDWMOp5e.js → status-DIkbKBxH.js} +4 -4
- package/dist/{status-e52jwOFE.js → status-jb8w0SwX.js} +1 -1
- package/dist/{tui-K3OJBQvo.js → tui-Dinku0o-.js} +1 -1
- package/dist/{tui-cli-DEHMUby1.js → tui-cli-dq7xafwR.js} +3 -3
- package/dist/{tui-cli-CNL0cM4e.js → tui-cli-z5WV3SVg.js} +3 -3
- package/dist/{tui-B5xfA4bO.js → tui-lmdDZi6Y.js} +1 -1
- package/dist/{update-cli-BqiZ9j4m.js → update-cli-BdQ3mRGs.js} +10 -10
- package/dist/{update-cli-Cr7BTqe1.js → update-cli-Cq47v0X_.js} +9 -9
- package/dist/{update-runner-CycjY65W.js → update-runner-BnRMhKva.js} +1 -1
- package/dist/{update-runner-DcpjVgBR.js → update-runner-C0SU1jGT.js} +1 -1
- package/dist/{web-CLOmxVxX.js → web-B9YrSF5u.js} +2 -2
- package/dist/{web-6MnkoZyD.js → web-BmjUKJI0.js} +3 -3
- package/dist/{web-ChgN9rvX.js → web-D9wmjTDC.js} +3 -3
- package/dist/{web-D6CU81WZ.js → web-DaMiLN7K.js} +1 -1
- package/dist/{web-DhwWGdvc.js → web-sBExtZCA.js} +1 -1
- package/dist/{web-search-providers.runtime-D-uepqZ5.js → web-search-providers.runtime-TGCiBzhv.js} +1 -1
- package/dist/{web-search-providers.runtime-u6_OyUd6.js → web-search-providers.runtime-gM-8LIxg.js} +1 -1
- package/package.json +3 -1
- package/scripts/run-node.mjs +294 -0
- package/scripts/start-managed.sh +1528 -0
|
@@ -0,0 +1,1528 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scripts/start-managed.sh
|
|
3
|
+
# Starts the FasedAgent in managed public runtime mode.
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
FASED_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
6
|
+
INSTALL_MARKER_PATH="${FASED_CONFIG_DIR:-$HOME/.fased}/install-complete.json"
|
|
7
|
+
RUN_NODE_SCRIPT="$FASED_ROOT/scripts/run-node.mjs"
|
|
8
|
+
|
|
9
|
+
node_runtime_ok_for() {
|
|
10
|
+
local node_bin="$1"
|
|
11
|
+
[[ -n "$node_bin" && -x "$node_bin" ]] || return 1
|
|
12
|
+
"$node_bin" -e 'const [major, minor] = process.versions.node.split(".").map(Number); if (major < 22 || (major === 22 && minor < 14)) process.exit(2); try { require("node:sqlite"); } catch { process.exit(3); }' >/dev/null 2>&1
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
resolve_node_bin() {
|
|
16
|
+
if node_runtime_ok_for "${FASED_NODE_BIN:-}"; then
|
|
17
|
+
printf '%s\n' "${FASED_NODE_BIN}"
|
|
18
|
+
return 0
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
local candidate
|
|
22
|
+
for candidate in \
|
|
23
|
+
"$HOME"/.nvm/versions/node/*/bin/node \
|
|
24
|
+
"$HOME"/.fnm/node-versions/*/installation/bin/node \
|
|
25
|
+
"$HOME"/.volta/bin/node \
|
|
26
|
+
"$HOME"/.asdf/shims/node \
|
|
27
|
+
"$HOME"/.local/share/mise/shims/node \
|
|
28
|
+
/usr/bin/node \
|
|
29
|
+
/usr/local/bin/node \
|
|
30
|
+
/opt/homebrew/bin/node; do
|
|
31
|
+
[[ -e "$candidate" ]] || continue
|
|
32
|
+
if node_runtime_ok_for "$candidate"; then
|
|
33
|
+
printf '%s\n' "$candidate"
|
|
34
|
+
return 0
|
|
35
|
+
fi
|
|
36
|
+
done
|
|
37
|
+
|
|
38
|
+
if command -v node >/dev/null 2>&1 && node_runtime_ok_for "$(command -v node)"; then
|
|
39
|
+
command -v node
|
|
40
|
+
return 0
|
|
41
|
+
fi
|
|
42
|
+
return 1
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if [[ "${FASED_MANAGED_INTERNAL:-0}" != "1" ]]; then
|
|
46
|
+
if [[ ! -f "$INSTALL_MARKER_PATH" ]]; then
|
|
47
|
+
echo "==> Canonical onboarding not detected ($INSTALL_MARKER_PATH missing)."
|
|
48
|
+
echo "==> Recommended first run: $FASED_ROOT/install.sh"
|
|
49
|
+
if [[ "${FASED_AUTO_INSTALL_ON_START:-0}" == "1" ]]; then
|
|
50
|
+
echo "==> Auto-bootstrap enabled (FASED_AUTO_INSTALL_ON_START=1): running installer in no-start mode..."
|
|
51
|
+
"$FASED_ROOT/install.sh" --no-start
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
echo "==> Delegating to managed orchestrator (node $RUN_NODE_SCRIPT managed up)..."
|
|
55
|
+
NODE_BIN="$(resolve_node_bin || true)"
|
|
56
|
+
if [[ -z "$NODE_BIN" ]]; then
|
|
57
|
+
echo "==> ERROR: compatible node binary not found. Install Node 24 or Node >=22.14.0 with node:sqlite."
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
export PATH="$(dirname "$NODE_BIN"):$PATH"
|
|
61
|
+
FASED_SKIP_BUILD="${FASED_SKIP_BUILD:-1}" exec "$NODE_BIN" "$RUN_NODE_SCRIPT" managed up "$@"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
set -euo pipefail
|
|
65
|
+
|
|
66
|
+
NODE_BIN="$(resolve_node_bin || true)"
|
|
67
|
+
if [[ -z "$NODE_BIN" ]]; then
|
|
68
|
+
echo "[managed] ERROR: compatible node binary not found. Install Node 24 or Node >=22.14.0 with node:sqlite, then reinstall or restart the gateway service."
|
|
69
|
+
exit 1
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Config
|
|
73
|
+
export PATH="$(dirname "$NODE_BIN"):$HOME/.zrok/bin:$PATH"
|
|
74
|
+
export FASED_GATEWAY_MODE=managed
|
|
75
|
+
export FASED_GATEWAY_FAST_START="${FASED_GATEWAY_FAST_START:-1}"
|
|
76
|
+
export FASED_GATEWAY_STARTUP_TRACE="${FASED_GATEWAY_STARTUP_TRACE:-1}"
|
|
77
|
+
export FASED_DISABLE_CONTROL_UI_AUTOBUILD="${FASED_DISABLE_CONTROL_UI_AUTOBUILD:-1}"
|
|
78
|
+
export FASED_FEDERATION_URL=https://ff1.fased.app
|
|
79
|
+
export FASED_GATEWAY_PORT="${FASED_GATEWAY_PORT:-18789}"
|
|
80
|
+
export FASED_CONFIG_DIR="${FASED_CONFIG_DIR:-$HOME/.fased}"
|
|
81
|
+
FASED_GATEWAY_MAX_OLD_SPACE_MB="${FASED_GATEWAY_MAX_OLD_SPACE_MB:-1024}"
|
|
82
|
+
if [[ "${NODE_OPTIONS:-}" != *"--max-old-space-size="* ]]; then
|
|
83
|
+
export NODE_OPTIONS="${NODE_OPTIONS:+$NODE_OPTIONS }--max-old-space-size=${FASED_GATEWAY_MAX_OLD_SPACE_MB}"
|
|
84
|
+
fi
|
|
85
|
+
TOKEN_PATH="$FASED_CONFIG_DIR/federation/access-token.json"
|
|
86
|
+
GW_TOKEN_PATH="$FASED_CONFIG_DIR/gateway-secret"
|
|
87
|
+
INITIAL_TOKEN_SIG=""
|
|
88
|
+
LOG_DIR="${FASED_LOG_DIR:-$FASED_CONFIG_DIR/logs}"
|
|
89
|
+
GATEWAY_BOOT_LOG="$LOG_DIR/start-managed-gateway.log"
|
|
90
|
+
ZROK_RUNTIME_LOG="$LOG_DIR/start-managed-zrok.log"
|
|
91
|
+
WALLET_SETUP_LOG="$LOG_DIR/start-managed-wallet.log"
|
|
92
|
+
VERBOSE_STARTUP="${FASED_VERBOSE_STARTUP:-0}"
|
|
93
|
+
# First boots on small VPS can take several minutes (control-ui asset prep, cold fs/cache, etc).
|
|
94
|
+
# Keep timeout generous to avoid restart loops that kill startup progress before listener comes up.
|
|
95
|
+
GATEWAY_READY_TIMEOUT="${FASED_GATEWAY_READY_TIMEOUT:-600}"
|
|
96
|
+
WALLET_BASELINE_MODE="${FASED_WALLET_BASELINE_MODE:-enforce}" # enforce|skip
|
|
97
|
+
WALLET_BASELINE_TIMEOUT_SECONDS="${FASED_WALLET_BASELINE_TIMEOUT_SECONDS:-25}"
|
|
98
|
+
SIGNERD_READY_TIMEOUT_SECONDS="${FASED_SIGNERD_READY_TIMEOUT_SECONDS:-10}"
|
|
99
|
+
ZROK_MONITOR_PID_FILE="$FASED_CONFIG_DIR/.zrok-monitor.pid"
|
|
100
|
+
ZROK_MONITOR_PID=""
|
|
101
|
+
ZROK_INITIAL_START_RETRIES="${FASED_ZROK_INITIAL_START_RETRIES:-5}"
|
|
102
|
+
ZROK_INITIAL_START_RETRY_DELAY_SECONDS="${FASED_ZROK_INITIAL_START_RETRY_DELAY_SECONDS:-2}"
|
|
103
|
+
CLOCK_SYNC_SKEW_THRESHOLD_SECONDS="${FASED_CLOCK_SYNC_SKEW_THRESHOLD_SECONDS:-2}"
|
|
104
|
+
|
|
105
|
+
resolve_gateway_cli_entry() {
|
|
106
|
+
local candidates=(
|
|
107
|
+
"$FASED_ROOT/dist/index.js"
|
|
108
|
+
"$FASED_ROOT/dist/index.mjs"
|
|
109
|
+
"$FASED_ROOT/dist/entry.js"
|
|
110
|
+
"$FASED_ROOT/dist/entry.mjs"
|
|
111
|
+
)
|
|
112
|
+
local candidate
|
|
113
|
+
for candidate in "${candidates[@]}"; do
|
|
114
|
+
if [[ -f "$candidate" ]]; then
|
|
115
|
+
printf "%s" "$candidate"
|
|
116
|
+
return 0
|
|
117
|
+
fi
|
|
118
|
+
done
|
|
119
|
+
return 1
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
mask_secret() {
|
|
123
|
+
local raw="$1"
|
|
124
|
+
local n=${#raw}
|
|
125
|
+
if [[ $n -le 10 ]]; then
|
|
126
|
+
printf "%s" "$raw"
|
|
127
|
+
return
|
|
128
|
+
fi
|
|
129
|
+
printf "%s...%s" "${raw:0:6}" "${raw: -4}"
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
is_gateway_listener_ready() {
|
|
133
|
+
(echo >"/dev/tcp/127.0.0.1/${FASED_GATEWAY_PORT}") >/dev/null 2>&1
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
abs_int() {
|
|
137
|
+
local value="${1:-0}"
|
|
138
|
+
value="${value#-}"
|
|
139
|
+
if [[ -z "$value" ]]; then
|
|
140
|
+
value="0"
|
|
141
|
+
fi
|
|
142
|
+
printf '%s\n' "$value"
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
can_run_privileged_time_sync_command() {
|
|
146
|
+
if [[ "$(id -u)" -eq 0 ]]; then
|
|
147
|
+
return 0
|
|
148
|
+
fi
|
|
149
|
+
command -v sudo >/dev/null 2>&1 && sudo -n true >/dev/null 2>&1
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
run_privileged_time_sync_command() {
|
|
153
|
+
if [[ "$(id -u)" -eq 0 ]]; then
|
|
154
|
+
"$@"
|
|
155
|
+
return $?
|
|
156
|
+
fi
|
|
157
|
+
sudo -n "$@"
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
fetch_remote_epoch_from_http_date() {
|
|
161
|
+
local url="$1"
|
|
162
|
+
local date_header=""
|
|
163
|
+
local remote_epoch=""
|
|
164
|
+
date_header="$(
|
|
165
|
+
curl -fsSI --max-time 10 "$url" 2>/dev/null \
|
|
166
|
+
| tr -d '\r' \
|
|
167
|
+
| awk 'BEGIN{IGNORECASE=1} /^Date:/ { sub(/^Date:[[:space:]]*/, "", $0); print; exit }'
|
|
168
|
+
)"
|
|
169
|
+
if [[ -z "$date_header" ]]; then
|
|
170
|
+
return 1
|
|
171
|
+
fi
|
|
172
|
+
remote_epoch="$(date -u -d "$date_header" +%s 2>/dev/null || true)"
|
|
173
|
+
if [[ -z "$remote_epoch" ]]; then
|
|
174
|
+
return 1
|
|
175
|
+
fi
|
|
176
|
+
printf '%s\n' "$remote_epoch"
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
measure_managed_clock_skew_seconds() {
|
|
180
|
+
local probe_urls=(
|
|
181
|
+
"${ZROK_API_ENDPOINT:-https://zrok.fased.app}"
|
|
182
|
+
"${FASED_FEDERATION_URL:-https://ff1.fased.app}"
|
|
183
|
+
)
|
|
184
|
+
local url=""
|
|
185
|
+
local remote_epoch=""
|
|
186
|
+
local local_epoch=""
|
|
187
|
+
for url in "${probe_urls[@]}"; do
|
|
188
|
+
remote_epoch="$(fetch_remote_epoch_from_http_date "$url" || true)"
|
|
189
|
+
if [[ -z "$remote_epoch" ]]; then
|
|
190
|
+
continue
|
|
191
|
+
fi
|
|
192
|
+
local_epoch="$(date -u +%s)"
|
|
193
|
+
printf '%s\n' "$((remote_epoch - local_epoch))"
|
|
194
|
+
return 0
|
|
195
|
+
done
|
|
196
|
+
return 1
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
zrok_log_indicates_clock_skew() {
|
|
200
|
+
tail -n 80 "$ZROK_RUNTIME_LOG" 2>/dev/null | grep -Fqi "issuedAt of token is in the future"
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
attempt_managed_clock_sync_repair() {
|
|
204
|
+
if ! can_run_privileged_time_sync_command; then
|
|
205
|
+
return 1
|
|
206
|
+
fi
|
|
207
|
+
echo "[tunnel] Attempting automatic host clock sync repair..."
|
|
208
|
+
run_privileged_time_sync_command timedatectl set-ntp true >/dev/null 2>&1 || true
|
|
209
|
+
run_privileged_time_sync_command systemctl restart systemd-timesyncd >/dev/null 2>&1 || \
|
|
210
|
+
run_privileged_time_sync_command systemctl restart chronyd >/dev/null 2>&1 || true
|
|
211
|
+
if command -v chronyc >/dev/null 2>&1; then
|
|
212
|
+
run_privileged_time_sync_command chronyc -a makestep >/dev/null 2>&1 || true
|
|
213
|
+
fi
|
|
214
|
+
sleep 2
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
ensure_managed_clock_sync() {
|
|
218
|
+
local threshold="${CLOCK_SYNC_SKEW_THRESHOLD_SECONDS:-2}"
|
|
219
|
+
local skew_seconds=""
|
|
220
|
+
local remaining_skew=""
|
|
221
|
+
|
|
222
|
+
skew_seconds="$(measure_managed_clock_skew_seconds || true)"
|
|
223
|
+
if [[ -z "$skew_seconds" ]]; then
|
|
224
|
+
return 0
|
|
225
|
+
fi
|
|
226
|
+
if (( $(abs_int "$skew_seconds") < threshold )); then
|
|
227
|
+
return 0
|
|
228
|
+
fi
|
|
229
|
+
|
|
230
|
+
echo "[tunnel] WARNING: Host clock skew detected (${skew_seconds}s versus public control plane)."
|
|
231
|
+
if ! attempt_managed_clock_sync_repair; then
|
|
232
|
+
echo "[tunnel] WARNING: Automatic host clock repair requires root or passwordless sudo."
|
|
233
|
+
return 1
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
remaining_skew="$(measure_managed_clock_skew_seconds || true)"
|
|
237
|
+
if [[ -n "$remaining_skew" ]] && (( $(abs_int "$remaining_skew") < threshold )); then
|
|
238
|
+
echo "[tunnel] Host clock sync repaired (remaining skew ${remaining_skew}s)."
|
|
239
|
+
return 0
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
echo "[tunnel] WARNING: Host clock repair did not fully converge (remaining skew ${remaining_skew:-unknown}s)."
|
|
243
|
+
return 1
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
start_initial_zrok_share() {
|
|
247
|
+
local slug="$1"
|
|
248
|
+
local attempts="${ZROK_INITIAL_START_RETRIES:-5}"
|
|
249
|
+
local retry_delay="${ZROK_INITIAL_START_RETRY_DELAY_SECONDS:-2}"
|
|
250
|
+
local attempt=1
|
|
251
|
+
|
|
252
|
+
while (( attempt <= attempts )); do
|
|
253
|
+
echo "[tunnel] Starting tunnel for ${slug} (attempt ${attempt}/${attempts})..."
|
|
254
|
+
"$ZROK_BIN" share reserved "$RES_TOKEN" --headless >>"$ZROK_RUNTIME_LOG" 2>&1 &
|
|
255
|
+
ZROK_PID=$!
|
|
256
|
+
echo "$ZROK_PID" > "$FASED_CONFIG_DIR/.zrok-pid"
|
|
257
|
+
sleep 5
|
|
258
|
+
|
|
259
|
+
if ! kill -0 "$AGENT_PID" 2>/dev/null; then
|
|
260
|
+
if is_gateway_listener_ready; then
|
|
261
|
+
echo "[gateway] WARNING: Gateway launcher exited, but listener is healthy on 127.0.0.1:${FASED_GATEWAY_PORT}; continuing."
|
|
262
|
+
else
|
|
263
|
+
echo "[gateway] ERROR: Gateway process exited during tunnel startup."
|
|
264
|
+
if [[ "$VERBOSE_STARTUP" != "1" ]]; then
|
|
265
|
+
echo "[debug] Last gateway startup logs:"
|
|
266
|
+
tail -n 80 "$GATEWAY_BOOT_LOG" || true
|
|
267
|
+
fi
|
|
268
|
+
return 1
|
|
269
|
+
fi
|
|
270
|
+
fi
|
|
271
|
+
|
|
272
|
+
if ! is_gateway_listener_ready; then
|
|
273
|
+
echo "[gateway] ERROR: Gateway listener on 127.0.0.1:${FASED_GATEWAY_PORT} is not reachable."
|
|
274
|
+
if [[ "$VERBOSE_STARTUP" != "1" ]]; then
|
|
275
|
+
echo "[debug] Last gateway startup logs:"
|
|
276
|
+
tail -n 80 "$GATEWAY_BOOT_LOG" || true
|
|
277
|
+
fi
|
|
278
|
+
return 1
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
if kill -0 "$ZROK_PID" 2>/dev/null; then
|
|
282
|
+
echo "[tunnel] ✓ Tunnel active."
|
|
283
|
+
return 0
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
echo "[tunnel] WARNING: zrok share died during startup."
|
|
287
|
+
if [[ "$VERBOSE_STARTUP" != "1" ]]; then
|
|
288
|
+
echo "[debug] Last zrok startup logs:"
|
|
289
|
+
tail -n 40 "$ZROK_RUNTIME_LOG" || true
|
|
290
|
+
fi
|
|
291
|
+
if zrok_log_indicates_clock_skew; then
|
|
292
|
+
echo "[tunnel] Detected zrok auth failure caused by host clock skew."
|
|
293
|
+
ensure_managed_clock_sync || true
|
|
294
|
+
fi
|
|
295
|
+
|
|
296
|
+
if (( attempt == attempts )); then
|
|
297
|
+
echo "[tunnel] ERROR: zrok share failed to stay up after ${attempts} attempts."
|
|
298
|
+
return 1
|
|
299
|
+
fi
|
|
300
|
+
|
|
301
|
+
echo "[tunnel] Retrying zrok startup in ${retry_delay}s..."
|
|
302
|
+
sleep "$retry_delay"
|
|
303
|
+
if [[ "$retry_delay" =~ ^[0-9]+$ ]] && (( retry_delay < 8 )); then
|
|
304
|
+
retry_delay=$((retry_delay * 2))
|
|
305
|
+
fi
|
|
306
|
+
attempt=$((attempt + 1))
|
|
307
|
+
done
|
|
308
|
+
|
|
309
|
+
return 1
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
force_stop_local_gateway() {
|
|
313
|
+
if ! is_gateway_listener_ready; then
|
|
314
|
+
return 0
|
|
315
|
+
fi
|
|
316
|
+
if command -v fuser >/dev/null 2>&1; then
|
|
317
|
+
fuser -k "${FASED_GATEWAY_PORT}/tcp" >/dev/null 2>&1 || true
|
|
318
|
+
fi
|
|
319
|
+
if command -v lsof >/dev/null 2>&1; then
|
|
320
|
+
mapfile -t PORT_PIDS < <(lsof -t -iTCP:"${FASED_GATEWAY_PORT}" -sTCP:LISTEN 2>/dev/null || true)
|
|
321
|
+
if [[ ${#PORT_PIDS[@]} -gt 0 ]]; then
|
|
322
|
+
kill "${PORT_PIDS[@]}" >/dev/null 2>&1 || true
|
|
323
|
+
fi
|
|
324
|
+
fi
|
|
325
|
+
pkill -f "scripts/run-node.mjs gateway" >/dev/null 2>&1 || true
|
|
326
|
+
pkill -f "fased-gateway" >/dev/null 2>&1 || true
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
wait_for_gateway_listener() {
|
|
330
|
+
local retries="$1"
|
|
331
|
+
local count=0
|
|
332
|
+
echo "==> Waiting for local gateway listener on 127.0.0.1:${FASED_GATEWAY_PORT} (max ${retries}s)..."
|
|
333
|
+
while true; do
|
|
334
|
+
if is_gateway_listener_ready; then
|
|
335
|
+
return 0
|
|
336
|
+
fi
|
|
337
|
+
if ! kill -0 "$AGENT_PID" 2>/dev/null; then
|
|
338
|
+
echo "[gateway] ERROR: Gateway process exited before listener became ready."
|
|
339
|
+
if [[ "$VERBOSE_STARTUP" != "1" ]]; then
|
|
340
|
+
echo "[debug] Last gateway startup logs:"
|
|
341
|
+
tail -n 80 "$GATEWAY_BOOT_LOG" || true
|
|
342
|
+
fi
|
|
343
|
+
return 1
|
|
344
|
+
fi
|
|
345
|
+
sleep 1
|
|
346
|
+
count=$((count + 1))
|
|
347
|
+
if [[ $count -ge $retries ]]; then
|
|
348
|
+
echo "[gateway] ERROR: Timed out waiting for local listener on port ${FASED_GATEWAY_PORT}."
|
|
349
|
+
if [[ "$VERBOSE_STARTUP" != "1" ]]; then
|
|
350
|
+
echo "[debug] Last gateway startup logs:"
|
|
351
|
+
tail -n 80 "$GATEWAY_BOOT_LOG" || true
|
|
352
|
+
fi
|
|
353
|
+
return 1
|
|
354
|
+
fi
|
|
355
|
+
done
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
start_gateway_if_needed() {
|
|
359
|
+
if [[ -n "${AGENT_PID:-}" ]] && kill -0 "$AGENT_PID" 2>/dev/null; then
|
|
360
|
+
if is_gateway_listener_ready; then
|
|
361
|
+
return 0
|
|
362
|
+
fi
|
|
363
|
+
fi
|
|
364
|
+
|
|
365
|
+
echo "==> Starting FasedAgent Gateway..."
|
|
366
|
+
# Capture pre-existing token fingerprint so we can detect a real refresh.
|
|
367
|
+
if [[ -f "$TOKEN_PATH" ]]; then
|
|
368
|
+
INITIAL_TOKEN_SIG=$(sha256sum "$TOKEN_PATH" | awk '{print $1}')
|
|
369
|
+
fi
|
|
370
|
+
GATEWAY_ENTRY="$(resolve_gateway_cli_entry || true)"
|
|
371
|
+
if [[ -z "$GATEWAY_ENTRY" ]]; then
|
|
372
|
+
echo "[gateway] ERROR: Unable to resolve built gateway entry under $FASED_ROOT/dist"
|
|
373
|
+
exit 1
|
|
374
|
+
fi
|
|
375
|
+
if [[ "$VERBOSE_STARTUP" == "1" ]]; then
|
|
376
|
+
FASED_SKIP_BUILD=1 "$NODE_BIN" "$GATEWAY_ENTRY" gateway --allow-unconfigured --force --bind loopback --port "$FASED_GATEWAY_PORT" &
|
|
377
|
+
else
|
|
378
|
+
: > "$GATEWAY_BOOT_LOG"
|
|
379
|
+
FASED_SKIP_BUILD=1 "$NODE_BIN" "$GATEWAY_ENTRY" gateway --allow-unconfigured --force --bind loopback --port "$FASED_GATEWAY_PORT" >>"$GATEWAY_BOOT_LOG" 2>&1 &
|
|
380
|
+
fi
|
|
381
|
+
AGENT_PID=$!
|
|
382
|
+
|
|
383
|
+
if ! wait_for_gateway_listener "$GATEWAY_READY_TIMEOUT"; then
|
|
384
|
+
exit 1
|
|
385
|
+
fi
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
# Ensure Gateway Token exists
|
|
389
|
+
mkdir -p "$FASED_CONFIG_DIR"
|
|
390
|
+
mkdir -p "$LOG_DIR"
|
|
391
|
+
: > "$ZROK_RUNTIME_LOG"
|
|
392
|
+
: > "$WALLET_SETUP_LOG"
|
|
393
|
+
SERVICE_GATEWAY_TOKEN="${FASED_GATEWAY_TOKEN:-}"
|
|
394
|
+
if [ ! -f "$GW_TOKEN_PATH" ]; then
|
|
395
|
+
if [[ -n "$SERVICE_GATEWAY_TOKEN" ]]; then
|
|
396
|
+
printf '%s\n' "$SERVICE_GATEWAY_TOKEN" > "$GW_TOKEN_PATH"
|
|
397
|
+
else
|
|
398
|
+
openssl rand -hex 32 > "$GW_TOKEN_PATH"
|
|
399
|
+
fi
|
|
400
|
+
fi
|
|
401
|
+
export FASED_GATEWAY_TOKEN="$(tr -d '\n' < "$GW_TOKEN_PATH")"
|
|
402
|
+
|
|
403
|
+
# 0. Clean up stale state
|
|
404
|
+
# rm -f "$TOKEN_PATH" # (Disabled: Preserve identity for stable Zrok tunnels)
|
|
405
|
+
# Note: --force on gateway start will handle the port conflict,
|
|
406
|
+
# but we still kill zrok to ensure no tunnel conflicts.
|
|
407
|
+
if [[ "${FASED_MANAGED_INTERNAL:-0}" != "1" ]]; then
|
|
408
|
+
FASED_SKIP_BUILD=1 "$NODE_BIN" "$RUN_NODE_SCRIPT" gateway stop >/dev/null 2>&1 || true
|
|
409
|
+
fi
|
|
410
|
+
force_stop_local_gateway
|
|
411
|
+
|
|
412
|
+
# Start the dashboard backend before slower hosted setup. Signer, wallet,
|
|
413
|
+
# federation, and tunnel work must not prevent the owner from opening the UI.
|
|
414
|
+
start_gateway_if_needed
|
|
415
|
+
|
|
416
|
+
# 0a. Start local key signer daemon (fased-signerd) if available
|
|
417
|
+
SIGNERD_BIN="${FASED_CONFIG_DIR}/bin/fased-signerd"
|
|
418
|
+
SIGNERD_SOCKET="${FASED_CONFIG_DIR}/wallet/local-signer.sock"
|
|
419
|
+
SIGNERD_BACKEND_SOCKET="$SIGNERD_SOCKET"
|
|
420
|
+
SIGNERD_MATERIAL_DIR="${FASED_CONFIG_DIR}/wallet"
|
|
421
|
+
SIGNERD_PASSPHRASE_FILE="$SIGNERD_MATERIAL_DIR/passphrase"
|
|
422
|
+
SIGNERD_EVM_KEYSTORE="$SIGNERD_MATERIAL_DIR/keystore-evm.v1.enc"
|
|
423
|
+
SIGNERD_SOL_KEYSTORE="$SIGNERD_MATERIAL_DIR/keystore-solana.v1.enc"
|
|
424
|
+
SIGNERD_LOG="${LOG_DIR}/fased-signerd.log"
|
|
425
|
+
SIGNERD_BROKER_LOG="${LOG_DIR}/local-signer-broker.log"
|
|
426
|
+
SIGNER_ISOLATION_HELPER="/usr/local/sbin/fased-signer-isolation"
|
|
427
|
+
CONFIG_JSON="${FASED_CONFIG_DIR}/fased.json"
|
|
428
|
+
SIGNERD_ENV_FILE="${FASED_CONFIG_DIR}/wallet/signer.env"
|
|
429
|
+
WALLET_REGISTRY_JSON="${FASED_CONFIG_DIR}/wallet/provider-registry.v1.json"
|
|
430
|
+
SIGNERD_STARTUP_MODE="disabled"
|
|
431
|
+
SIGNERD_ERROR=""
|
|
432
|
+
|
|
433
|
+
mark_signerd_degraded() {
|
|
434
|
+
SIGNERD_STARTUP_MODE="degraded"
|
|
435
|
+
SIGNERD_ERROR="$1"
|
|
436
|
+
echo "[signerd] WARNING: $1"
|
|
437
|
+
echo "[signerd] Dashboard stays online; wallet actions remain degraded until signer is fixed."
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
load_wallet_signer_env_from_config() {
|
|
441
|
+
if [[ ! -f "$CONFIG_JSON" ]]; then
|
|
442
|
+
return 0
|
|
443
|
+
fi
|
|
444
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
445
|
+
return 0
|
|
446
|
+
fi
|
|
447
|
+
while IFS= read -r line; do
|
|
448
|
+
[[ "$line" == *=* ]] || continue
|
|
449
|
+
local key="${line%%=*}"
|
|
450
|
+
local value="${line#*=}"
|
|
451
|
+
if [[ "$key" =~ ^FASED_WALLET_(EVM|SOLANA)_(RPC_URL|KEYSTORE_PATH)(__[A-Za-z0-9_-]+)?$ ]] \
|
|
452
|
+
|| [[ "$key" == "FASED_WALLET_CHAINS" ]] \
|
|
453
|
+
|| [[ "$key" == "FASED_WALLET_PASSPHRASE_FILE" ]] \
|
|
454
|
+
|| [[ "$key" == "FASED_WALLET_LOCAL_SIGNER_SOCKET" ]] \
|
|
455
|
+
|| [[ "$key" == "FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET" ]] \
|
|
456
|
+
|| [[ "$key" == "FASED_WALLET_SIGNER_STATE_DIR" ]] \
|
|
457
|
+
|| [[ "$key" == "FASED_WALLET_LOCAL_SIGNER_RUN_AS_USER" ]] \
|
|
458
|
+
|| [[ "$key" == "FASED_WALLET_LOCAL_SIGNER_BIN" ]] \
|
|
459
|
+
|| [[ "$key" =~ ^FASED_WALLET_LOCAL_SIGNER_(ROLE|DIRECT_SIGNING|CAPS_ENABLED|SOLANA_MAX_PER_TX|SOLANA_MAX_DAILY|SOLANA_ALLOW_PROGRAMS)(__[A-Za-z0-9_-]+)?$ ]]; then
|
|
460
|
+
export "$key=$value"
|
|
461
|
+
fi
|
|
462
|
+
done < <(
|
|
463
|
+
jq -r '
|
|
464
|
+
.env.vars // {}
|
|
465
|
+
| to_entries[]
|
|
466
|
+
| select(
|
|
467
|
+
.key
|
|
468
|
+
| test("^FASED_WALLET_(EVM|SOLANA)_(RPC_URL|KEYSTORE_PATH)(__[A-Za-z0-9_-]+)?$")
|
|
469
|
+
or . == "FASED_WALLET_CHAINS"
|
|
470
|
+
or . == "FASED_WALLET_PASSPHRASE_FILE"
|
|
471
|
+
or . == "FASED_WALLET_LOCAL_SIGNER_SOCKET"
|
|
472
|
+
or . == "FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET"
|
|
473
|
+
or . == "FASED_WALLET_SIGNER_STATE_DIR"
|
|
474
|
+
or . == "FASED_WALLET_LOCAL_SIGNER_RUN_AS_USER"
|
|
475
|
+
or . == "FASED_WALLET_LOCAL_SIGNER_BIN"
|
|
476
|
+
or test("^FASED_WALLET_LOCAL_SIGNER_(ROLE|DIRECT_SIGNING|CAPS_ENABLED|SOLANA_MAX_PER_TX|SOLANA_MAX_DAILY|SOLANA_ALLOW_PROGRAMS)(__[A-Za-z0-9_-]+)?$")
|
|
477
|
+
)
|
|
478
|
+
| "\(.key)=\(.value|tostring)"
|
|
479
|
+
' "$CONFIG_JSON" 2>/dev/null || true
|
|
480
|
+
)
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
load_wallet_signer_env_file() {
|
|
484
|
+
if [[ ! -f "$SIGNERD_ENV_FILE" ]]; then
|
|
485
|
+
return 0
|
|
486
|
+
fi
|
|
487
|
+
|
|
488
|
+
set -a
|
|
489
|
+
# signer.env is generated by onboarding.wallet.ts; only import export lines, not the launch command.
|
|
490
|
+
source <(grep -E '^export FASED_WALLET_' "$SIGNERD_ENV_FILE" || true)
|
|
491
|
+
set +a
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
has_scoped_wallet_env_value() {
|
|
495
|
+
local prefix="$1"
|
|
496
|
+
local name=""
|
|
497
|
+
while IFS= read -r name; do
|
|
498
|
+
[[ -n "$name" ]] || continue
|
|
499
|
+
if [[ -n "${!name:-}" ]]; then
|
|
500
|
+
return 0
|
|
501
|
+
fi
|
|
502
|
+
done < <(compgen -A variable "${prefix}__" || true)
|
|
503
|
+
return 1
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
normalize_wallet_env_suffix() {
|
|
507
|
+
local raw="${1:-}"
|
|
508
|
+
printf '%s' "$raw" \
|
|
509
|
+
| tr '[:upper:]' '[:lower:]' \
|
|
510
|
+
| sed -E 's/[^a-z0-9]+/_/g; s/^_+//; s/_+$//'
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
normalize_wallet_filename_id() {
|
|
514
|
+
local raw="${1:-}"
|
|
515
|
+
printf '%s' "$raw" \
|
|
516
|
+
| tr '[:upper:]' '[:lower:]' \
|
|
517
|
+
| sed -E 's/[^a-z0-9_-]+/-/g; s/^-+//; s/-+$//'
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
hydrate_scoped_wallet_keystore_env_from_registry() {
|
|
521
|
+
local material_dir="$1"
|
|
522
|
+
if [[ ! -f "$WALLET_REGISTRY_JSON" ]] || ! command -v jq >/dev/null 2>&1; then
|
|
523
|
+
return 0
|
|
524
|
+
fi
|
|
525
|
+
|
|
526
|
+
while IFS=$'\t' read -r wallet_id provider_id; do
|
|
527
|
+
[[ -n "$wallet_id" ]] || continue
|
|
528
|
+
if [[ "$provider_id" != "local-socket-signer" && "$provider_id" != "embedded-keystore" ]]; then
|
|
529
|
+
continue
|
|
530
|
+
fi
|
|
531
|
+
local env_suffix
|
|
532
|
+
local file_suffix
|
|
533
|
+
env_suffix="$(normalize_wallet_env_suffix "$wallet_id")"
|
|
534
|
+
file_suffix="$(normalize_wallet_filename_id "$wallet_id")"
|
|
535
|
+
[[ -n "$env_suffix" && -n "$file_suffix" ]] || continue
|
|
536
|
+
|
|
537
|
+
local sol_key="FASED_WALLET_SOLANA_KEYSTORE_PATH__${env_suffix^^}"
|
|
538
|
+
if [[ -z "${!sol_key:-}" ]]; then
|
|
539
|
+
local sol_candidate="${material_dir}/keystore-solana-${file_suffix}.v1.enc"
|
|
540
|
+
if [[ -f "$sol_candidate" ]]; then
|
|
541
|
+
export "$sol_key=$sol_candidate"
|
|
542
|
+
fi
|
|
543
|
+
fi
|
|
544
|
+
|
|
545
|
+
local evm_key="FASED_WALLET_EVM_KEYSTORE_PATH__${env_suffix^^}"
|
|
546
|
+
if [[ -z "${!evm_key:-}" ]]; then
|
|
547
|
+
local evm_candidate="${material_dir}/keystore-evm-${file_suffix}.v1.enc"
|
|
548
|
+
if [[ -f "$evm_candidate" ]]; then
|
|
549
|
+
export "$evm_key=$evm_candidate"
|
|
550
|
+
fi
|
|
551
|
+
fi
|
|
552
|
+
done < <(
|
|
553
|
+
jq -r '
|
|
554
|
+
(.wallets // [])
|
|
555
|
+
| .[]
|
|
556
|
+
| select(.id != null and .providerId != null)
|
|
557
|
+
| "\(.id|tostring)\t\(.providerId|tostring)"
|
|
558
|
+
' "$WALLET_REGISTRY_JSON" 2>/dev/null || true
|
|
559
|
+
)
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
resolve_signerd_keystore_export() {
|
|
563
|
+
local explicit_value="$1"
|
|
564
|
+
local scoped_prefix="$2"
|
|
565
|
+
local default_path="$3"
|
|
566
|
+
|
|
567
|
+
if has_scoped_wallet_env_value "$scoped_prefix"; then
|
|
568
|
+
printf '\n'
|
|
569
|
+
return 0
|
|
570
|
+
fi
|
|
571
|
+
|
|
572
|
+
if [[ -n "$explicit_value" && "$explicit_value" != "$default_path" ]]; then
|
|
573
|
+
printf '%s\n' "$explicit_value"
|
|
574
|
+
return 0
|
|
575
|
+
fi
|
|
576
|
+
|
|
577
|
+
if [[ -n "$explicit_value" ]]; then
|
|
578
|
+
printf '%s\n' "$explicit_value"
|
|
579
|
+
return 0
|
|
580
|
+
fi
|
|
581
|
+
|
|
582
|
+
printf '%s\n' "$default_path"
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
resolve_wallet_chains_from_config() {
|
|
586
|
+
if [[ ! -f "$CONFIG_JSON" ]] || ! command -v jq >/dev/null 2>&1; then
|
|
587
|
+
printf '%s\n' "evm,solana"
|
|
588
|
+
return 0
|
|
589
|
+
fi
|
|
590
|
+
jq -r '
|
|
591
|
+
[
|
|
592
|
+
((.env.vars.FASED_WALLET_CHAINS // "") | split(",")[]?),
|
|
593
|
+
(.wallet.runtime.chains // [] | .[]?)
|
|
594
|
+
]
|
|
595
|
+
| map((.|tostring|ascii_downcase|gsub("^\\s+|\\s+$"; "")))
|
|
596
|
+
| map(select(. == "evm" or . == "solana"))
|
|
597
|
+
| unique
|
|
598
|
+
| if length > 0 then join(",") else "evm,solana" end
|
|
599
|
+
' "$CONFIG_JSON" 2>/dev/null || printf '%s\n' "evm,solana"
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
resolve_local_signer_sidecar_path() {
|
|
603
|
+
local socket_path="$1"
|
|
604
|
+
local kind="$2"
|
|
605
|
+
local socket_dir
|
|
606
|
+
local socket_name
|
|
607
|
+
local sidecar_base
|
|
608
|
+
socket_dir="$(dirname "$socket_path")"
|
|
609
|
+
socket_name="$(basename "$socket_path")"
|
|
610
|
+
sidecar_base="${socket_name%.sock}"
|
|
611
|
+
if [[ "$sidecar_base" == "$socket_name" ]]; then
|
|
612
|
+
sidecar_base="$socket_name"
|
|
613
|
+
fi
|
|
614
|
+
if [[ "$kind" == "pid" ]]; then
|
|
615
|
+
printf '%s\n' "${socket_dir}/${sidecar_base}.pid"
|
|
616
|
+
else
|
|
617
|
+
printf '%s\n' "${socket_dir}/${sidecar_base}.audit.jsonl"
|
|
618
|
+
fi
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
registry_has_local_signer_wallet() {
|
|
622
|
+
if [[ ! -f "$WALLET_REGISTRY_JSON" ]] || ! command -v jq >/dev/null 2>&1; then
|
|
623
|
+
return 1
|
|
624
|
+
fi
|
|
625
|
+
jq -e '
|
|
626
|
+
(.wallets // [])
|
|
627
|
+
| any(.providerId == "local-socket-signer")
|
|
628
|
+
' "$WALLET_REGISTRY_JSON" >/dev/null 2>&1
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
has_local_signer_keystore_material() {
|
|
632
|
+
if [[ -n "${FASED_WALLET_SOLANA_KEYSTORE_PATH:-}" ]] || has_scoped_wallet_env_value "FASED_WALLET_SOLANA_KEYSTORE_PATH"; then
|
|
633
|
+
return 0
|
|
634
|
+
fi
|
|
635
|
+
local candidate
|
|
636
|
+
for candidate in "$SIGNERD_MATERIAL_DIR"/keystore-solana*.v1.enc "$SIGNERD_MATERIAL_DIR"/keystore-evm*.v1.enc; do
|
|
637
|
+
[[ -f "$candidate" ]] && return 0
|
|
638
|
+
done
|
|
639
|
+
return 1
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
should_start_signerd() {
|
|
643
|
+
registry_has_local_signer_wallet || has_local_signer_keystore_material
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
collect_existing_signerd_pids() {
|
|
647
|
+
local pid_file app_pid_file
|
|
648
|
+
pid_file="$(resolve_local_signer_sidecar_path "$SIGNERD_BACKEND_SOCKET" "pid")"
|
|
649
|
+
app_pid_file="$(resolve_local_signer_sidecar_path "$SIGNERD_SOCKET" "pid")"
|
|
650
|
+
{
|
|
651
|
+
if [[ -f "$pid_file" ]]; then
|
|
652
|
+
cat "$pid_file" 2>/dev/null || true
|
|
653
|
+
fi
|
|
654
|
+
if [[ "$app_pid_file" != "$pid_file" && -f "$app_pid_file" ]]; then
|
|
655
|
+
cat "$app_pid_file" 2>/dev/null || true
|
|
656
|
+
fi
|
|
657
|
+
pgrep -f "$SIGNERD_BIN" 2>/dev/null || true
|
|
658
|
+
pgrep -f "wallet signer broker" 2>/dev/null || true
|
|
659
|
+
} | awk '/^[0-9]+$/ { if (!seen[$1]++) print $1 }'
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
count_existing_signerd_pids() {
|
|
663
|
+
local count=0
|
|
664
|
+
while IFS= read -r _pid; do
|
|
665
|
+
count=$((count + 1))
|
|
666
|
+
done < <(collect_existing_signerd_pids)
|
|
667
|
+
printf '%s\n' "$count"
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
dump_existing_signerd_processes() {
|
|
671
|
+
pgrep -af "$SIGNERD_BIN" 2>/dev/null || true
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
stop_existing_signerd() {
|
|
675
|
+
local pid_file app_pid_file
|
|
676
|
+
pid_file="$(resolve_local_signer_sidecar_path "$SIGNERD_BACKEND_SOCKET" "pid")"
|
|
677
|
+
app_pid_file="$(resolve_local_signer_sidecar_path "$SIGNERD_SOCKET" "pid")"
|
|
678
|
+
if signer_isolation_helper_available; then
|
|
679
|
+
run_signer_isolation_helper stop "$SIGNERD_BACKEND_SOCKET" "$pid_file" >/dev/null 2>&1 || true
|
|
680
|
+
if [[ "$SIGNERD_SOCKET" != "$SIGNERD_BACKEND_SOCKET" ]]; then
|
|
681
|
+
run_signer_isolation_helper stop "$SIGNERD_SOCKET" "$app_pid_file" >/dev/null 2>&1 || true
|
|
682
|
+
fi
|
|
683
|
+
return 0
|
|
684
|
+
fi
|
|
685
|
+
local pid=""
|
|
686
|
+
while IFS= read -r pid; do
|
|
687
|
+
[[ "$pid" =~ ^[0-9]+$ ]] || continue
|
|
688
|
+
kill "$pid" >/dev/null 2>&1 || true
|
|
689
|
+
done < <(collect_existing_signerd_pids)
|
|
690
|
+
sleep 0.5
|
|
691
|
+
while IFS= read -r pid; do
|
|
692
|
+
[[ "$pid" =~ ^[0-9]+$ ]] || continue
|
|
693
|
+
kill -9 "$pid" >/dev/null 2>&1 || true
|
|
694
|
+
done < <(collect_existing_signerd_pids)
|
|
695
|
+
rm -f "$SIGNERD_SOCKET" "$SIGNERD_BACKEND_SOCKET" "$pid_file" "$app_pid_file"
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
wait_for_signerd_ready() {
|
|
699
|
+
local retries="$1"
|
|
700
|
+
local count=0
|
|
701
|
+
while true; do
|
|
702
|
+
local active_count
|
|
703
|
+
active_count="$(count_existing_signerd_pids)"
|
|
704
|
+
if [[ "$active_count" == "1" && -S "$SIGNERD_BACKEND_SOCKET" ]]; then
|
|
705
|
+
return 0
|
|
706
|
+
fi
|
|
707
|
+
if [[ "$active_count" -gt 1 ]]; then
|
|
708
|
+
echo "[signerd] ERROR: multiple fased-signerd processes detected; refusing to start gateway."
|
|
709
|
+
dump_existing_signerd_processes
|
|
710
|
+
return 1
|
|
711
|
+
fi
|
|
712
|
+
sleep 1
|
|
713
|
+
count=$((count + 1))
|
|
714
|
+
if [[ $count -ge $retries ]]; then
|
|
715
|
+
echo "[signerd] ERROR: fased-signerd did not become healthy within ${retries}s."
|
|
716
|
+
dump_existing_signerd_processes
|
|
717
|
+
tail -n 40 "$SIGNERD_LOG" 2>/dev/null || true
|
|
718
|
+
return 1
|
|
719
|
+
fi
|
|
720
|
+
done
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
wait_for_signer_broker_ready() {
|
|
724
|
+
local retries="$1"
|
|
725
|
+
local count=0
|
|
726
|
+
while true; do
|
|
727
|
+
if [[ -S "$SIGNERD_SOCKET" ]]; then
|
|
728
|
+
return 0
|
|
729
|
+
fi
|
|
730
|
+
sleep 1
|
|
731
|
+
count=$((count + 1))
|
|
732
|
+
if [[ $count -ge $retries ]]; then
|
|
733
|
+
echo "[signerd] ERROR: signer broker did not create app socket within ${retries}s."
|
|
734
|
+
tail -n 40 "$SIGNERD_BROKER_LOG" 2>/dev/null || true
|
|
735
|
+
return 1
|
|
736
|
+
fi
|
|
737
|
+
done
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
collect_wallet_env_args() {
|
|
741
|
+
local key value
|
|
742
|
+
while IFS='=' read -r key value; do
|
|
743
|
+
[[ "$key" == FASED_WALLET_* ]] || continue
|
|
744
|
+
printf '%s=%s\0' "$key" "$value"
|
|
745
|
+
done < <(env)
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
current_app_user() {
|
|
749
|
+
id -un 2>/dev/null || printf '%s\n' "${USER:-app}"
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
signer_isolation_helper_available() {
|
|
753
|
+
[[ -n "${FASED_WALLET_LOCAL_SIGNER_RUN_AS_USER:-}" ]] || return 1
|
|
754
|
+
[[ -x "$SIGNER_ISOLATION_HELPER" ]] || return 1
|
|
755
|
+
command -v sudo >/dev/null 2>&1 || return 1
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
run_signer_isolation_helper() {
|
|
759
|
+
local app_user
|
|
760
|
+
app_user="$(current_app_user)"
|
|
761
|
+
sudo -n -E "$SIGNER_ISOLATION_HELPER" "$app_user" "$FASED_WALLET_LOCAL_SIGNER_RUN_AS_USER" "$@"
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
start_signerd_process() {
|
|
765
|
+
local pid_file audit_log
|
|
766
|
+
pid_file="$(resolve_local_signer_sidecar_path "$SIGNERD_BACKEND_SOCKET" "pid")"
|
|
767
|
+
audit_log="$(resolve_local_signer_sidecar_path "$SIGNERD_BACKEND_SOCKET" "audit")"
|
|
768
|
+
if [[ -n "${FASED_WALLET_LOCAL_SIGNER_RUN_AS_USER:-}" ]]; then
|
|
769
|
+
if signer_isolation_helper_available; then
|
|
770
|
+
run_signer_isolation_helper start-signerd \
|
|
771
|
+
"$SIGNERD_BIN" \
|
|
772
|
+
"$SIGNERD_BACKEND_SOCKET" \
|
|
773
|
+
"$pid_file" \
|
|
774
|
+
"$audit_log" \
|
|
775
|
+
>>"$SIGNERD_LOG" 2>&1 &
|
|
776
|
+
return 0
|
|
777
|
+
fi
|
|
778
|
+
if ! command -v sudo >/dev/null 2>&1; then
|
|
779
|
+
return 1
|
|
780
|
+
fi
|
|
781
|
+
local env_args=()
|
|
782
|
+
while IFS= read -r -d '' env_arg; do
|
|
783
|
+
env_args+=("$env_arg")
|
|
784
|
+
done < <(collect_wallet_env_args)
|
|
785
|
+
sudo -n -u "$FASED_WALLET_LOCAL_SIGNER_RUN_AS_USER" -H env "${env_args[@]}" \
|
|
786
|
+
"$SIGNERD_BIN" \
|
|
787
|
+
-socket "$SIGNERD_BACKEND_SOCKET" \
|
|
788
|
+
-pid-file "$pid_file" \
|
|
789
|
+
-audit-log "$audit_log" \
|
|
790
|
+
>>"$SIGNERD_LOG" 2>&1 &
|
|
791
|
+
return 0
|
|
792
|
+
fi
|
|
793
|
+
"$SIGNERD_BIN" \
|
|
794
|
+
-socket "$SIGNERD_BACKEND_SOCKET" \
|
|
795
|
+
-pid-file "$pid_file" \
|
|
796
|
+
-audit-log "$audit_log" \
|
|
797
|
+
>>"$SIGNERD_LOG" 2>&1 &
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
start_signer_broker_process() {
|
|
801
|
+
if [[ "$SIGNERD_SOCKET" == "$SIGNERD_BACKEND_SOCKET" ]]; then
|
|
802
|
+
return 0
|
|
803
|
+
fi
|
|
804
|
+
local gateway_entry
|
|
805
|
+
gateway_entry="$(resolve_gateway_cli_entry || true)"
|
|
806
|
+
if [[ -z "$gateway_entry" ]]; then
|
|
807
|
+
echo "[signerd] ERROR: signer broker CLI unavailable under $FASED_ROOT/dist"
|
|
808
|
+
return 1
|
|
809
|
+
fi
|
|
810
|
+
if [[ -n "${FASED_WALLET_LOCAL_SIGNER_RUN_AS_USER:-}" ]]; then
|
|
811
|
+
if signer_isolation_helper_available; then
|
|
812
|
+
run_signer_isolation_helper start-broker \
|
|
813
|
+
"$NODE_BIN" \
|
|
814
|
+
"$gateway_entry" \
|
|
815
|
+
"$SIGNERD_SOCKET" \
|
|
816
|
+
"$SIGNERD_BACKEND_SOCKET" \
|
|
817
|
+
"$(resolve_local_signer_sidecar_path "$SIGNERD_SOCKET" "pid")" \
|
|
818
|
+
"$(resolve_local_signer_sidecar_path "$SIGNERD_SOCKET" "audit")" \
|
|
819
|
+
>>"$SIGNERD_BROKER_LOG" 2>&1 &
|
|
820
|
+
return 0
|
|
821
|
+
fi
|
|
822
|
+
if ! command -v sudo >/dev/null 2>&1; then
|
|
823
|
+
return 1
|
|
824
|
+
fi
|
|
825
|
+
local env_args=()
|
|
826
|
+
while IFS= read -r -d '' env_arg; do
|
|
827
|
+
env_args+=("$env_arg")
|
|
828
|
+
done < <(collect_wallet_env_args)
|
|
829
|
+
sudo -n -u "$FASED_WALLET_LOCAL_SIGNER_RUN_AS_USER" -H env "${env_args[@]}" \
|
|
830
|
+
"$NODE_BIN" "$gateway_entry" wallet signer broker \
|
|
831
|
+
--socket "$SIGNERD_SOCKET" \
|
|
832
|
+
--backend-socket "$SIGNERD_BACKEND_SOCKET" \
|
|
833
|
+
--pid-file "$(resolve_local_signer_sidecar_path "$SIGNERD_SOCKET" "pid")" \
|
|
834
|
+
--audit-log "$(resolve_local_signer_sidecar_path "$SIGNERD_SOCKET" "audit")" \
|
|
835
|
+
>>"$SIGNERD_BROKER_LOG" 2>&1 &
|
|
836
|
+
return 0
|
|
837
|
+
fi
|
|
838
|
+
"$NODE_BIN" "$gateway_entry" wallet signer broker \
|
|
839
|
+
--socket "$SIGNERD_SOCKET" \
|
|
840
|
+
--backend-socket "$SIGNERD_BACKEND_SOCKET" \
|
|
841
|
+
--pid-file "$(resolve_local_signer_sidecar_path "$SIGNERD_SOCKET" "pid")" \
|
|
842
|
+
--audit-log "$(resolve_local_signer_sidecar_path "$SIGNERD_SOCKET" "audit")" \
|
|
843
|
+
>>"$SIGNERD_BROKER_LOG" 2>&1 &
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
load_wallet_signer_env_from_config
|
|
847
|
+
load_wallet_signer_env_file
|
|
848
|
+
SIGNERD_BIN="${FASED_WALLET_LOCAL_SIGNER_BIN:-$SIGNERD_BIN}"
|
|
849
|
+
SIGNERD_MATERIAL_DIR="${FASED_WALLET_SIGNER_STATE_DIR:-$SIGNERD_MATERIAL_DIR}"
|
|
850
|
+
if [[ -n "${FASED_WALLET_LOCAL_SIGNER_RUN_AS_USER:-}" ]]; then
|
|
851
|
+
SIGNERD_BROKER_LOG="$(dirname "${FASED_WALLET_LOCAL_SIGNER_SOCKET:-$SIGNERD_SOCKET}")/local-signer-broker.log"
|
|
852
|
+
fi
|
|
853
|
+
hydrate_scoped_wallet_keystore_env_from_registry "$SIGNERD_MATERIAL_DIR"
|
|
854
|
+
if should_start_signerd; then
|
|
855
|
+
SIGNERD_STARTUP_MODE="healthy"
|
|
856
|
+
SIGNERD_SOCKET="${FASED_WALLET_LOCAL_SIGNER_SOCKET:-$SIGNERD_SOCKET}"
|
|
857
|
+
SIGNERD_BACKEND_SOCKET="${FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET:-$SIGNERD_SOCKET}"
|
|
858
|
+
SIGNERD_EVM_KEYSTORE="$(resolve_signerd_keystore_export "${FASED_WALLET_EVM_KEYSTORE_PATH:-}" "FASED_WALLET_EVM_KEYSTORE_PATH" "$SIGNERD_MATERIAL_DIR/keystore-evm.v1.enc")"
|
|
859
|
+
SIGNERD_SOL_KEYSTORE="$(resolve_signerd_keystore_export "${FASED_WALLET_SOLANA_KEYSTORE_PATH:-}" "FASED_WALLET_SOLANA_KEYSTORE_PATH" "$SIGNERD_MATERIAL_DIR/keystore-solana.v1.enc")"
|
|
860
|
+
SIGNERD_PASSPHRASE="${FASED_WALLET_PASSPHRASE:-}"
|
|
861
|
+
SIGNERD_PASSPHRASE_FILE="${FASED_WALLET_PASSPHRASE_FILE:-}"
|
|
862
|
+
SIGNERD_CHAINS="${FASED_WALLET_CHAINS:-$(resolve_wallet_chains_from_config)}"
|
|
863
|
+
export FASED_WALLET_LOCAL_SIGNER_SOCKET="$SIGNERD_SOCKET"
|
|
864
|
+
export FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET="$SIGNERD_BACKEND_SOCKET"
|
|
865
|
+
export FASED_WALLET_SIGNER_STATE_DIR="$SIGNERD_MATERIAL_DIR"
|
|
866
|
+
export FASED_WALLET_CHAINS="${SIGNERD_CHAINS:-evm,solana}"
|
|
867
|
+
if [[ -n "$SIGNERD_EVM_KEYSTORE" ]]; then
|
|
868
|
+
export FASED_WALLET_EVM_KEYSTORE_PATH="$SIGNERD_EVM_KEYSTORE"
|
|
869
|
+
else
|
|
870
|
+
unset FASED_WALLET_EVM_KEYSTORE_PATH
|
|
871
|
+
fi
|
|
872
|
+
if [[ -n "$SIGNERD_SOL_KEYSTORE" ]]; then
|
|
873
|
+
export FASED_WALLET_SOLANA_KEYSTORE_PATH="$SIGNERD_SOL_KEYSTORE"
|
|
874
|
+
else
|
|
875
|
+
unset FASED_WALLET_SOLANA_KEYSTORE_PATH
|
|
876
|
+
fi
|
|
877
|
+
if [[ -n "$SIGNERD_PASSPHRASE" ]]; then
|
|
878
|
+
export FASED_WALLET_PASSPHRASE="$SIGNERD_PASSPHRASE"
|
|
879
|
+
unset FASED_WALLET_PASSPHRASE_FILE
|
|
880
|
+
else
|
|
881
|
+
SIGNERD_PASSPHRASE_FILE="${SIGNERD_PASSPHRASE_FILE:-$SIGNERD_MATERIAL_DIR/passphrase}"
|
|
882
|
+
export FASED_WALLET_PASSPHRASE_FILE="$SIGNERD_PASSPHRASE_FILE"
|
|
883
|
+
unset FASED_WALLET_PASSPHRASE
|
|
884
|
+
fi
|
|
885
|
+
if [[ -f "$SIGNERD_BIN" ]]; then
|
|
886
|
+
if [[ -S "$SIGNERD_SOCKET" ]] || [[ -S "$SIGNERD_BACKEND_SOCKET" ]] || [[ -f "$(resolve_local_signer_sidecar_path "$SIGNERD_BACKEND_SOCKET" "pid")" ]] || [[ "$(count_existing_signerd_pids)" -gt 0 ]]; then
|
|
887
|
+
echo "==> Restarting fased-signerd to apply current wallet chain/runtime config..."
|
|
888
|
+
stop_existing_signerd
|
|
889
|
+
fi
|
|
890
|
+
echo "==> Starting fased-signerd (Go key signer)..."
|
|
891
|
+
mkdir -p "$LOG_DIR"
|
|
892
|
+
mkdir -p "$(dirname "$SIGNERD_LOG")" "$(dirname "$SIGNERD_BROKER_LOG")" 2>/dev/null || true
|
|
893
|
+
if start_signerd_process; then
|
|
894
|
+
SIGNERD_PID=$!
|
|
895
|
+
else
|
|
896
|
+
mark_signerd_degraded "failed to start isolated fased-signerd as ${FASED_WALLET_LOCAL_SIGNER_RUN_AS_USER:-current user}. Check sudoers and $SIGNERD_LOG"
|
|
897
|
+
SIGNERD_PID=""
|
|
898
|
+
fi
|
|
899
|
+
if [[ -n "$SIGNERD_PID" ]] && wait_for_signerd_ready "$SIGNERD_READY_TIMEOUT_SECONDS"; then
|
|
900
|
+
if ! start_signer_broker_process; then
|
|
901
|
+
mark_signerd_degraded "fased-signerd started but signer broker failed. Check $SIGNERD_BROKER_LOG"
|
|
902
|
+
elif wait_for_signer_broker_ready "$SIGNERD_READY_TIMEOUT_SECONDS"; then
|
|
903
|
+
echo "==> fased-signerd started (PID=$SIGNERD_PID, socket: $SIGNERD_BACKEND_SOCKET)"
|
|
904
|
+
else
|
|
905
|
+
mark_signerd_degraded "fased-signerd broker did not become ready. Check $SIGNERD_BROKER_LOG"
|
|
906
|
+
fi
|
|
907
|
+
else
|
|
908
|
+
mark_signerd_degraded "fased-signerd did not create socket. Check $SIGNERD_LOG"
|
|
909
|
+
fi
|
|
910
|
+
else
|
|
911
|
+
mark_signerd_degraded "fased-signerd is not installed; dashboard stays online. Configure wallet signer from the dashboard or run fased wallet signer setup."
|
|
912
|
+
fi
|
|
913
|
+
else
|
|
914
|
+
unset FASED_WALLET_LOCAL_SIGNER_SOCKET
|
|
915
|
+
unset FASED_WALLET_LOCAL_SIGNER_BACKEND_SOCKET
|
|
916
|
+
unset FASED_WALLET_SIGNER_STATE_DIR
|
|
917
|
+
unset FASED_WALLET_CHAINS
|
|
918
|
+
unset FASED_WALLET_SOLANA_KEYSTORE_PATH
|
|
919
|
+
unset FASED_WALLET_EVM_KEYSTORE_PATH
|
|
920
|
+
unset FASED_WALLET_PASSPHRASE
|
|
921
|
+
unset FASED_WALLET_PASSPHRASE_FILE
|
|
922
|
+
echo "==> Wallet signer not configured; skipping fased-signerd startup."
|
|
923
|
+
fi
|
|
924
|
+
if [[ -f "$ZROK_MONITOR_PID_FILE" ]]; then
|
|
925
|
+
OLD_MONITOR_PID=$(cat "$ZROK_MONITOR_PID_FILE" 2>/dev/null || true)
|
|
926
|
+
if [[ -n "$OLD_MONITOR_PID" ]] && kill -0 "$OLD_MONITOR_PID" 2>/dev/null; then
|
|
927
|
+
kill "$OLD_MONITOR_PID" 2>/dev/null || true
|
|
928
|
+
fi
|
|
929
|
+
rm -f "$ZROK_MONITOR_PID_FILE" || true
|
|
930
|
+
fi
|
|
931
|
+
pkill -f "zrok share" || true
|
|
932
|
+
sleep 1
|
|
933
|
+
|
|
934
|
+
# === Tunnel Helper Functions ===
|
|
935
|
+
|
|
936
|
+
start_health_monitor() {
|
|
937
|
+
local SLUG="$1"
|
|
938
|
+
local RES_TOKEN="$2"
|
|
939
|
+
local retry_delay=30
|
|
940
|
+
local max_retry_delay=300
|
|
941
|
+
while true; do
|
|
942
|
+
sleep "$retry_delay"
|
|
943
|
+
local pid=""
|
|
944
|
+
if [[ -f "$FASED_CONFIG_DIR/.zrok-pid" ]]; then
|
|
945
|
+
pid="$(cat "$FASED_CONFIG_DIR/.zrok-pid" 2>/dev/null || true)"
|
|
946
|
+
fi
|
|
947
|
+
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
|
|
948
|
+
retry_delay=30
|
|
949
|
+
continue
|
|
950
|
+
fi
|
|
951
|
+
|
|
952
|
+
echo "[tunnel] zrok share is not active, attempting restart..."
|
|
953
|
+
if zrok_log_indicates_clock_skew; then
|
|
954
|
+
echo "[tunnel] Detected zrok auth failure caused by host clock skew."
|
|
955
|
+
ensure_managed_clock_sync || true
|
|
956
|
+
fi
|
|
957
|
+
if [[ -n "$RES_TOKEN" ]]; then
|
|
958
|
+
"$ZROK_BIN" share reserved "$RES_TOKEN" --headless >>"$ZROK_RUNTIME_LOG" 2>&1 &
|
|
959
|
+
else
|
|
960
|
+
"$ZROK_BIN" share public "http://127.0.0.1:${FASED_GATEWAY_PORT}" --unique-name "$SLUG" >>"$ZROK_RUNTIME_LOG" 2>&1 &
|
|
961
|
+
fi
|
|
962
|
+
pid=$!
|
|
963
|
+
echo "$pid" > "$FASED_CONFIG_DIR/.zrok-pid"
|
|
964
|
+
sleep 5
|
|
965
|
+
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
|
|
966
|
+
echo "[tunnel] ✓ Tunnel active."
|
|
967
|
+
retry_delay=30
|
|
968
|
+
continue
|
|
969
|
+
fi
|
|
970
|
+
|
|
971
|
+
echo "[tunnel] WARNING: zrok share restart failed."
|
|
972
|
+
if zrok_log_indicates_clock_skew; then
|
|
973
|
+
echo "[tunnel] Detected zrok auth failure caused by host clock skew."
|
|
974
|
+
ensure_managed_clock_sync || true
|
|
975
|
+
fi
|
|
976
|
+
if (( retry_delay < max_retry_delay )); then
|
|
977
|
+
retry_delay=$((retry_delay * 2))
|
|
978
|
+
if (( retry_delay > max_retry_delay )); then
|
|
979
|
+
retry_delay=$max_retry_delay
|
|
980
|
+
fi
|
|
981
|
+
fi
|
|
982
|
+
done
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
# === Execution Flow ===
|
|
986
|
+
|
|
987
|
+
MANAGED_TUNNEL_DISABLED=0
|
|
988
|
+
TUNNEL_ERROR=""
|
|
989
|
+
disable_managed_tunnel() {
|
|
990
|
+
MANAGED_TUNNEL_DISABLED=1
|
|
991
|
+
TUNNEL_STARTED=0
|
|
992
|
+
FINAL_URL="N/A"
|
|
993
|
+
TUNNEL_ERROR="$1"
|
|
994
|
+
echo "[tunnel] WARNING: $1"
|
|
995
|
+
echo "[tunnel] Dashboard gateway stays online; Fased Network tunnel remains degraded until repaired."
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
# 1. Wait for the agent to enroll/refresh access token.
|
|
999
|
+
# We do not treat a stale pre-existing token as ready if it has no zrok metadata.
|
|
1000
|
+
echo "==> Waiting for agent enrollment/token refresh (max 60s)..."
|
|
1001
|
+
MAX_RETRIES=60
|
|
1002
|
+
COUNT=0
|
|
1003
|
+
while true; do
|
|
1004
|
+
if [[ -f "$TOKEN_PATH" ]]; then
|
|
1005
|
+
CURRENT_ZROK=$(jq -r '.zrokToken // empty' "$TOKEN_PATH" 2>/dev/null || true)
|
|
1006
|
+
if [[ -n "$CURRENT_ZROK" ]]; then
|
|
1007
|
+
break
|
|
1008
|
+
fi
|
|
1009
|
+
|
|
1010
|
+
CURRENT_SIG=$(sha256sum "$TOKEN_PATH" | awk '{print $1}')
|
|
1011
|
+
# If token changed after startup, proceed even if zrok is still missing.
|
|
1012
|
+
# Recovery logic below will decide strict-mode outcome.
|
|
1013
|
+
if [[ -n "$INITIAL_TOKEN_SIG" && "$CURRENT_SIG" != "$INITIAL_TOKEN_SIG" ]]; then
|
|
1014
|
+
break
|
|
1015
|
+
fi
|
|
1016
|
+
|
|
1017
|
+
# No initial token: once we have a token file, give it a short chance to gain zrok fields.
|
|
1018
|
+
if [[ -z "$INITIAL_TOKEN_SIG" && $COUNT -ge 3 ]]; then
|
|
1019
|
+
break
|
|
1020
|
+
fi
|
|
1021
|
+
fi
|
|
1022
|
+
sleep 1
|
|
1023
|
+
COUNT=$((COUNT + 1))
|
|
1024
|
+
if [[ $COUNT -ge $MAX_RETRIES ]]; then
|
|
1025
|
+
if [[ -f "$TOKEN_PATH" ]]; then
|
|
1026
|
+
echo "[tunnel] WARNING: Token refresh did not complete in time; continuing with current token."
|
|
1027
|
+
break
|
|
1028
|
+
fi
|
|
1029
|
+
disable_managed_tunnel "Agent enrollment/token refresh did not complete in time and no token is available."
|
|
1030
|
+
if [[ "$VERBOSE_STARTUP" != "1" ]]; then
|
|
1031
|
+
echo "[debug] Last gateway startup logs:"
|
|
1032
|
+
tail -n 40 "$GATEWAY_BOOT_LOG" || true
|
|
1033
|
+
fi
|
|
1034
|
+
break
|
|
1035
|
+
fi
|
|
1036
|
+
done
|
|
1037
|
+
|
|
1038
|
+
# === Tunneling (zrok) ===
|
|
1039
|
+
|
|
1040
|
+
FINAL_URL="N/A"
|
|
1041
|
+
TUNNEL_STARTED=0
|
|
1042
|
+
SLUG="N/A"
|
|
1043
|
+
RES_TOKEN=""
|
|
1044
|
+
|
|
1045
|
+
ZROK_VDIR="$HOME/.zrok"
|
|
1046
|
+
ZROK_BIN="$ZROK_VDIR/bin/zrok"
|
|
1047
|
+
|
|
1048
|
+
if [[ "$MANAGED_TUNNEL_DISABLED" != "1" ]]; then
|
|
1049
|
+
|
|
1050
|
+
# Check if zrok is installed; if not, install it
|
|
1051
|
+
if [[ ! -f "$ZROK_BIN" ]]; then
|
|
1052
|
+
echo "[tunnel] Installing zrok CLI..."
|
|
1053
|
+
echo "[tunnel] Destination: $ZROK_VDIR/bin"
|
|
1054
|
+
mkdir -p "$ZROK_VDIR/bin"
|
|
1055
|
+
|
|
1056
|
+
# Temporarily disable pipefail to capture install errors
|
|
1057
|
+
set +e
|
|
1058
|
+
curl -sLf https://get.openziti.io/zrok/install/get.bash | bash -s -- --install-dir "$ZROK_VDIR/bin" > /tmp/zrok-install.log 2>&1
|
|
1059
|
+
INSTALL_EXIT=$?
|
|
1060
|
+
set -e
|
|
1061
|
+
|
|
1062
|
+
if [[ $INSTALL_EXIT -ne 0 ]]; then
|
|
1063
|
+
echo "[tunnel] ERROR: zrok installation failed (Exit: $INSTALL_EXIT). Log:"
|
|
1064
|
+
cat /tmp/zrok-install.log
|
|
1065
|
+
echo "[tunnel] Attempting manual download as fallback (v1.1.11)..."
|
|
1066
|
+
|
|
1067
|
+
TMP_ZROK_DIR=$(mktemp -d)
|
|
1068
|
+
ZROK_URL="https://github.com/openziti/zrok/releases/download/v1.1.11/zrok_1.1.11_linux_amd64.tar.gz"
|
|
1069
|
+
|
|
1070
|
+
if curl -sLf "$ZROK_URL" -o "$TMP_ZROK_DIR/zrok.tar.gz"; then
|
|
1071
|
+
tar -xzf "$TMP_ZROK_DIR/zrok.tar.gz" -C "$TMP_ZROK_DIR"
|
|
1072
|
+
|
|
1073
|
+
# Find zrok binary safely within the temp dir
|
|
1074
|
+
ZROK_FOUND=$(find "$TMP_ZROK_DIR" -name zrok -type f | head -n 1)
|
|
1075
|
+
|
|
1076
|
+
if [[ -n "$ZROK_FOUND" ]]; then
|
|
1077
|
+
mv "$ZROK_FOUND" "$ZROK_VDIR/bin/"
|
|
1078
|
+
chmod +x "$ZROK_VDIR/bin/zrok"
|
|
1079
|
+
echo "[tunnel] Manual download success."
|
|
1080
|
+
else
|
|
1081
|
+
echo "[tunnel] Extracted but 'zrok' binary not found."
|
|
1082
|
+
ls -R "$TMP_ZROK_DIR"
|
|
1083
|
+
rm -rf "$TMP_ZROK_DIR"
|
|
1084
|
+
disable_managed_tunnel "zrok manual download extracted without a zrok binary."
|
|
1085
|
+
fi
|
|
1086
|
+
else
|
|
1087
|
+
echo "[tunnel] Download failed (curl)."
|
|
1088
|
+
rm -rf "$TMP_ZROK_DIR"
|
|
1089
|
+
disable_managed_tunnel "zrok manual download failed."
|
|
1090
|
+
fi
|
|
1091
|
+
rm -rf "$TMP_ZROK_DIR" /tmp/zrok-install.log
|
|
1092
|
+
fi
|
|
1093
|
+
fi
|
|
1094
|
+
|
|
1095
|
+
if [[ "$MANAGED_TUNNEL_DISABLED" != "1" && ! -f "$ZROK_BIN" ]]; then
|
|
1096
|
+
disable_managed_tunnel "zrok binary not found at $ZROK_BIN after install."
|
|
1097
|
+
fi
|
|
1098
|
+
|
|
1099
|
+
fi
|
|
1100
|
+
|
|
1101
|
+
if [[ "$MANAGED_TUNNEL_DISABLED" != "1" ]]; then
|
|
1102
|
+
|
|
1103
|
+
chmod +x "$ZROK_BIN"
|
|
1104
|
+
echo "[tunnel] zrok version: $($ZROK_BIN version)"
|
|
1105
|
+
|
|
1106
|
+
echo "==> Consuming server-issued zrok credentials..."
|
|
1107
|
+
|
|
1108
|
+
# Read zrok token from the enrolled agent's access token
|
|
1109
|
+
if [[ ! -f "$TOKEN_PATH" ]]; then
|
|
1110
|
+
disable_managed_tunnel "Agent enrollment token is unavailable. Tunnel cannot start yet."
|
|
1111
|
+
fi
|
|
1112
|
+
|
|
1113
|
+
fi
|
|
1114
|
+
|
|
1115
|
+
if [[ "$MANAGED_TUNNEL_DISABLED" != "1" ]]; then
|
|
1116
|
+
|
|
1117
|
+
ZROK_TOKEN=$(jq -r .zrokToken "$TOKEN_PATH")
|
|
1118
|
+
SERVER_SLUG=$(jq -r '.agentSlug // empty' "$TOKEN_PATH")
|
|
1119
|
+
AGENT_HANDLE=$(jq -r '.handle // empty' "$TOKEN_PATH")
|
|
1120
|
+
|
|
1121
|
+
if [[ -n "$SERVER_SLUG" ]]; then
|
|
1122
|
+
SLUG="$SERVER_SLUG"
|
|
1123
|
+
elif [[ -n "$AGENT_HANDLE" ]]; then
|
|
1124
|
+
SLUG=$(echo "$AGENT_HANDLE" | sed 's/[^a-zA-Z0-9]/-/g' | sed 's/^-//' | sed 's/-$//' | tr '[:upper:]' '[:lower:]')
|
|
1125
|
+
else
|
|
1126
|
+
SLUG=""
|
|
1127
|
+
fi
|
|
1128
|
+
|
|
1129
|
+
if [[ -z "$SLUG" ]]; then
|
|
1130
|
+
disable_managed_tunnel "Unable to resolve tunnel slug from enrollment token."
|
|
1131
|
+
fi
|
|
1132
|
+
|
|
1133
|
+
fi
|
|
1134
|
+
|
|
1135
|
+
if [[ "$MANAGED_TUNNEL_DISABLED" != "1" ]]; then
|
|
1136
|
+
|
|
1137
|
+
echo "[tunnel] Target Slug: $SLUG"
|
|
1138
|
+
|
|
1139
|
+
# Reservation persistence
|
|
1140
|
+
RES_FILE="$FASED_CONFIG_DIR/${SLUG}.zrok-reservation"
|
|
1141
|
+
RES_TOKEN=""
|
|
1142
|
+
if [[ -f "$RES_FILE" ]]; then
|
|
1143
|
+
RES_TOKEN=$(cat "$RES_FILE")
|
|
1144
|
+
echo "[tunnel] Found cached reservation: $RES_TOKEN"
|
|
1145
|
+
fi
|
|
1146
|
+
|
|
1147
|
+
# Recovery when exact slug file is missing: use the only reservation token in config dir.
|
|
1148
|
+
if [[ -z "$RES_TOKEN" ]]; then
|
|
1149
|
+
mapfile -t RES_FILES < <(ls "$FASED_CONFIG_DIR"/*.zrok-reservation 2>/dev/null || true)
|
|
1150
|
+
if [[ ${#RES_FILES[@]} -eq 1 ]]; then
|
|
1151
|
+
RES_FILE="${RES_FILES[0]}"
|
|
1152
|
+
RES_TOKEN=$(cat "$RES_FILE")
|
|
1153
|
+
RECOVERED_BASENAME=$(basename "$RES_FILE")
|
|
1154
|
+
RECOVERED_SLUG="${RECOVERED_BASENAME%.zrok-reservation}"
|
|
1155
|
+
if [[ -n "$RECOVERED_SLUG" ]]; then
|
|
1156
|
+
SLUG="$RECOVERED_SLUG"
|
|
1157
|
+
echo "[tunnel] Recovered slug from cached reservation filename: $SLUG"
|
|
1158
|
+
fi
|
|
1159
|
+
echo "[tunnel] Recovered cached reservation: $RES_TOKEN"
|
|
1160
|
+
fi
|
|
1161
|
+
fi
|
|
1162
|
+
|
|
1163
|
+
TUNNEL_STARTED=0
|
|
1164
|
+
|
|
1165
|
+
if [[ "$ZROK_TOKEN" == "null" || -z "$ZROK_TOKEN" ]]; then
|
|
1166
|
+
echo "[tunnel] WARNING: No zrok credentials found in enrollment result."
|
|
1167
|
+
echo "[tunnel] Attempting strict managed recovery using cached reservation + existing zrok identity..."
|
|
1168
|
+
export ZROK_API_ENDPOINT="${ZROK_API_ENDPOINT:-https://zrok.fased.app}"
|
|
1169
|
+
"$ZROK_BIN" config set apiEndpoint "$ZROK_API_ENDPOINT" 2>/dev/null || true
|
|
1170
|
+
|
|
1171
|
+
HAS_ZROK_IDENTITY=0
|
|
1172
|
+
if "$ZROK_BIN" overview --json >/dev/null 2>&1; then
|
|
1173
|
+
HAS_ZROK_IDENTITY=1
|
|
1174
|
+
fi
|
|
1175
|
+
|
|
1176
|
+
if [[ -n "$RES_TOKEN" && "$HAS_ZROK_IDENTITY" -eq 1 ]]; then
|
|
1177
|
+
echo "[tunnel] Recovery prerequisites satisfied (cached reservation found)."
|
|
1178
|
+
FINAL_URL="https://${SLUG}.agents.fased.app"
|
|
1179
|
+
TUNNEL_STARTED=1
|
|
1180
|
+
else
|
|
1181
|
+
echo "[tunnel] ERROR: Managed mode is strict and cannot continue without tunnel credentials."
|
|
1182
|
+
echo "[tunnel] Required for recovery: existing zrok identity + cached *.zrok-reservation token."
|
|
1183
|
+
echo "[tunnel] Remediation:"
|
|
1184
|
+
echo "[tunnel] 1) Check server logs: journalctl -u fased -n 100 --no-pager | grep 'zrok:'"
|
|
1185
|
+
echo "[tunnel] 2) Ensure enroll returns zrokToken/agentSlug"
|
|
1186
|
+
echo "[tunnel] 3) Re-enroll only after server provisioning is healthy"
|
|
1187
|
+
disable_managed_tunnel "No zrok credentials or cached reservation are available yet."
|
|
1188
|
+
fi
|
|
1189
|
+
fi
|
|
1190
|
+
|
|
1191
|
+
if [[ "$MANAGED_TUNNEL_DISABLED" != "1" && "$ZROK_TOKEN" != "null" && -n "$ZROK_TOKEN" ]]; then
|
|
1192
|
+
echo "[tunnel] Enabling zrok environment..."
|
|
1193
|
+
export ZROK_API_ENDPOINT="${ZROK_API_ENDPOINT:-https://zrok.fased.app}"
|
|
1194
|
+
ensure_managed_clock_sync || true
|
|
1195
|
+
"$ZROK_BIN" config set apiEndpoint "$ZROK_API_ENDPOINT" 2>/dev/null
|
|
1196
|
+
|
|
1197
|
+
# zrok enable (idempotent-ish, or just try)
|
|
1198
|
+
# Remove --force as it's not supported in v1.1+
|
|
1199
|
+
# We ignore error if already enabled, but capture output to be safe
|
|
1200
|
+
"$ZROK_BIN" enable "$ZROK_TOKEN" --description "fased-agent" 2>/dev/null || true
|
|
1201
|
+
|
|
1202
|
+
# If no token, check if we already reserved it (recovery from lost file)
|
|
1203
|
+
if [[ -z "$RES_TOKEN" ]]; then
|
|
1204
|
+
echo "[tunnel] Checking for existing reservation..."
|
|
1205
|
+
OVERVIEW_JSON=$("$ZROK_BIN" overview --json 2>/dev/null || echo "{}")
|
|
1206
|
+
EXISTING_TOKEN=$(echo "$OVERVIEW_JSON" | jq -r --arg SLUG "$SLUG" '(.shares // [])[] | select(.frontendEndpoints[] | contains($SLUG)) | .token' 2>/dev/null | head -n 1 || true)
|
|
1207
|
+
|
|
1208
|
+
if [[ -n "$EXISTING_TOKEN" ]]; then
|
|
1209
|
+
echo "[tunnel] Recovered existing token: $EXISTING_TOKEN"
|
|
1210
|
+
RES_TOKEN="$EXISTING_TOKEN"
|
|
1211
|
+
echo "$RES_TOKEN" > "$RES_FILE"
|
|
1212
|
+
fi
|
|
1213
|
+
fi
|
|
1214
|
+
|
|
1215
|
+
# If still no token, try to reserve
|
|
1216
|
+
if [[ -z "$RES_TOKEN" ]]; then
|
|
1217
|
+
echo "[tunnel] Reserving public share..."
|
|
1218
|
+
# Use --json-output to parse output reliably; separate stderr
|
|
1219
|
+
PARAMS="--unique-name $SLUG --backend-mode proxy --json-output"
|
|
1220
|
+
|
|
1221
|
+
# Capture logic: stdout to variable, stderr to temp file
|
|
1222
|
+
ZROK_LOG="/tmp/zrok-reserve.log"
|
|
1223
|
+
rm -f "$ZROK_LOG"
|
|
1224
|
+
|
|
1225
|
+
if OUT=$("$ZROK_BIN" reserve public "http://127.0.0.1:${FASED_GATEWAY_PORT}" $PARAMS 2> "$ZROK_LOG"); then
|
|
1226
|
+
RES_TOKEN=$(echo "$OUT" | jq -r '.token // empty')
|
|
1227
|
+
else
|
|
1228
|
+
echo "[tunnel] Reserve failed. Log:"
|
|
1229
|
+
cat "$ZROK_LOG"
|
|
1230
|
+
fi
|
|
1231
|
+
|
|
1232
|
+
if [[ -z "$RES_TOKEN" ]]; then
|
|
1233
|
+
echo "[tunnel] Reservation failed or already reserved. Output: $OUT"
|
|
1234
|
+
disable_managed_tunnel "Could not establish reserved tunnel for '$SLUG'."
|
|
1235
|
+
else
|
|
1236
|
+
echo "$RES_TOKEN" > "$RES_FILE"
|
|
1237
|
+
echo "[tunnel] Reserved: $RES_TOKEN"
|
|
1238
|
+
fi
|
|
1239
|
+
fi
|
|
1240
|
+
|
|
1241
|
+
fi
|
|
1242
|
+
|
|
1243
|
+
if [[ "$MANAGED_TUNNEL_DISABLED" != "1" ]]; then
|
|
1244
|
+
FINAL_URL="https://${SLUG}.agents.fased.app"
|
|
1245
|
+
TUNNEL_STARTED=1
|
|
1246
|
+
fi
|
|
1247
|
+
|
|
1248
|
+
if [[ "$TUNNEL_STARTED" -eq 1 ]]; then
|
|
1249
|
+
if ! start_initial_zrok_share "$SLUG"; then
|
|
1250
|
+
echo "[tunnel] WARNING: Continuing in degraded mode without a public tunnel."
|
|
1251
|
+
echo "[tunnel] WARNING: The local gateway remains up and background tunnel retries will continue."
|
|
1252
|
+
rm -f "$FASED_CONFIG_DIR/.zrok-pid" 2>/dev/null || true
|
|
1253
|
+
fi
|
|
1254
|
+
if [[ -f "$ZROK_MONITOR_PID_FILE" ]]; then
|
|
1255
|
+
OLD_MONITOR_PID=$(cat "$ZROK_MONITOR_PID_FILE" 2>/dev/null || true)
|
|
1256
|
+
if [[ -n "$OLD_MONITOR_PID" ]] && kill -0 "$OLD_MONITOR_PID" 2>/dev/null; then
|
|
1257
|
+
kill "$OLD_MONITOR_PID" 2>/dev/null || true
|
|
1258
|
+
fi
|
|
1259
|
+
fi
|
|
1260
|
+
start_health_monitor "$SLUG" "$RES_TOKEN" &
|
|
1261
|
+
ZROK_MONITOR_PID=$!
|
|
1262
|
+
echo "$ZROK_MONITOR_PID" > "$ZROK_MONITOR_PID_FILE"
|
|
1263
|
+
fi
|
|
1264
|
+
|
|
1265
|
+
fi
|
|
1266
|
+
|
|
1267
|
+
WALLET_JSON=""
|
|
1268
|
+
WALLET_HEALTHY="false"
|
|
1269
|
+
WALLET_PID="N/A"
|
|
1270
|
+
WALLET_MODE="n/a"
|
|
1271
|
+
WALLET_SERVICE="n/a"
|
|
1272
|
+
WALLET_CHAINS="n/a"
|
|
1273
|
+
WALLET_DIRECT_SIGNING="n/a"
|
|
1274
|
+
WALLET_TOOL_SCOPE="n/a"
|
|
1275
|
+
WALLET_KEYS_PATH="n/a"
|
|
1276
|
+
WALLET_STARTUP_MODE="healthy"
|
|
1277
|
+
WALLET_AUTH_STATE="unknown"
|
|
1278
|
+
WALLET_AUTH_MODE="unknown"
|
|
1279
|
+
WALLET_AUTH_SOURCE="unknown"
|
|
1280
|
+
WALLET_ERROR=""
|
|
1281
|
+
|
|
1282
|
+
echo "==> Enforcing wallet baseline (wallet service)..."
|
|
1283
|
+
if [[ "${WALLET_BASELINE_MODE,,}" == "skip" ]]; then
|
|
1284
|
+
WALLET_STARTUP_MODE="degraded"
|
|
1285
|
+
WALLET_ERROR="Wallet baseline skipped (FASED_WALLET_BASELINE_MODE=skip)."
|
|
1286
|
+
echo "[wallet] WARNING: Wallet baseline skipped; continuing startup immediately."
|
|
1287
|
+
else
|
|
1288
|
+
# Keep stderr visible to avoid hidden sudo/docker prompts while also logging.
|
|
1289
|
+
WALLET_SETUP_CMD=(
|
|
1290
|
+
env
|
|
1291
|
+
FASED_SKIP_BUILD=1
|
|
1292
|
+
FASED_WALLET_SETUP_ALLOW_DEGRADED=1
|
|
1293
|
+
"$NODE_BIN"
|
|
1294
|
+
"$RUN_NODE_SCRIPT"
|
|
1295
|
+
wallet
|
|
1296
|
+
setup
|
|
1297
|
+
--json
|
|
1298
|
+
)
|
|
1299
|
+
USE_TIMEOUT=0
|
|
1300
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
1301
|
+
if [[ "$WALLET_BASELINE_TIMEOUT_SECONDS" =~ ^[0-9]+$ ]] && [[ "$WALLET_BASELINE_TIMEOUT_SECONDS" -gt 0 ]]; then
|
|
1302
|
+
USE_TIMEOUT=1
|
|
1303
|
+
fi
|
|
1304
|
+
fi
|
|
1305
|
+
if [[ "$USE_TIMEOUT" == "1" ]]; then
|
|
1306
|
+
echo "[wallet] Baseline timeout: ${WALLET_BASELINE_TIMEOUT_SECONDS}s (override: FASED_WALLET_BASELINE_TIMEOUT_SECONDS, disable timeout with 0)."
|
|
1307
|
+
if WALLET_OUTPUT=$(timeout --signal=TERM "${WALLET_BASELINE_TIMEOUT_SECONDS}" "${WALLET_SETUP_CMD[@]}" 2> >(tee "$WALLET_SETUP_LOG" >&2)); then
|
|
1308
|
+
WALLET_SETUP_RC=0
|
|
1309
|
+
else
|
|
1310
|
+
WALLET_SETUP_RC=$?
|
|
1311
|
+
fi
|
|
1312
|
+
else
|
|
1313
|
+
if WALLET_OUTPUT=$("${WALLET_SETUP_CMD[@]}" 2> >(tee "$WALLET_SETUP_LOG" >&2)); then
|
|
1314
|
+
WALLET_SETUP_RC=0
|
|
1315
|
+
else
|
|
1316
|
+
WALLET_SETUP_RC=$?
|
|
1317
|
+
fi
|
|
1318
|
+
fi
|
|
1319
|
+
|
|
1320
|
+
if [[ "${WALLET_SETUP_RC:-0}" == "0" ]]; then
|
|
1321
|
+
WALLET_JSON=$(printf '%s\n' "$WALLET_OUTPUT" | sed -n '/^{/,$p')
|
|
1322
|
+
if [[ -z "$WALLET_JSON" ]]; then
|
|
1323
|
+
WALLET_STARTUP_MODE="degraded"
|
|
1324
|
+
WALLET_ERROR="Wallet setup returned no JSON payload."
|
|
1325
|
+
else
|
|
1326
|
+
WALLET_HEALTHY=$(printf '%s\n' "$WALLET_JSON" | jq -r '.status.service.healthy // false' 2>/dev/null || echo "false")
|
|
1327
|
+
WALLET_PID=$(printf '%s\n' "$WALLET_JSON" | jq -r '.status.service.pid // "N/A"' 2>/dev/null || echo "N/A")
|
|
1328
|
+
WALLET_MODE=$(printf '%s\n' "$WALLET_JSON" | jq -r '.status.mode // "n/a"' 2>/dev/null || echo "n/a")
|
|
1329
|
+
WALLET_SERVICE=$(printf '%s\n' "$WALLET_JSON" | jq -r '.status.service.host + ":" + ((.status.service.port // 0)|tostring)' 2>/dev/null || echo "n/a")
|
|
1330
|
+
WALLET_CHAINS=$(printf '%s\n' "$WALLET_JSON" | jq -r '.status.chains // [] | join(",")' 2>/dev/null || echo "n/a")
|
|
1331
|
+
WALLET_DIRECT_SIGNING=$(printf '%s\n' "$WALLET_JSON" | jq -r '.status.policy.directSigning // "n/a"' 2>/dev/null || echo "n/a")
|
|
1332
|
+
WALLET_TOOL_SCOPE=$(printf '%s\n' "$WALLET_JSON" | jq -r '.status.policy.toolAccessMode // "n/a"' 2>/dev/null || echo "n/a")
|
|
1333
|
+
WALLET_KEYS_PATH=$(printf '%s\n' "$WALLET_JSON" | jq -r '.status.paths.keysPath // "n/a"' 2>/dev/null || echo "n/a")
|
|
1334
|
+
WALLET_STARTUP_MODE=$(printf '%s\n' "$WALLET_JSON" | jq -r '.status.startupState // "healthy"' 2>/dev/null || echo "healthy")
|
|
1335
|
+
WALLET_AUTH_STATE=$(printf '%s\n' "$WALLET_JSON" | jq -r '.status.authState // "unknown"' 2>/dev/null || echo "unknown")
|
|
1336
|
+
WALLET_AUTH_MODE=$(printf '%s\n' "$WALLET_JSON" | jq -r '.status.authMode // "unknown"' 2>/dev/null || echo "unknown")
|
|
1337
|
+
WALLET_AUTH_SOURCE=$(printf '%s\n' "$WALLET_JSON" | jq -r '.status.authSource // "unknown"' 2>/dev/null || echo "unknown")
|
|
1338
|
+
WALLET_ERROR=$(printf '%s\n' "$WALLET_JSON" | jq -r '.status.error // empty' 2>/dev/null || true)
|
|
1339
|
+
if [[ "$WALLET_HEALTHY" != "true" && "$WALLET_STARTUP_MODE" == "healthy" ]]; then
|
|
1340
|
+
WALLET_STARTUP_MODE="degraded"
|
|
1341
|
+
fi
|
|
1342
|
+
fi
|
|
1343
|
+
else
|
|
1344
|
+
WALLET_STARTUP_MODE="degraded"
|
|
1345
|
+
if [[ "${WALLET_SETUP_RC:-1}" == "124" || "${WALLET_SETUP_RC:-1}" == "143" ]]; then
|
|
1346
|
+
WALLET_ERROR="Wallet baseline timed out after ${WALLET_BASELINE_TIMEOUT_SECONDS}s (startup continued)."
|
|
1347
|
+
echo "[wallet] WARNING: Wallet baseline timed out; continuing startup in degraded mode."
|
|
1348
|
+
else
|
|
1349
|
+
WALLET_ERROR=$(tail -n 1 "$WALLET_SETUP_LOG" 2>/dev/null || echo "wallet setup failed")
|
|
1350
|
+
echo "[wallet] WARNING: Wallet baseline failed; continuing startup in degraded mode."
|
|
1351
|
+
fi
|
|
1352
|
+
echo "[wallet] See: $WALLET_SETUP_LOG"
|
|
1353
|
+
fi
|
|
1354
|
+
|
|
1355
|
+
if [[ "$WALLET_HEALTHY" != "true" ]]; then
|
|
1356
|
+
WALLET_STARTUP_MODE="degraded"
|
|
1357
|
+
fi
|
|
1358
|
+
fi
|
|
1359
|
+
|
|
1360
|
+
TAILSCALE_DNS_NAME=""
|
|
1361
|
+
TAILSCALE_ADMIN_URL="N/A"
|
|
1362
|
+
TAILSCALE_SSH_CMD="N/A"
|
|
1363
|
+
TAILSCALE_SERVE_READY=0
|
|
1364
|
+
if command -v tailscale >/dev/null 2>&1; then
|
|
1365
|
+
if [[ "${FASED_TAILSCALE_AUTO_SERVE:-1}" == "1" ]]; then
|
|
1366
|
+
tailscale serve --bg "http://127.0.0.1:${FASED_GATEWAY_PORT}" >/dev/null 2>&1 || \
|
|
1367
|
+
sudo tailscale serve --bg "http://127.0.0.1:${FASED_GATEWAY_PORT}" >/dev/null 2>&1 || \
|
|
1368
|
+
tailscale serve https / "http://127.0.0.1:${FASED_GATEWAY_PORT}" >/dev/null 2>&1 || true
|
|
1369
|
+
fi
|
|
1370
|
+
if tailscale serve status 2>/dev/null | grep -q "127.0.0.1:${FASED_GATEWAY_PORT}" || \
|
|
1371
|
+
sudo tailscale serve status 2>/dev/null | grep -q "127.0.0.1:${FASED_GATEWAY_PORT}"; then
|
|
1372
|
+
TAILSCALE_SERVE_READY=1
|
|
1373
|
+
fi
|
|
1374
|
+
TAILSCALE_DNS_NAME=$(tailscale status --json 2>/dev/null | jq -r '.Self.DNSName // empty' | tr -d '\n' || true)
|
|
1375
|
+
TAILSCALE_DNS_NAME="${TAILSCALE_DNS_NAME%.}"
|
|
1376
|
+
if [[ -n "$TAILSCALE_DNS_NAME" ]]; then
|
|
1377
|
+
TAILSCALE_SSH_CMD="tailscale ssh app@${TAILSCALE_DNS_NAME}"
|
|
1378
|
+
if [[ "$TAILSCALE_SERVE_READY" == "1" ]]; then
|
|
1379
|
+
TAILSCALE_ADMIN_URL="https://${TAILSCALE_DNS_NAME}"
|
|
1380
|
+
fi
|
|
1381
|
+
fi
|
|
1382
|
+
fi
|
|
1383
|
+
|
|
1384
|
+
FED_HANDLE=$(jq -r '.handle // "N/A"' "$TOKEN_PATH" 2>/dev/null || echo "N/A")
|
|
1385
|
+
FED_TOKEN_ID=$(jq -r '.tokenId // "N/A"' "$TOKEN_PATH" 2>/dev/null || echo "N/A")
|
|
1386
|
+
FED_EXPIRES_AT=$(jq -r '.expiresAt // "N/A"' "$TOKEN_PATH" 2>/dev/null || echo "N/A")
|
|
1387
|
+
FED_AGENT_SLUG=$(jq -r '.agentSlug // "N/A"' "$TOKEN_PATH" 2>/dev/null || echo "N/A")
|
|
1388
|
+
FED_PUBLIC_URL=$(jq -r '.publicUrl // "N/A"' "$TOKEN_PATH" 2>/dev/null || echo "N/A")
|
|
1389
|
+
FED_ZROK_TOKEN=$(jq -r '.zrokToken // empty' "$TOKEN_PATH" 2>/dev/null || true)
|
|
1390
|
+
FED_ZROK_TOKEN_MASKED="N/A"
|
|
1391
|
+
if [[ -n "$FED_ZROK_TOKEN" ]]; then
|
|
1392
|
+
FED_ZROK_TOKEN_MASKED=$(mask_secret "$FED_ZROK_TOKEN")
|
|
1393
|
+
fi
|
|
1394
|
+
RES_TOKEN_MASKED="N/A"
|
|
1395
|
+
if [[ -n "${RES_TOKEN:-}" ]]; then
|
|
1396
|
+
RES_TOKEN_MASKED=$(mask_secret "$RES_TOKEN")
|
|
1397
|
+
fi
|
|
1398
|
+
GATEWAY_TOKEN_MASKED=$(mask_secret "$(tr -d '\n' < "$GW_TOKEN_PATH")")
|
|
1399
|
+
|
|
1400
|
+
echo "==> Agent Running."
|
|
1401
|
+
echo ""
|
|
1402
|
+
echo "================== FASED STARTUP SUMMARY =================="
|
|
1403
|
+
echo "Gateway"
|
|
1404
|
+
echo " PID: $AGENT_PID"
|
|
1405
|
+
echo " Port: $FASED_GATEWAY_PORT"
|
|
1406
|
+
echo " Boot log: $GATEWAY_BOOT_LOG"
|
|
1407
|
+
echo "Federation"
|
|
1408
|
+
echo " Handle: $FED_HANDLE"
|
|
1409
|
+
echo " Token ID: $FED_TOKEN_ID"
|
|
1410
|
+
echo " Expires At: $FED_EXPIRES_AT"
|
|
1411
|
+
echo " Agent Slug: $FED_AGENT_SLUG"
|
|
1412
|
+
echo " Public URL (token): $FED_PUBLIC_URL"
|
|
1413
|
+
echo " zrokToken: $FED_ZROK_TOKEN_MASKED"
|
|
1414
|
+
echo "Federation/A2A Tunnel (zrok)"
|
|
1415
|
+
echo " PID: ${ZROK_PID:-N/A}"
|
|
1416
|
+
echo " Slug: ${SLUG:-N/A}"
|
|
1417
|
+
echo " Reservation token: $RES_TOKEN_MASKED"
|
|
1418
|
+
echo " Public URL (A2A): $FINAL_URL"
|
|
1419
|
+
echo " Runtime log: $ZROK_RUNTIME_LOG"
|
|
1420
|
+
if [[ "$MANAGED_TUNNEL_DISABLED" == "1" ]]; then
|
|
1421
|
+
echo " Startup Mode: degraded"
|
|
1422
|
+
echo " Warning: ${TUNNEL_ERROR:-Fased Network tunnel unavailable; dashboard gateway kept online}"
|
|
1423
|
+
fi
|
|
1424
|
+
echo "Wallet"
|
|
1425
|
+
echo " Healthy: $WALLET_HEALTHY"
|
|
1426
|
+
echo " Startup Mode: $WALLET_STARTUP_MODE"
|
|
1427
|
+
echo " Auth State: $WALLET_AUTH_STATE"
|
|
1428
|
+
echo " Auth Mode: $WALLET_AUTH_MODE"
|
|
1429
|
+
echo " Auth Source: $WALLET_AUTH_SOURCE"
|
|
1430
|
+
echo " Mode: $WALLET_MODE"
|
|
1431
|
+
echo " PID: $WALLET_PID"
|
|
1432
|
+
echo " Service: $WALLET_SERVICE"
|
|
1433
|
+
echo " Chains: $WALLET_CHAINS"
|
|
1434
|
+
echo " Direct Signing: $WALLET_DIRECT_SIGNING"
|
|
1435
|
+
echo " Tool Scope: $WALLET_TOOL_SCOPE"
|
|
1436
|
+
echo " Keys path: $WALLET_KEYS_PATH"
|
|
1437
|
+
echo " Runtime log: $WALLET_SETUP_LOG"
|
|
1438
|
+
if [[ "$WALLET_STARTUP_MODE" == "degraded" ]]; then
|
|
1439
|
+
echo " Warning: wallet degraded; gateway/tunnel kept online"
|
|
1440
|
+
if [[ -n "$WALLET_ERROR" ]]; then
|
|
1441
|
+
echo " Last Error: $WALLET_ERROR"
|
|
1442
|
+
fi
|
|
1443
|
+
fi
|
|
1444
|
+
echo "Signer"
|
|
1445
|
+
echo " Startup Mode: $SIGNERD_STARTUP_MODE"
|
|
1446
|
+
if [[ -n "$SIGNERD_ERROR" ]]; then
|
|
1447
|
+
echo " Last Error: $SIGNERD_ERROR"
|
|
1448
|
+
fi
|
|
1449
|
+
echo "Admin Access (Tailscale)"
|
|
1450
|
+
echo " Admin URL: $TAILSCALE_ADMIN_URL"
|
|
1451
|
+
if [[ "$TAILSCALE_ADMIN_URL" == "N/A" ]]; then
|
|
1452
|
+
if [[ -n "$TAILSCALE_DNS_NAME" ]]; then
|
|
1453
|
+
echo " Status: DNS present but tailscale serve is not active for 127.0.0.1:${FASED_GATEWAY_PORT}"
|
|
1454
|
+
echo " Fix: tailscale serve --bg http://127.0.0.1:${FASED_GATEWAY_PORT}"
|
|
1455
|
+
else
|
|
1456
|
+
echo " Status: tailscale DNS unavailable; run 'tailscale up --ssh' and verify with 'tailscale status'"
|
|
1457
|
+
fi
|
|
1458
|
+
else
|
|
1459
|
+
echo " Status: tailnet-only URL (not public internet)"
|
|
1460
|
+
fi
|
|
1461
|
+
echo " SSH command: $TAILSCALE_SSH_CMD"
|
|
1462
|
+
echo " Auth: Gateway token/password still required by Fased UI/API"
|
|
1463
|
+
echo " Public login link: disabled (no one-time public dashboard links)"
|
|
1464
|
+
echo "Secrets"
|
|
1465
|
+
echo " Gateway token file: $GW_TOKEN_PATH"
|
|
1466
|
+
echo " Gateway token: $GATEWAY_TOKEN_MASKED"
|
|
1467
|
+
echo " Federation token: $TOKEN_PATH"
|
|
1468
|
+
echo "==========================================================="
|
|
1469
|
+
|
|
1470
|
+
echo ""
|
|
1471
|
+
echo "Stream gateway logs: tail -f $GATEWAY_BOOT_LOG"
|
|
1472
|
+
echo "Stream zrok logs: tail -f $ZROK_RUNTIME_LOG"
|
|
1473
|
+
|
|
1474
|
+
cleanup_managed_runtime() {
|
|
1475
|
+
local current_zrok_pid=""
|
|
1476
|
+
local current_monitor_pid=""
|
|
1477
|
+
if [[ -f "$FASED_CONFIG_DIR/.zrok-pid" ]]; then
|
|
1478
|
+
current_zrok_pid="$(cat "$FASED_CONFIG_DIR/.zrok-pid" 2>/dev/null || true)"
|
|
1479
|
+
fi
|
|
1480
|
+
if [[ -f "$ZROK_MONITOR_PID_FILE" ]]; then
|
|
1481
|
+
current_monitor_pid="$(cat "$ZROK_MONITOR_PID_FILE" 2>/dev/null || true)"
|
|
1482
|
+
fi
|
|
1483
|
+
if [[ -n "${AGENT_PID:-}" ]] && kill -0 "$AGENT_PID" 2>/dev/null; then
|
|
1484
|
+
kill "$AGENT_PID" 2>/dev/null || true
|
|
1485
|
+
fi
|
|
1486
|
+
if [[ -n "$current_zrok_pid" ]] && kill -0 "$current_zrok_pid" 2>/dev/null; then
|
|
1487
|
+
kill "$current_zrok_pid" 2>/dev/null || true
|
|
1488
|
+
fi
|
|
1489
|
+
if [[ -n "${ZROK_PID:-}" ]] && kill -0 "$ZROK_PID" 2>/dev/null; then
|
|
1490
|
+
kill "$ZROK_PID" 2>/dev/null || true
|
|
1491
|
+
fi
|
|
1492
|
+
if [[ -n "$current_monitor_pid" ]] && kill -0 "$current_monitor_pid" 2>/dev/null; then
|
|
1493
|
+
kill "$current_monitor_pid" 2>/dev/null || true
|
|
1494
|
+
fi
|
|
1495
|
+
if [[ -n "${ZROK_MONITOR_PID:-}" ]] && kill -0 "$ZROK_MONITOR_PID" 2>/dev/null; then
|
|
1496
|
+
kill "$ZROK_MONITOR_PID" 2>/dev/null || true
|
|
1497
|
+
fi
|
|
1498
|
+
rm -f "$FASED_CONFIG_DIR/.zrok-pid" "$ZROK_MONITOR_PID_FILE" 2>/dev/null || true
|
|
1499
|
+
stop_existing_signerd >/dev/null 2>&1 || true
|
|
1500
|
+
force_stop_local_gateway
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
MANAGED_RUNTIME_SHUTTING_DOWN=0
|
|
1504
|
+
|
|
1505
|
+
handle_managed_runtime_signal() {
|
|
1506
|
+
MANAGED_RUNTIME_SHUTTING_DOWN=1
|
|
1507
|
+
cleanup_managed_runtime
|
|
1508
|
+
exit 0
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
trap cleanup_managed_runtime EXIT
|
|
1512
|
+
trap handle_managed_runtime_signal INT TERM
|
|
1513
|
+
if kill -0 "$AGENT_PID" 2>/dev/null; then
|
|
1514
|
+
wait "$AGENT_PID" || true
|
|
1515
|
+
fi
|
|
1516
|
+
|
|
1517
|
+
if [[ "$MANAGED_RUNTIME_SHUTTING_DOWN" == "1" ]]; then
|
|
1518
|
+
exit 0
|
|
1519
|
+
fi
|
|
1520
|
+
|
|
1521
|
+
echo "[gateway] supervising listener on port ${FASED_GATEWAY_PORT}."
|
|
1522
|
+
while true; do
|
|
1523
|
+
if ! is_gateway_listener_ready; then
|
|
1524
|
+
echo "[gateway] ERROR: Gateway listener stopped on port ${FASED_GATEWAY_PORT}."
|
|
1525
|
+
exit 1
|
|
1526
|
+
fi
|
|
1527
|
+
sleep 5
|
|
1528
|
+
done
|