Skip to content

Commit a7e6fb9

Browse files
authored
feat(config): add REMOTE_CONFIG_ENABLED opt-in gate for remote config sync (#86)
The apl-feed config sync CLI now refuses to contact the website unless the feeder owner has explicitly opted in via REMOTE_CONFIG_ENABLED=true in feed.env. New apl-feed config enable / config disable subcommands flip the toggle through the canonical apply path. Default is off — a feeder that has not consented stays silent.
1 parent 72cf1b7 commit a7e6fb9

4 files changed

Lines changed: 405 additions & 2 deletions

File tree

scripts/apl-feed/config.sh

Lines changed: 133 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,28 @@ CONFIG_SYNC_LEGACY_EDITED_BY="legacy"
3232
CONFIG_SYNC_EXIT_OK=0
3333
CONFIG_SYNC_EXIT_BAD_CONFIG=64
3434

35+
# parse_opt_in RAW
36+
# echoes one of: enabled, disabled, invalid, empty
37+
# Mirrors parse_report_status in airplanes-diagnostics.sh.
38+
# Callers map "empty" to disabled — REMOTE_CONFIG_ENABLED is opt-in,
39+
# so absence means "not consented" rather than "default on".
40+
_config_sync_parse_opt_in() {
41+
local raw="$1"
42+
if [[ -z "$raw" ]]; then
43+
printf '%s' 'empty'
44+
return
45+
fi
46+
local lower
47+
lower="$(printf '%s' "$raw" | tr '[:upper:]' '[:lower:]')"
48+
lower="${lower#"${lower%%[![:space:]]*}"}"
49+
lower="${lower%"${lower##*[![:space:]]}"}"
50+
case "$lower" in
51+
true|yes|1|on) printf '%s' 'enabled' ;;
52+
false|no|0|off) printf '%s' 'disabled' ;;
53+
*) printf '%s' 'invalid' ;;
54+
esac
55+
}
56+
3557
# Sentinel mtime path. Overridable for tests + chroot smokes.
3658
CONFIG_SYNC_LAST_SUCCESS_FILE="${AIRPLANES_CONFIG_SYNC_LAST_SUCCESS:-/var/lib/airplanes/config-sync-last-success}"
3759

@@ -584,6 +606,27 @@ apl_feed_config_sync() {
584606
esac
585607
done
586608

609+
# Opt-in gate. REMOTE_CONFIG_ENABLED must be explicitly true before
610+
# this CLI contacts the website. Absent / empty / false all short-
611+
# circuit silently with exit 0 — the timer keeps ticking, the
612+
# collector exits silently each tick, no payload leaves the feeder.
613+
# An unparseable value surfaces as a hard config error (exit 64) so
614+
# operators see it in `apl-feed status` / `systemctl status`.
615+
local opt_in opt_in_state
616+
opt_in="$(feed_env_get REMOTE_CONFIG_ENABLED 2>/dev/null || true)"
617+
opt_in_state="$(_config_sync_parse_opt_in "$opt_in")"
618+
case "$opt_in_state" in
619+
disabled|empty)
620+
_config_sync_log info "status=disabled reason=opt_in_required"
621+
return "$CONFIG_SYNC_EXIT_OK"
622+
;;
623+
invalid)
624+
_config_sync_log error "status=bad_config key=REMOTE_CONFIG_ENABLED value=${opt_in}"
625+
return "$CONFIG_SYNC_EXIT_BAD_CONFIG"
626+
;;
627+
enabled) ;;
628+
esac
629+
587630
require_jq
588631

589632
if (( dry_run )); then
@@ -705,12 +748,100 @@ apl_feed_config_sync() {
705748
return "$CONFIG_SYNC_EXIT_OK"
706749
}
707750

751+
# Operator-facing toggle for the REMOTE_CONFIG_ENABLED opt-in. Mirrors
752+
# _diagnostics_apply / _diagnostics_emit_result in apl-feed/diagnostics.sh:
753+
# routes the sparse update through apl_feed_apply so the canonical
754+
# privileged writer handles locking, validation, and atomic rewrite.
755+
# REMOTE_CONFIG_ENABLED is registered as a no-restart key in
756+
# feed-env-keys.sh — the next sync tick (within ~60s) reads the new
757+
# value at the top-of-function gate.
758+
_config_toggle_apply() {
759+
feed_env_ensure_canonical_for_write
760+
local -a args=()
761+
args+=(--feed-env "$(feed_env_write_path)")
762+
args+=(--lock-file "$(feed_env_lock_path)")
763+
if [[ "$ROOT" != "/" ]]; then
764+
args+=(--no-restart --no-audit)
765+
echo "Skipping service restart (--root=$ROOT, not the host root)" >&2
766+
fi
767+
CONFIG_TOGGLE_APPLY_RC=0
768+
apl_feed_apply "${args[@]}" "$@" || CONFIG_TOGGLE_APPLY_RC=$?
769+
}
770+
771+
_config_toggle_emit_result() {
772+
local success_msg="$1"
773+
case "$APL_APPLY_STATUS" in
774+
applied)
775+
echo "$success_msg"
776+
if (( ${#APL_APPLY_PENDING_RESTART[@]} > 0 )); then
777+
echo "Warning: failed to restart ${APL_APPLY_PENDING_RESTART[*]} — re-run: sudo systemctl restart ${APL_APPLY_PENDING_RESTART[*]}" >&2
778+
fi
779+
apl_feed_apply_emit_meta_warning
780+
return 0
781+
;;
782+
no_change)
783+
return 0
784+
;;
785+
rejected)
786+
local k
787+
for k in "${!APL_APPLY_ERRORS[@]}"; do
788+
echo "ERROR: $k: ${APL_APPLY_ERRORS[$k]}" >&2
789+
done
790+
return 1
791+
;;
792+
lock_timeout)
793+
echo "ERROR: could not acquire feed.env lock: $APL_APPLY_ERROR_MESSAGE" >&2
794+
return 1
795+
;;
796+
filesystem_error)
797+
echo "ERROR: $APL_APPLY_ERROR_MESSAGE" >&2
798+
return 1
799+
;;
800+
*)
801+
echo "ERROR: ${APL_APPLY_ERROR_MESSAGE:-apply failed with status ${APL_APPLY_STATUS:-<unset>}}" >&2
802+
return 1
803+
;;
804+
esac
805+
}
806+
807+
apl_feed_config_enable() {
808+
local opt_rc
809+
while [[ $# -gt 0 ]]; do
810+
if parse_common_option "$@"; then opt_rc=0; else opt_rc=$?; fi
811+
case "$opt_rc" in
812+
1) shift ;;
813+
2) shift 2 ;;
814+
0) die "unknown flag for config enable: $1" ;;
815+
esac
816+
done
817+
818+
_config_toggle_apply REMOTE_CONFIG_ENABLED=true
819+
_config_toggle_emit_result "REMOTE_CONFIG_ENABLED set to true (remote config sync enabled; next tick within ~60s will contact the website)"
820+
}
821+
822+
apl_feed_config_disable() {
823+
local opt_rc
824+
while [[ $# -gt 0 ]]; do
825+
if parse_common_option "$@"; then opt_rc=0; else opt_rc=$?; fi
826+
case "$opt_rc" in
827+
1) shift ;;
828+
2) shift 2 ;;
829+
0) die "unknown flag for config disable: $1" ;;
830+
esac
831+
done
832+
833+
_config_toggle_apply REMOTE_CONFIG_ENABLED=false
834+
_config_toggle_emit_result "REMOTE_CONFIG_ENABLED set to false (remote config sync disabled; the timer stays armed but the sync CLI exits silently each tick)"
835+
}
836+
708837
dispatch_config() {
709838
local sub="${1:-}"
710-
[[ -n "$sub" ]] || die "config requires a subcommand (sync)"
839+
[[ -n "$sub" ]] || die "config requires a subcommand (enable|disable|sync)"
711840
shift || true
712841
case "$sub" in
713-
sync) apl_feed_config_sync "$@" ;;
842+
enable) apl_feed_config_enable "$@" ;;
843+
disable) apl_feed_config_disable "$@" ;;
844+
sync) apl_feed_config_sync "$@" ;;
714845
-h|--help) usage ;;
715846
*) die "unknown config subcommand: $sub" ;;
716847
esac

scripts/lib/feed-env-keys.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ declare -ga APL_FEED_WRITABLE_KEYS=(
2828
DUMP978_SDR_SERIAL
2929
DUMP978_GAIN
3030
REPORT_STATUS
31+
REMOTE_CONFIG_ENABLED
3132
)
3233

3334
declare -ga APL_FEED_READABLE_KEYS=(
@@ -45,6 +46,7 @@ declare -ga APL_FEED_READABLE_KEYS=(
4546
DUMP978_SDR_SERIAL
4647
DUMP978_GAIN
4748
REPORT_STATUS
49+
REMOTE_CONFIG_ENABLED
4850
)
4951

5052
declare -gA APL_FEED_KEY_TYPE=(
@@ -60,6 +62,7 @@ declare -gA APL_FEED_KEY_TYPE=(
6062
[DUMP978_SDR_SERIAL]=dump978_serial
6163
[DUMP978_GAIN]=dump978_gain
6264
[REPORT_STATUS]=bool
65+
[REMOTE_CONFIG_ENABLED]=bool
6366
)
6467

6568
# Restart map: a key landing on disk → space-separated list of systemd units
@@ -80,6 +83,7 @@ declare -gA APL_FEED_KEY_RESTART=(
8083
[DUMP978_SDR_SERIAL]="dump978-fa airplanes-978"
8184
[DUMP978_GAIN]="dump978-fa"
8285
[REPORT_STATUS]=""
86+
[REMOTE_CONFIG_ENABLED]=""
8387
)
8488

8589
apl_feed_is_writable_key() {

0 commit comments

Comments
 (0)