Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -108,20 +108,14 @@ RUN chmod +x /app/deploy/start.sh
# nodes_config.json is runtime-editable and stays in the volume; constants.py
# is source code and must follow the image.
#
# blah2_nodes.json is runtime-editable too, but it also has to be *seedable*:
# on an existing deployment the volume masks backend/config, so a copy shipped
# only there would be invisible and the bridge would poll nothing. The pristine
# copy is what runtime_config.default_source_path() seeds the overlay from.
#
# Layout: /app/deploy/config-image/config/constants.py (no __init__.py so
# Python treats 'config' as a namespace package and merges all 'config/'
# dirs on sys.path). start.sh prepends /app/deploy/config-image to
# PYTHONPATH so this copy takes priority over the potentially-stale volume
# copy at /app/backend/config/constants.py — even when the volume is
# root-owned and the cp refresh fails.
RUN mkdir -p /app/deploy/config-image/config && \
cp /app/backend/config/constants.py /app/deploy/config-image/config/constants.py && \
cp /app/backend/config/blah2_nodes.json /app/deploy/config-image/config/blah2_nodes.json
cp /app/backend/config/constants.py /app/deploy/config-image/config/constants.py

# ── Non-root user ────────────────────────────────────────────────────────────
RUN useradd -r -s /usr/sbin/nologin appuser && \
Expand Down
57 changes: 0 additions & 57 deletions backend/config/blah2_nodes.json

This file was deleted.

6 changes: 0 additions & 6 deletions backend/config/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,12 +456,6 @@ def _assoc_alt_layers_km() -> tuple[float, ...]:
IQ_COMMITMENTS_MAX_PER_NODE = 200 # Max IQ commitments per node (rolling)
RATE_BUCKETS_MAX_IPS = 10_000 # Max unique IPs in rate limiter

# ── blah2 bridge ─────────────────────────────────────────────────────────────
BLAH2_POLL_INTERVAL_S = 1.0 # blah2 API poll cadence (s)
BLAH2_STALE_THRESHOLD_S = 10.0 # Ignore frames older than this (s)
BLAH2_RECONNECT_DELAY_S = 5.0 # Backoff after failures (s)
BLAH2_MAX_FAILURES = 5 # Failures before backing off

# ── Node retirement ──────────────────────────────────────────────────────────
# Which node ids the admin route will force-retire. Empty, the default,
# imposes no restriction, which is what production wants: a decommissioned
Expand Down
2 changes: 1 addition & 1 deletion backend/core/runtime_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
# invisible on an existing deployment, because the volume masks the directory.
_IMAGE_DEFAULTS_DIR = _BACKEND_DIR.parent / "deploy" / "config-image" / "config"

_RUNTIME_FILES = ("nodes_config.json", "blah2_nodes.json")
_RUNTIME_FILES = ("nodes_config.json",)

logger = logging.getLogger(__name__)

Expand Down
12 changes: 0 additions & 12 deletions backend/core/task_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,9 @@
"storage_refresh": 720, # expected every 300 s; alert if >2× late
"track_archive_flush": 180, # flush every 60 s; alert if >3× late
"users_db_backup": 86400 * 2, # daily; alert if it hasn't run in 2 days
# The blah2 bridge registers one key per configured live node at startup
# (see services/blah2_bridge.load_nodes) — its node list is config-driven,
# so those keys cannot be enumerated here.
}


def register_task(name: str, expected_interval_s: int) -> None:
"""Add a dynamically-discovered task to the staleness registry.

For tasks whose number is not known until config is read. Idempotent, so
re-reading a config file does not disturb an already-registered task.
"""
TASK_EXPECTED_INTERVAL_S.setdefault(name, expected_interval_s)


def get_stale_tasks() -> list[str]:
"""Tasks that have not reported success within 2x their expected interval.

Expand Down
7 changes: 0 additions & 7 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@
track_flush_task,
users_backup_task,
)
from services.blah2_bridge import blah2_bridge_task
from services.blah2_bridge import load_nodes as load_blah2_nodes
from services.runtime_coverage import start as _start_coverage
from services.runtime_coverage import stop as _stop_coverage
from services.state_snapshot import SAVE_INTERVAL_S, restore_snapshot, save_snapshot
Expand Down Expand Up @@ -145,10 +143,6 @@ async def lifespan(app: FastAPI):

migrate_defaults_into_runtime()

# Live blah2 nodes are config-driven — read after the overlay is seeded so
# the runtime copy wins, and before the task list is built below.
blah2_nodes = load_blah2_nodes()

# No-op everywhere except tests (RETINA_SCHEMA_SOURCE guards create_all off
# otherwise). The schema comes from Alembic migrations instead: deploy/start.sh
# runs them before uvicorn starts, and `just setup` runs them for local dev.
Expand Down Expand Up @@ -218,7 +212,6 @@ async def _snapshot_loop():
asyncio.create_task(coverage_constraints_task()),
asyncio.create_task(storage_refresh_task()),
asyncio.create_task(detection_mirror.mirror_task()),
*[asyncio.create_task(blah2_bridge_task(n)) for n in blah2_nodes],
asyncio.create_task(health_monitor_task()),
asyncio.create_task(heartbeat_task()),
asyncio.create_task(_snapshot_loop()),
Expand Down
8 changes: 8 additions & 0 deletions backend/pipeline/passive_radar.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@
from services.publication import is_private

# ─── Node Configuration ─────────────────────────────────────────────
# Fallback geometry for a frame whose node has no configuration of its own: a
# real receiver and its illuminator, kept because they are a self-consistent
# bistatic pair. Which site, and the fit behind the numbers, is in ClickUp
# 86cb6385b rather than here: a receiver position is someone's home and this
# repo is public.
#
# __init__ takes this branch for any falsy node_config, an empty dict included,
# so being registered is not on its own enough to be solved against your own.
DEFAULT_NODE_CONFIG = {
"node_id": "net13",
"Fs": 2_000_000, # Sample rate Hz
Expand Down
14 changes: 0 additions & 14 deletions backend/routes/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,8 +724,6 @@ def _mlat_verification_summary() -> dict:

# ── Per-node solver verification ──────────────────────────────────────────────

_RADAR3_NODE_ID = "radar3-retnode"


@router.get("/api/test/node/{node_id}/verification")
async def node_verification(node_id: str):
Expand All @@ -736,12 +734,6 @@ async def node_verification(node_id: str):
)


@router.get("/api/test/radar3/verification")
async def radar3_verification():
"""Back-compat alias for the radar3 node's verification stats."""
return await node_verification(_RADAR3_NODE_ID)


@router.get("/api/test/mlat-verification")
async def mlat_verification():
"""Return pre-computed multinode (MLAT) solver-vs-ground-truth verification stats."""
Expand Down Expand Up @@ -1753,9 +1745,3 @@ async def node_detection_range(node_id: str):
),
media_type="application/json",
)


@router.get("/api/test/radar3/detection-range")
async def radar3_detection_range():
"""Back-compat alias for the radar3 node's detection range."""
return await node_detection_range(_RADAR3_NODE_ID)
Loading
Loading