#!/bin/sh /etc/rc.common
# relayway-cored — OpenWrt procd service for the embedded router admin API +
# dashboard. Serves the Flutter web UI (shipped at /usr/share/relayway-cored/www)
# on the UCI-configured port (default 7654) so the luci-app can iframe it.
#
# UCI: config relayway 'cored'  →  option port '7654'  option listen '0.0.0.0'
#   listen defaults to 0.0.0.0 so the panel is reachable from a LAN computer's
#   browser out of the box (OpenWrt's firewall DROPs WAN→router input, so this is
#   LAN-only in practice). NOTE: /consumer login is token-gated, but /api/v1
#   admin endpoints are only HMAC-gated when RELAYWAY_CORED_HMAC_SECRET is set
#   (this init does not set one), so on the LAN they are currently unauthenticated.
#   Set listen '127.0.0.1' for loopback-only (then reach it via SSH tunnel).
START=96
STOP=09
USE_PROCD=1
PROG=/usr/bin/relayway-cored

start_service() {
    config_load relayway
    local port listen airport_id middleware_url middleware_urls base_url config dat_dir tun tun_instances capture_mode log_level
    config_get port   cored port   7654
    config_get listen cored listen '0.0.0.0'
    # Per-airport identity (baked by build-ipk.sh, or set by an operator). cored's
    # consumer channel reads RELAYWAY_CORED_AIRPORT_ID / RELAYWAY_CORED_MW_URL to
    # auto-register with the right airport/brand. Unset → consumer channel stays
    # idle (the dashboard still serves; operator can configure later via UCI).
    config_get airport_id     cored airport_id     ''
    config_get middleware_url cored middleware_url ''
    # Ordered failover list (comma/whitespace separated, domestic-first); cored's
    # mw_upstream::candidates() fails over across these on transport errors so a
    # GFW-RST'd / down overseas ingress no longer wedges login. Empty → only the
    # single middleware_url is used (no failover).
    config_get middleware_urls cored middleware_urls ''
    # Self-update source for cored's /consumer/self-update (re-runs install.sh).
    config_get base_url       cored base_url       ''
    # Engine config + TUN data plane (web softrouter gap §6). WITHOUT -c, cored
    # runs an EMPTY engine (HTTP-only) so `consumer_connect` injects a node that
    # nothing feeds → "connected but no traffic". WITH --tun, cored brings up the
    # transparent TUN data plane that routes LAN traffic through the selected
    # node. dat_dir holds rd.bin/rs.bin so geoip:/geosite: CN-split rules work.
    config_get config   cored config  '/etc/relayway/config.json'
    config_get dat_dir  cored dat_dir '/etc/relayway'
    config_get_bool tun cored tun 1
    config_get tun_instances cored tun_instances ''

    # Prevent IPv6 proxy bypass on LAN clients. Keep this in shell/UCI instead of
    # nftables so old OpenWrt/fw3/opkg soft-routers get the same protection.
    [ "$tun" = "1" ] && [ -x /usr/bin/relayway-ipv6-guard ] && \
        /usr/bin/relayway-ipv6-guard apply 2>/dev/null || true

    # NOTE (web softrouter gap §6 Part 2): crash recovery is handled IN cored
    # (src/tun_recovery.rs), not here. On a hard crash (SIGKILL/OOM) cored's
    # in-process teardown can't run, so on the next start cored restores the WAN
    # default route + resolv.conf from a persisted gateway marker. An earlier
    # init-level `/etc/init.d/network reload` attempt was dropped — verified on
    # OpenWrt 25.12.4 that it does NOT re-assert the default route.

    procd_open_instance
    procd_set_param command "$PROG" --listen "${listen}:${port}"
    [ -f "$config" ]  && procd_append_param command -c "$config"
    [ "$tun" = "1" ]  && procd_append_param command --tun
    [ -n "$dat_dir" ] && procd_append_param command -d "$dat_dir"
    # cored serves the dashboard from $RELAYWAY_CORED_WWW (default
    # /usr/share/relayway-cored/www) — set explicitly so it survives a
    # future default change.
    procd_set_param env RELAYWAY_CORED_WWW=/usr/share/relayway-cored/www
    [ -n "$airport_id" ]     && procd_append_param env "RELAYWAY_CORED_AIRPORT_ID=$airport_id"
    [ -n "$middleware_url" ] && procd_append_param env "RELAYWAY_CORED_MW_URL=$middleware_url"
    [ -n "$middleware_urls" ] && procd_append_param env "RELAYWAY_CORED_MW_URLS=$middleware_urls"
    [ -n "$base_url" ]       && procd_append_param env "RELAYWAY_CORED_BASE_URL=$base_url"
    # Point cored's geo-DB auto-download at the airport's mirror (base_url, which
    # is CN-reachable) instead of the GitHub default. cored runtime-downloads
    # rd.bin/rs.bin when they're missing/stale; the GitHub default fails on a CN
    # router with no node connected → empty geoip:/geosite: matchers → CN-direct
    # breaks. build-release-bundle.sh publishes rd.bin/rs.bin next to install.sh
    # on base_url. (install.sh/the .ipk already lay the files down; this is the
    # self-heal path if they're ever missing.)
    [ -n "$base_url" ]       && procd_append_param env "RELAYWAY_GEOIP_URL=$base_url/rd.bin"
    [ -n "$base_url" ]       && procd_append_param env "RELAYWAY_GEOSITE_URL=$base_url/rs.bin"
    [ -n "$tun_instances" ]  && procd_append_param env "RELAYWAY_TUN_INSTANCES=$tun_instances"
    # raw-IP capture mode. DEFAULT is now 'all' (unset → native.rs AllForwarded):
    # ALL forwarded LAN traffic enters the user-space TUN; cored still splits
    # geoip:cn/private + geosite:cn to the `direct` outbound. This fixes apps that
    # dial HARDCODED IPs WITHOUT a DNS lookup — Telegram MTProto, some games, VoIP
    # — which never get a fake-IP, so under the old 'fakeip' default they missed
    # the `to 198.18.0.0/15` policy rule, fell to the WAN default, and got blocked
    # (2026-06-21 prod incident). ESCAPE HATCH: set UCI relayway.cored.capture_mode=
    # 'fakeip' to RESTORE the old kernel-fast-path (only fake-IP dests enter the
    # TUN; real-IP/direct bypass the user-space stack) — recommended for weak-CPU
    # routers where 'all' throttles big direct downloads, at the cost of re-exposing
    # raw-IP apps to blocking. See relayway-proxy native.rs linux_tun_capture_mode.
    config_get capture_mode cored capture_mode ''
    [ -n "$capture_mode" ] && procd_append_param env "RELAYWAY_TUN_CAPTURE_MODE=$capture_mode"
    # Log level pinned to `info` for production: a soft-router has little storage
    # and (below) logs go to logd, so never let it accumulate verbose/sensitive
    # output. Operators can raise it transiently via UCI for debugging.
    config_get log_level cored log_level 'info'
    procd_append_param env "RELAYWAY_CORED_LOG=$log_level"
    procd_set_param respawn 3600 5 5
    # stdout/stderr → procd → logd: a size-bounded syslog ring buffer in RAM
    # (auto-rotates, cleared on reboot), NOT a growing file on the router's
    # flash. `logread` shows recent lines; old ones drop off automatically — so
    # cored logs self-clean and never fill the limited storage.
    procd_set_param stdout 1
    procd_set_param stderr 1
    procd_close_instance
}

reload_service() {
    restart
}
