Skip to content

Development Roadmap

Owner: Pierros Papadeas Last updated: 2026-04-08 Current version: v0.5.3

This is the single source of truth for TALOS development priorities. Items are organized by target release, then by priority within each release. Change status to APPROVED to greenlight work, or move items between versions as priorities shift.

2026-04-08 update: v0.6.0 has been re-themed around the always-on SDR + bidirectional TC&C architectural shift (Phase A from docs/research/08-always-on-sdr.md and docs/research/09-tcnc-integration.md). The previous v0.6.0 contents have been distributed across v0.6.1, v0.6.2, v0.6.3, and v0.7+.


Status Key

Status Meaning
APPROVED Signed off -- ready to implement
PROPOSED Needs sign-off before work begins
DONE Implemented and merged
DEFERRED Moved to a later version
DROPPED Will not implement

v0.3.1 -- Quick Wins (Bug Fixes and Hygiene)

Items that take minutes to hours. No architectural changes. Ship fast.

# Item Priority Effort Status Source
1 Wrap agent on_message in try/except (malformed JSON crashes handler) P0 5 min DONE arch-review #2
2 Add secure=True to session cookie set_cookie() call P0 1 min DONE sec-review #4
3 Delete dead core/tracker.py (161 lines, never imported) P0 1 min DONE arch-review #3
4 Remove stale window_start/window_end from AssignmentCreate and AssignmentResponse schemas P1 5 min DONE arch-review #6
5 Move ADMIN_EMAIL from hardcoded pierros@libre.space to ADMIN_EMAIL env var P1 5 min DONE arch-review #10
6 Replace raw dict login parameter with Pydantic model using EmailStr P1 15 min DONE sec-review #7
7 Add agent --org CLI argument and subscribe to org-scoped topics alongside legacy P1 30 min DONE arch-review #1
8 Compute footprint radius from orbital altitude instead of hardcoded 2,500 km P2 30 min DONE arch-review #12
9 Add max_length constraints to all string fields in API request models P2 1 hr DONE sec-review #10
10 Constrain Membership.role with DB CHECK + Literal["viewer","operator","owner"] P2 1 hr DONE sec-review #9
11 Add needs: [sast] to build stage in CI so deploys are gated on security scans P2 15 min DONE sec-review #8

v0.4.0 -- Security and Observability

Close the security gaps. Know when things break. Target: 6 weeks.

Security

# Item Priority Effort Status Source
12 Enable MQTT authentication (allow_anonymous false) + password file P0 4 hr DONE sec-review #1
13 Implement MQTT topic ACLs (agents restricted to own station prefix) P0 8 hr DONE sec-review #2
14 Wire agent --key as MQTT password for broker auth P0 4 hr DONE sec-review #3
15 Enable MQTT TLS on port 8883, update all clients P1 8 hr DONE sec-review #5
16 Route dashboard MQTT through FastAPI WebSocket relay (eliminate direct browser-to-broker) P1 2 wk DONE exec-summary
17 Add CORS middleware with explicit origin allowlist P1 2 hr DONE exec-summary
18 Rate limiting on /auth/login and /auth/verify (5 req/min/IP) P1 4 hr DONE sec-review #6
19 Deprecate legacy endpoints (/stations/provision, /missions/add) that bypass RBAC P2 4 hr DONE sec-review #11
20 Pin dependency hashes with pip-compile --generate-hashes P0 2 hr DEFERRED sec-review #13

Observability

# Item Priority Effort Status Source
21 Prometheus metrics endpoints on Core and Director (tick duration, MQTT rate, active campaigns) P1 1 wk DONE (code only) ops-review #5
22 Director deep health-check HTTP endpoint (loop completed within 2x interval) P1 4 hr DONE ops-review #3
23 Structured JSON logging for Core and Director P1 4 hr DONE ops-review #4
24 Grafana dashboard JSON + Prometheus config (not deployed -- config only) P2 1 wk DONE (code only) exec-summary
25 Docker build caching in CI (--cache-from) P2 2 hr DONE ops-review #6

Agent Modernization

# Item Priority Effort Status Source
26 Refactor Python agent to asyncio (aiomqtt) with reconnection logic P1 2 wk DONE tech-roadmap
27 Extract inline dashboard JS into ES modules (map.js, mqtt.js, stations.js) P2 1 wk DONE tech-roadmap
28 Replace browser Paho MQTT JS with MQTT.js P2 3 day DONE tech-roadmap

Refactoring

# Item Priority Effort Status Source
29 Split core/main.py into app package (app.py, auth.py, routes/*.py, sync.py) P1 1 wk DONE arch-review #4
30 Extract duplicated auth/org/membership lookup into reusable dependency P1 4 hr DONE arch-review #5
31 Wire Pydantic response_model= on all API endpoints for auto-validation + OpenAPI docs P2 1 day DEFERRED arch-review #8
32 Remove legacy Mission code path (_tick_legacy(), ~150 lines) P2 4 hr DONE arch-review #11

v0.5.0 -- Performance and Data Resilience

Fix the Director scaling wall. Add TLE redundancy. Max 7 items per release.

# Item Priority Effort Status Source
36 Move pass prediction to background thread (protect 2 Hz loop) P0 8 hr DONE ops-review #2
37 Extract Propagator protocol interface in Director (enable backend swaps) P1 3 day DONE tech-roadmap
41 CelesTrak as fallback TLE source (eliminate SatNOGS single point of failure) P1 4 hr DONE satnogs-review #1
42 Fix public tracker TLE caching (route through SatNOGSClient instead of raw httpx) P1 2 hr DONE satnogs-review #2
44 Add dedicated unit tests for director/physics.py (pure functions) P1 1 day DONE arch-review #9
45 Add dedicated unit tests for director/station_manager.py P2 1 day DONE arch-review #9
39 Coverage-gate CI job (enforce minimum test coverage threshold) P2 4 hr DONE exec-summary

~~v0.5.1 -- Scheduling~~ (DROPPED)

Dropped 2026-04-05. TALOS uses a reactive ConOps: the Director's 2Hz tick loop tracks whatever is above the horizon based on priority — there are no time-windowed observation jobs to schedule. Items #33-35 solved a SatNOGS-style scheduling problem that does not exist in TALOS. Replaced by Director-level priority arbitration in v0.6.0 (#111, #112).

# Item Priority Effort Status Source
33 Assignment conflict detection on creation P1 1 wk DROPPED (reactive model — priority resolves conflicts at runtime) tech-roadmap
34 Greedy single-campaign auto-scheduler (pass windows + station availability) P1 1 wk DROPPED (no time windows — assignments are permanent station↔campaign links) tech-roadmap
35 OR-Tools CP-SAT multi-campaign optimizer P2 3 wk DROPPED (nothing to schedule — Director is reactive, not proactive) exec-summary
40 Performance regression gates (load test baseline comparison) P2 1 wk PROPOSED (moved to v0.6.0) ops-review #7

v0.5.2 -- Streaming Architecture (phasma-operations Integration)

Streaming-first data pipeline from edge flowgraphs through TALOS to external YAMCS instances. Replaces satnogs-client with TALOS agent as the ground station client. Aligns with phasma-operations (gitlab.com/librespacefoundation/phasma/phasma-operations).

Phase 1: Stream Schemas and Mission Registry

# Item Priority Effort Status Source
67 Add StreamCommand, FrameData, MissionEndpoint schemas to shared/schemas.py P0 2 hr DONE phasma-integration
68 Add stream topics (cmd/stream, stream/frames) to shared/topics.py P0 1 hr DONE phasma-integration
69 Add MissionLink model to core/database.py (campaign-to-YAMCS mapping) P0 2 hr DONE phasma-integration
70 Create core/routes/missions.py -- mission link CRUD API (GET/PUT/DELETE per campaign) P0 4 hr DONE phasma-integration
71 Fork satnogs-flowgraphs: replace file sinks with ZMQ PUSH sinks P1 1 wk PROPOSED phasma-integration

Phase 2: Stream Router in Core

# Item Priority Effort Status Source
72 Create core/stream_router.py -- MQTT subscriber + UDP forwarder to YAMCS P0 1 wk DONE phasma-integration
73 Wire stream router into FastAPI lifespan (startup/shutdown) P0 2 hr DONE phasma-integration

Phase 3: Agent Flowgraph Manager

# Item Priority Effort Status Source
74 Create agent/flowgraph.py -- subprocess spawner for GNU Radio flowgraphs P0 1 wk DONE phasma-integration
75 Extend agent/agent.py to handle cmd/stream topic and ZMQ-to-MQTT frame bridge P0 1 wk DONE phasma-integration
76 Add agent CLI args: --sdr-device, --zmq-port P1 2 hr DONE phasma-integration

Phase 4: Director AOS/LOS Stream Triggers

# Item Priority Effort Status Source
77 Emit StreamCommand on AOS/LOS pass boundaries from Director tick loop P0 1 wk DONE phasma-integration
78 Map campaign transmitter data to flowgraph params (freq, mod, baud) P1 4 hr DONE phasma-integration

Phase 5: YAMCS MQTT Direct (future)

# Item Priority Effort Status Source
79 Configure YAMCS MqttTmDataLink to subscribe directly to TALOS MQTT frame topics P2 1 wk PROPOSED phasma-integration
80 Eliminate UDP stream router -- YAMCS reads MQTT natively via yamcs-mqtt plugin P2 4 hr PROPOSED phasma-integration

v0.5.3 -- Code Review Hardening

Address critical and high-severity findings from the independent code review (2026-04-05). 32 findings total: 4 critical, 7 high, 12 medium, 9 low. Full report: docs/research/07-code-review-2026-04.md.

Critical (P0) -- must fix before next release

# Item Priority Effort Status Source
81 Add threading.Lock to Director globals (force_reload, manual_override_uuid) -- race condition between MQTT callback and main loop threads P0 1 hr DONE code-review S1
82 Lock or remove lazy-init on stream router _udp_sock -- MQTT callback thread races on shared socket P0 30 min DONE code-review S2
83 Remove magic link URL from log output when RESEND_API_KEY unset; add startup guard (refuse to serve if no email provider and BASE_URL != localhost) P0 30 min DONE code-review S4
20 Pin dependency hashes with pip-compile --generate-hashes (escalated from v0.4.0 DEFERRED) P0 2 hr DEFERRED (deferred: pip-compile requires platform-specific builds) sec-review #13, code-review S3

High (P0-P1) -- security and resilience gaps

# Item Priority Effort Status Source
84 Restrict CORS allow_headers from ["*"] to explicit list (Content-Type, Authorization, X-Requested-With) P0 10 min DONE code-review S5
85 Add path="/" to session cookie in /auth/verify P1 5 min DONE code-review S9
86 Apply rate limiting to admin endpoints (30/min), public API (60/min), campaign creation (20/min) P1 1 hr DONE code-review S6
87 Replace get_remote_address rate-limiter key with proxy-aware function (trust first X-Forwarded-For hop) P1 30 min DONE code-review S7
88 Sanitize user-controlled text fields (org name, description, campaign name, join request message) -- strip HTML on input P1 1 hr DONE code-review S8
89 Paginate admin dashboard queries (select(User), select(Organization), etc.) -- currently loads ALL rows into memory P1 2 hr DONE code-review S10
90 Log suppressed migration errors at WARNING instead of silently swallowing all exceptions in _apply_safe_migrations P1 30 min DONE code-review S11

Medium (P2) -- correctness and hardening

# Item Priority Effort Status Source
91 Return uniform response on login for unknown vs. disallowed emails (prevent account enumeration) P2 30 min DONE code-review S15
92 Validate geographic coordinates on Station (lat: -90..90, lon: -180..180, alt: -500..20000m) P2 30 min DONE code-review S18
93 Use BROKER_PORT env var in Director MQTT connection (currently hardcoded to 1883) P2 5 min DONE code-review S17
94 Close agent sockets explicitly on connection error in connect_rotator/connect_rig P2 15 min DONE code-review S23
95 Replace deprecated asyncio.get_event_loop() with asyncio.get_running_loop() in core/app.py P2 5 min DONE code-review S30
96 Agent telemetry: publish to org-scoped topic when ORG_SLUG is set (currently legacy topic only) P2 15 min DONE code-review S32

Testing -- close coverage gaps identified by review

# Item Priority Effort Status Source
97 Add stream router tests (frame routing, UDP forwarding, cache refresh, malformed payloads) P1 4 hr DONE code-review T1
98 Add auth security tests (JWT tampering, expired token, rate limit 429 responses) P1 2 hr DONE code-review T2
99 Add RBAC violation tests (access another org's resources, role escalation attempts) P2 2 hr DONE code-review T3
100 Add input validation edge-case tests (boundary lat/lon, priority 0/11, negative port, oversized strings) P2 2 hr DONE code-review T4

v0.5.5 -- Multi-Satellite Campaigns

A campaign currently tracks exactly ONE satellite. This version removes that limitation so a single campaign can target a constellation, a debris cluster, or a group of related objects — each with an independent weight that controls Director priority arbitration when multiple objects compete for the same station hardware.

Design principle: a CampaignSatellite join table replaces the single Campaign.norad_id column. Every downstream system (Director tick loop, TLE management, streaming triggers, dashboard visualization, campaign wizard) is updated to iterate over the satellite list instead of assuming one.

Phase 1: Data Model and Migration

# Item Priority Effort Status Source
113 Create CampaignSatellite model (campaign_id, satnogs_id, norad_id, name, weight 1-10 default 5, created_at) with unique constraint on (campaign_id, norad_id) P0 2 hr PROPOSED multi-sat
114 Alembic migration: add campaign_satellite table; migrate existing Campaign.norad_id / Campaign.satnogs_id rows into CampaignSatellite entries (weight=5); make Campaign.norad_id nullable (keep for backward compat during transition) P0 2 hr PROPOSED multi-sat
115 Add satellites: list[CampaignSatellite] relationship on Campaign model; deprecate Campaign.norad_id / Campaign.satnogs_id fields (remove in v0.6.0) P0 1 hr PROPOSED multi-sat
116 Update Transmitter model: add norad_id column so transmitters can be linked per-satellite within a campaign (currently only per-campaign via campaign_id) P1 1 hr PROPOSED multi-sat

Phase 2: API Changes

# Item Priority Effort Status Source
117 Update CreateCampaignRequest to accept satellites: list[{satnogs_id, norad_id, weight}] (minimum 1); keep legacy single satnogs_id/norad_id fields for backward compat (auto-wrap into single-element list) P0 2 hr PROPOSED multi-sat
118 Update POST /api/orgs/{slug}/campaigns: iterate satellite list, create CampaignSatellite rows, fetch transmitters per norad_id and link each with the correct norad_id P0 4 hr PROPOSED multi-sat
119 Add PUT /api/orgs/{slug}/campaigns/{id}/satellites — add/remove/reweight satellites on an existing campaign; re-fetch transmitters for newly added satellites P1 4 hr PROPOSED multi-sat
120 Update GET campaign endpoints to include satellites array in response with per-satellite weight, name, norad_id, satnogs_id P0 1 hr PROPOSED multi-sat
121 Update campaign activation: validate that all satellites have at least one transmitter before allowing status→active P1 1 hr PROPOSED multi-sat

Phase 3: Director Tick Loop

# Item Priority Effort Status Source
122 Refactor MultiTLEManager to key on (campaign_id, norad_id) tuple instead of campaign_id alone; get_or_create() accepts list of satellites, returns list of TLEManager objects P0 4 hr PROPOSED multi-sat
123 Refactor Director inner tick loop: for each assignment, iterate all CampaignSatellite objects; compute az/el per satellite; select the highest-weight satellite currently above the horizon as the tracking target P0 1 wk PROPOSED multi-sat
124 Update AOS/LOS logic: track state per (station_id, campaign_id, norad_id) triple; emit StreamCommand per satellite transition, not per campaign transition P0 4 hr PROPOSED multi-sat
125 Update select_transmitter(): filter transmitters by the currently-tracked satellite's norad_id within the campaign P1 2 hr PROPOSED multi-sat
126 Update visualization: publish per-satellite footprint/ground-track within campaign viz payload (array of SatFootprint instead of single); update MissionViz schema P1 4 hr PROPOSED multi-sat
127 Update background predictor: compute passes per (station_id, campaign_id, norad_id) triple; merge pass lists into campaign-level schedule for station P1 4 hr PROPOSED multi-sat

Phase 4: Dashboard UI

# Item Priority Effort Status Source
128 Update campaign sidebar detail panel: show satellite list with name, NORAD ID, weight badge, and current tracking indicator (which satellite the Director is currently pointing at) P0 4 hr PROPOSED multi-sat
129 Update map visualization: render footprint and ground track for ALL satellites in selected campaign (each with campaign color but varying opacity by weight); highlight the currently-tracked satellite P1 4 hr PROPOSED multi-sat
130 Update campaign creation wizard Step 1: multi-select satellite search — selected satellites appear as removable chips with editable weight sliders (1-10); minimum 1 satellite required to proceed P0 1 wk PROPOSED multi-sat
131 Update campaign creation wizard Step 4 (review): show satellite table with name, NORAD ID, weight, transmitter count per satellite P1 2 hr PROPOSED multi-sat
132 Update MQTT campaign viz handler: parse array of satellite footprints; render each independently on the map layer P1 2 hr PROPOSED multi-sat

Phase 5: Tests

# Item Priority Effort Status Source
133 Migration test: verify single-satellite campaigns are correctly migrated to CampaignSatellite table; verify norad_id backward compat P0 2 hr PROPOSED multi-sat
134 API tests: create campaign with 1, 3, 10 satellites; add/remove satellites from existing campaign; verify transmitter linkage per satellite; verify weight validation (1-10) P0 4 hr PROPOSED multi-sat
135 Director unit tests: mock multi-satellite campaign; verify highest-weight satellite above horizon is selected; verify AOS/LOS state transitions per satellite; verify StreamCommand emission per satellite P0 4 hr PROPOSED multi-sat
136 Import check: add CampaignSatellite to tests/test_imports.py module list P0 5 min PROPOSED multi-sat

v0.5.4 -- Batch Propagation

GPU-accelerated SGP4. Builds on v0.5.0 Propagator interface.

# Item Priority Effort Status Source
38 dSGP4 batch propagation backend (10-50x faster campaign planning) P1 3 wk DONE exec-summary
43 TimescaleDB for tracking telemetry persistence (mission TM goes to YAMCS/InfluxDB) P2 1 wk DONE tech-roadmap

v0.6.0 -- Always-On SDR + Bidirectional TC&C

The architectural shift: SDRs are always-on, raw IQ streams over ZeroMQ to multiple parallel flowgraphs, doppler is applied as a phase-continuous NCO inside the flowgraph (not via SDR retune), and Telemetry/Tracking & Command (TC&C) is end-to-end through YAMCS in BOTH directions — downlink frames AND uplink commands. gRPC controls the local Agent ↔ flowgraph hop; MQTT remains the Director ↔ Agent bus; rotctld stays for rotators (Hamlib's strength); rigctl is dropped for the SDR.

Design: see plan in this session and docs/research/08-always-on-sdr.md + docs/research/09-tcnc-integration.md. The legacy v0.5.x subprocess flowgraph pipeline ships untouched, gated behind a TALOS_PIPELINE=legacy feature flag (default for v0.6.0). The new modular pipeline ships behind TALOS_PIPELINE=modular, exercised in CI as a parallel job. Default flip happens in v0.6.2, legacy removed in v0.7.0.

Items #46-49, #52-56, #66, #112, #57 from the original v0.6.0 plan have been moved into v0.6.1, v0.6.2, v0.6.3, or v0.7+ to make room. The "Always-On SDR + Bidirectional TC&C" work is the primary v0.6.0 theme.

Target: 12 weeks. Critical invariant: Phase A must demonstrate end-to-end TC&C through YAMCS (TM downlink + TC uplink + ack) on real hardware.

Phase A.1: Schemas, topics, gRPC contracts

# Item Priority Effort Status Source
137 Create shared/flowgraphs.py with FlowgraphSpec, FlowgraphKind enum (rx_source, tx_source, decoder, modulator, aux), FLOWGRAPH_CATALOG static registry, resolver P0 4 hr PROPOSED always-on-sdr
138 Add DopplerUpdate, TxDopplerUpdate, UplinkCommand, UplinkAck, FlowgraphSpec Pydantic models to shared/schemas.py P0 2 hr PROPOSED always-on-sdr
139 Extend StreamCommand with optional flowgraph_id, tx_flowgraph_id, capture_center_hz, capture_rate_hz; keep all v0.5.x fields for legacy pipeline P0 1 hr PROPOSED always-on-sdr
140 Add MQTT topic builders to shared/topics.py: cmd/doppler, cmd/tx_doppler, cmd/uplink, uplink/ack (org-scoped + legacy variants) P0 1 hr PROPOSED always-on-sdr
141 Add MQTT ACL rules in ops/mosquitto/config/acl.conf for new topics (CLAUDE.md invariant 7) P0 30 min PROPOSED always-on-sdr
142 Define shared/proto/flowgraph.proto with 4 services: RxSourceControl, TxSourceControl, DecoderControl, ModulatorControl P0 4 hr PROPOSED always-on-sdr
143 Generate flowgraph_pb2.py / flowgraph_pb2_grpc.py (vendored or buildtime) and wire into the package P0 2 hr PROPOSED always-on-sdr
144 Add grpcio>=1.60 and grpcio-tools>=1.60 to agent/requirements.txt AND pyproject.toml (CLAUDE.md invariant 6); verify arm64 wheels P0 1 hr PROPOSED always-on-sdr

Phase A.2: Flowgraph catalog (RX + TX, minimal viable set)

# Item Priority Effort Status Source
145 Create flowgraphs/src/rx_source_v1.grc + generated .py: SoapySDR source -> ZMQ PUB sink + embedded RxSourceControl gRPC server P0 1 wk PROPOSED always-on-sdr
146 Create flowgraphs/src/tx_source_v1.grc + generated .py: ZMQ PULL -> SoapySDR sink + TxSourceControl gRPC server. On transceiver hardware shares the SoapySDR handle with rx_source via co-resident wrapper P0 1 wk PROPOSED always-on-sdr
147 Create flowgraphs/src/decoder_fm_v1.grc + generated .py: ZMQ SUB -> BPF -> phase-continuous rotator_cc NCO -> FM demod -> HDLC deframer -> ZMQ PUSH frames + DecoderControl gRPC server P0 1 wk PROPOSED always-on-sdr
148 Create flowgraphs/src/decoder_bpsk_v1.grc + generated .py: ZMQ SUB -> BPF -> rotator_cc -> BPSK demod -> deframer -> ZMQ PUSH P1 1 wk PROPOSED always-on-sdr
149 Create flowgraphs/src/modulator_bpsk_v1.grc + generated .py: gRPC SendCommand -> frame queue -> BPSK mod -> inverse rotator_cc -> ZMQ PUSH P0 1 wk PROPOSED tcnc
150 Create flowgraphs/src/modulator_fsk_v1.grc + generated .py: same shape for FSK uplinks (FM RX + FSK TX is the most common amateur LEO pairing) P0 1 wk PROPOSED tcnc
# Item Priority Effort Status Source
151 Create agent/flowgraph_orchestrator.py to replace FlowgraphManager on the modular path: manages long-lived RX and TX source flowgraphs plus N decoder + M modulator flowgraphs, each with its own gRPC stub; tracks dict[norad_id, DecoderHandle] and dict[norad_id, ModulatorHandle] P0 1 wk PROPOSED always-on-sdr
152 Create agent/grpc_clients.py with thin gRPC client wrappers for the four services; reusable from orchestrator and tests P0 4 hr PROPOSED always-on-sdr
153 Refactor agent/agent.py handle_message to route cmd/stream / cmd/doppler / cmd/tx_doppler / cmd/uplink through the orchestrator when TALOS_PIPELINE=modular; leave cmd/rig as no-op on modular path; preserve full legacy behavior on legacy P0 1 wk PROPOSED always-on-sdr
154 Agent boot-time: when modular flag is set, hot-start RX source flowgraph; if station config declares TX-capable hardware, also hot-start TX source flowgraph. PLLs ready before AOS. P0 4 hr PROPOSED always-on-sdr
155 Agent uplink ack path: after modulator's SendCommand completes, agent publishes UplinkAck{uplink_id, status, ts} on uplink/ack MQTT topic for Core uplink router to fan back to YAMCS P0 4 hr PROPOSED tcnc

Phase A.4: Director (RX + TX doppler emission)

# Item Priority Effort Status Source
156 Add calculate_tx_doppler() to director/physics.py: inverse range-rate sign vs calculate_doppler(); reuse existing skyfield topocentric vector P0 2 hr PROPOSED always-on-sdr
157 Promote director/physics.calculate_doppler() from int to float overload to preserve sub-Hz precision the NCO can use (current int(...) cast at physics.py:40 loses precision) P0 1 hr PROPOSED always-on-sdr
158 director/director.py (modular flag): emit DopplerUpdate to cmd/doppler per active sat at 2 Hz; emit TxDopplerUpdate to cmd/tx_doppler per uplink-capable active sat at 2 Hz; suppress cmd/rig emission P0 1 wk PROPOSED always-on-sdr
159 director/director.py: include flowgraph_id (from CampaignSatellite) and tx_flowgraph_id in StreamCommand on AOS; legacy fields preserved when modular flag unset P0 4 hr PROPOSED always-on-sdr
# Item Priority Effort Status Source
160 Create core/database.py Uplink table: (id, uplink_id, campaign_id, station_id, frame_hex, status, deadline, created_at, sent_at, ack_at, ack_status); Alembic migration P0 2 hr PROPOSED tcnc
161 Create core/routes/uplinks.py: POST /api/orgs/{slug}/campaigns/{id}/uplink (accepts TC frame, mints uplink_id, persists, publishes on cmd/uplink); GET /api/orgs/{slug}/uplinks/{uplink_id} (status poll for YAMCS) P0 1 wk PROPOSED tcnc
162 Create core/uplink_router.py: background MQTT subscriber on uplink/ack, correlates by uplink_id, updates Uplink row, fans ack back to YAMCS via HTTP callback or yamcs-mqtt topic P0 1 wk PROPOSED tcnc
163 Wire routes.uplinks and uplink_router into core/app.py lifespan (CLAUDE.md invariant 1: imports come from core.config, never core.app) P0 1 hr PROPOSED tcnc
164 Add yamcs_tc_link_type (HTTP | MQTT) and yamcs_tc_endpoint fields to MissionLink model; alembic migration; update core/routes/missions.py API P1 2 hr PROPOSED tcnc
165 Idempotency: duplicate submissions of the same uplink_id within 24h return the existing record without re-publishing; tests/test_uplink_idempotent.py proves it P0 2 hr PROPOSED tcnc
166 Authorization: uplink endpoint requires require_role("operator") from core/deps.py; uplink-disabled campaigns reject with 403 P0 1 hr PROPOSED tcnc

Phase A.6: Mission profile <-> flowgraph binding

# Item Priority Effort Status Source
167 Add optional decoder_flowgraph_id and modulator_flowgraph_id fields to CampaignSatellite (defaults decoder_fm_v1 and, if uplink-capable, modulator_fsk_v1); Alembic migration; preserve legacy campaigns P0 2 hr PROPOSED always-on-sdr
168 Update core/routes/campaigns.py create/update to accept and validate flowgraph IDs against FLOWGRAPH_CATALOG P1 2 hr PROPOSED always-on-sdr

Phase A.7: Tests

# Item Priority Effort Status Source
169 tests/test_flowgraph_catalog.py: catalog resolution + by_kind() filtering + unknown ID rejection P0 1 hr PROPOSED always-on-sdr
170 tests/test_flowgraph_orchestrator.py: unit tests with mocked gRPC for RX source, TX source, decoder, modulator; lifecycle (start/stop/restart) P0 1 day PROPOSED always-on-sdr
171 tests/test_agent_modular_pipeline.py: subprocess fakes for source + decoder + modulator using inproc:// ZMQ; verify frames flow end-to-end identical to legacy P0 1 day PROPOSED always-on-sdr
172 tests/test_director_modular_flag.py: assert Director emits cmd/doppler + cmd/tx_doppler on modular, cmd/rig on legacy, both at 2 Hz P0 4 hr PROPOSED always-on-sdr
173 tests/test_uplink_router.py: TC ingest -> MQTT fanout -> ack roundtrip -> YAMCS callback; correlation by uplink_id P0 1 day PROPOSED tcnc
174 tests/test_uplink_route.py: HTTP API for command release; auth/RBAC; validation; status polling P0 4 hr PROPOSED tcnc
175 tests/test_tx_doppler_math.py: calculate_tx_doppler() against known reference; sign inversion vs calculate_doppler() P0 2 hr PROPOSED always-on-sdr
176 tests/test_doppler_psk.py: phase-continuity test against time-varying doppler with synthetic PSK signal — verifies BOTH decoder NCO and modulator inverse NCO maintain carrier phase across UpdateDoppler/UpdateTxDoppler calls P0 1 day PROPOSED always-on-sdr
177 tests/test_uplink_idempotent.py: duplicate uplink_id within window -> single transmission, two acks correlated to same row P0 2 hr PROPOSED tcnc
178 Add all new modules to tests/test_imports.py (CLAUDE.md invariant 1; v0.4.0 lesson) P0 30 min PROPOSED always-on-sdr

Phase A.8: CI

# Item Priority Effort Status Source
179 Add test-modular CI job mirroring test-app with TALOS_PIPELINE=modular env (CLAUDE.md invariant 2: separate job because env differs) P0 2 hr PROPOSED always-on-sdr
180 Add test-uplink CI job exercising the Core TC ingest path against Postgres (separate job per CLAUDE.md invariant 2) P0 2 hr PROPOSED tcnc
181 Update HIL CI job to exercise both RX decode and TX uplink + ack roundtrip on Pi5+transceiver SDR; gated on $TALOS_HIL_RUNNER per v0.4.0 lesson P1 4 hr PROPOSED tcnc

Phase A.9: Docs and CLAUDE.md invariants

# Item Priority Effort Status Source
182 Create docs/research/08-always-on-sdr.md: full architecture analysis, ZMQ HWM gotchas, gnuradio #3877 phase-continuity, Estévez doppler references, Phase A scope P0 4 hr PROPOSED always-on-sdr
183 Create docs/research/09-tcnc-integration.md: TC&C uplink path design, YAMCS link options (MqttTcDataLink vs HTTP), idempotency semantics, ack flow, authorization model P0 4 hr PROPOSED tcnc
184 Update docs/research/06-phasma-integration.md to add a section about the modular pipeline as the new primary path; legacy subprocess pipeline becomes the fallback P1 2 hr PROPOSED always-on-sdr
185 Update CLAUDE.md with new invariants: (11) source flowgraphs own the SDR; (12) doppler is phase-continuous NCO inside decoder/modulator; (13) uplink commands carry uplink_id end-to-end P0 1 hr PROPOSED always-on-sdr
186 Update CLAUDE.md "Current State" + File Map with new modules and v0.6.0 entry once shipped P1 30 min PROPOSED always-on-sdr

Phase A.10: Hardware acceptance (blocking for v0.6.0 ship)

# Item Priority Effort Status Source
187 RX acceptance: full LEO pass decoded via modular pipeline on Pi5+transceiver SDR; frames reach a real YAMCS instance via existing TM datalink; decoded count matches legacy on the same IQ recording P0 2 day PROPOSED always-on-sdr
188 TX acceptance: during the same pass, YAMCS operator releases a TC via the new HTTP endpoint; modulator emits doppler-pre-comp signal on SDR TX; loopback receiver sees the carrier; UplinkAck propagates to YAMCS within deadline P0 2 day PROPOSED tcnc
189 Phase-continuity acceptance: tests/test_doppler_psk.py passes for both decoder NCO and modulator inverse NCO with realistic LEO doppler curve P0 4 hr PROPOSED always-on-sdr

Phase A: Items remaining from prior v0.6.0 plan that fit alongside the SDR work

Small, P0/P1, independent items kept in v0.6.0. Larger items moved to v0.6.1+.

Hardening that fits alongside the SDR work

Small, code-review-derived items that don't compete with Phase A scope.

# Item Priority Effort Status Source
50 Station hardware mutex (MQTT coordination for concurrent SDR access) — directly relevant to multi-decoder orchestration; SDR orchestrator uses this P1 1 wk PROPOSED satnogs-review #3
101 Hash station API keys (bcrypt) instead of storing plaintext P1 2 hr PROPOSED code-review S14
102 Add non-root USER directive to all Dockerfiles P2 30 min PROPOSED code-review S29
103 Add SBOM generation (cyclonedx-bom or syft) to CI pipeline P2 1 hr PROPOSED code-review S28
104 Fix Prometheus metric cardinality explosion -- use route templates instead of raw URL paths as labels P2 1 hr PROPOSED code-review S31a
105 Add API versioning prefix (/api/v1/) to all API endpoints — required since Core gains new uplink endpoints in Phase A.5 P1 4 hr PROPOSED code-review A1
106 Commit to Alembic-only migration strategy -- remove _apply_safe_migrations manual DDL — required since Phase A.5 adds new tables (Uplink) P2 4 hr PROPOSED code-review A2
107 Agent socket health checks + reconnect logic for dropped rotctld connections (rigctl drops only matter on the legacy pipeline) P1 2 hr PROPOSED code-review A3
108 Add Strict-Transport-Security header at application level (defense-in-depth) P2 15 min PROPOSED code-review S31b
109 Replace _detach_assignment bare-class hack with proper dataclass or NamedTuple P2 30 min PROPOSED code-review S21
110 Enforce deprecated Mission model EOL (2026-10-01): add startup warning, track removal P2 1 hr PROPOSED code-review S27

Director priority arbitration (kept — directly enables multi-decoder)

Replaces v0.5.1 scheduling (DROPPED). TALOS is reactive — the Director decides at runtime which campaign gets a station's hardware. Item #111 is the on-ramp for the modular pipeline's multi-decoder orchestration: it tells the orchestrator which sat to spawn a decoder for first.

# Item Priority Effort Status Source
111 Single-station priority preemption: when multiple assigned satellites are above the horizon, the Director drives the rotator/SDR to the highest-priority campaign and parks lower-priority ones until the higher one sets or completes. On the modular pipeline, this also drives which decoder/modulator pairs the orchestrator keeps running. P1 1 wk PROPOSED conops-review

Items moved out of v0.6.0 to make room for the SDR/TC&C work

The original v0.6.0 plan had ~30 items. To fit the always-on SDR + TC&C work in 12 weeks, the following are deferred:

  • #46 CesiumJS 3D globe (3 wk, P1) → v0.6.3
  • #47 HTMX for non-real-time pages (2 wk, P2) → v0.6.3
  • #48 CCSDS 502.0 (OMM) (2 wk, P1) → v0.6.2
  • #49 CCSDS 503.0 (TDM) (2 wk, P2) → v0.6.2
  • #51 Lightweight waterfall capture → v0.6.1 as part of aux_waterfall_v1 Phase B work
  • #52 Server-side session store (8 hr, P2) → v0.6.2
  • #53 PgBouncer (4 hr, P2) → v0.6.2
  • #54 Fly Postgres HA (2 hr, P2) → v0.6.2
  • #55 MQTT broker persistence on Fly (2 hr, P2) → v0.6.2
  • #56 MQTT 5.0 on Mosquitto (1 day, P2) → v0.6.2
  • #66 Prometheus + Grafana stack (1 day, P2) → v0.6.1 (with Phase B observability)
  • #112 Cross-station deduplication (2 wk, P1) → v0.6.1
  • #40 Performance regression gates (1 wk, P2) → v0.6.2
  • #57 Z-score anomaly detection on rotator telemetry (2 wk, P2) → v0.7+

v0.6.1 -- Auxiliary Flowgraphs, Observability, Hardware Profiles

Phase B from the v0.6.0 plan: with TC&C already shipped in v0.6.0, this release adds operational depth — auxiliary flowgraphs (waterfall, PFD, ODT), per-station hardware profiles, dashboard observability, and the expanded decoder/modulator catalog.

Auxiliary flowgraphs (parallel consumers of the always-on RX IQ)

# Item Priority Effort Status Source
190 flowgraphs/src/aux_waterfall_v1.grc: ZMQ SUB IQ -> FFT -> waterfall PNG / web stream; aggregates the v0.6.0 #51 lightweight waterfall capture work P1 1 wk PROPOSED always-on-sdr
191 flowgraphs/src/aux_pfd_v1.grc: power flux density meter; periodic Prometheus export P2 1 wk PROPOSED always-on-sdr
192 flowgraphs/src/aux_odt_v1.grc: orbit determination tone collector; range-rate measurements published over MQTT for downstream OD P2 1 wk PROPOSED always-on-sdr
193 Register aux_*_v1 IDs in shared/flowgraphs.py; agent spawns them based on per-station config, not per-pass commands P1 2 hr PROPOSED always-on-sdr

Decoder + modulator catalog expansion

# Item Priority Effort Status Source
194 flowgraphs/src/decoder_afsk_v1.grc P1 1 wk PROPOSED always-on-sdr
195 flowgraphs/src/decoder_gmsk_v1.grc P1 1 wk PROPOSED always-on-sdr
196 flowgraphs/src/decoder_4fsk_v1.grc P2 1 wk PROPOSED always-on-sdr
197 flowgraphs/src/modulator_fm_v1.grc P1 1 wk PROPOSED tcnc
198 flowgraphs/src/modulator_gmsk_v1.grc P1 1 wk PROPOSED tcnc

Per-station hardware profiles + observability

# Item Priority Effort Status Source
199 Per-station config schema: max_parallel_decoders, max_parallel_modulators, enabled aux flowgraphs, SDR hardware profile (transceiver / rx+tx_pair); Alembic migration P0 4 hr PROPOSED always-on-sdr
200 Dashboard surface: live SDR status (RX center / sample rate / gain / temperature), decoder + modulator roster with per-flowgraph CPU and ZMQ queue depth from Prometheus P1 1 wk PROPOSED always-on-sdr
201 core/metrics.py registers new gauges/counters from the orchestrator: talos_flowgraph_running{kind,id}, talos_zmq_queue_depth{consumer}, talos_zmq_dropped_samples_total{consumer}, talos_decoder_cpu_seconds_total{norad_id}, talos_modulator_cpu_seconds_total{norad_id}, talos_uplinks_total{status}, talos_uplink_latency_seconds P1 4 hr PROPOSED always-on-sdr
66 Deploy Prometheus + Grafana monitoring stack (Grafana Cloud or self-hosted) P2 1 day PROPOSED v0.4.0 deferred
51 Lightweight waterfall capture — folded into #190 aux_waterfall_v1 P2 (done by #190) DEFERRED satnogs-review #4

YAMCS TC&C: validate yamcs-mqtt for production

# Item Priority Effort Status Source
202 Validate YAMCS MqttTcDataLink (yamcs-mqtt plugin) as the production TC ingest path; deprecate the HTTP fallback if reliable P1 1 wk PROPOSED tcnc
79 Configure YAMCS MqttTmDataLink to subscribe directly to TALOS MQTT frame topics (deferred from v0.5.2) P2 1 wk PROPOSED phasma-integration
80 Eliminate UDP stream router -- YAMCS reads MQTT natively via yamcs-mqtt plugin P2 4 hr PROPOSED phasma-integration

Cross-station coordination

# Item Priority Effort Status Source
112 Cross-station deduplication: when satellite S is visible from stations A and B, and station A is already tracking S, the Director should free station B to track a lower-priority satellite that only B can see — maximizing total coverage across the network P1 2 wk PROPOSED conops-review

v0.6.2 -- Scale, Cloud IQ, Default Pipeline Flip

Phase C from the v0.6.0 plan: validate the high-rate (USRP, 10+ MSPS) tier, add the cloud IQ network sink for off-station processing, and make the modular pipeline the default. Legacy subprocess path stays behind the flag for one more cycle, then is removed in v0.7.0.

Scale and cloud

# Item Priority Effort Status Source
203 flowgraphs/src/aux_net_iq_sink_v1.grc: VITA-49 or plain UDP network IQ sink for off-station consumers (cloud, second decoder cluster) P1 1 wk PROPOSED always-on-sdr
204 USRP B210 / X310 hardware support; benchmark and validate the 10+ MSPS tier on x86; document CPU/network sizing and per-decoder CPU budget P1 1 wk PROPOSED always-on-sdr
205 Flip default TALOS_PIPELINE=modular; legacy still available behind the flag P0 30 min PROPOSED always-on-sdr
206 Cloud streaming reference deployment: docker-compose example consuming network IQ off-station and running gr-satellites P2 1 wk PROPOSED always-on-sdr

Standards

# Item Priority Effort Status Source
48 CCSDS 502.0 (OMM) for standardized TLE interchange P1 2 wk PROPOSED exec-summary
49 CCSDS 503.0 (TDM) for tracking data export P2 2 wk PROPOSED exec-summary

Infrastructure (deferred from v0.6.0)

# Item Priority Effort Status Source
52 Server-side session store (Redis/DB-backed, enables instant revocation) P2 8 hr PROPOSED sec-review #12
53 PgBouncer connection pooling P2 4 hr PROPOSED ops-review #9
54 Fly Postgres HA (2-node failover) P2 2 hr PROPOSED ops-review #10
55 MQTT broker persistence volume on Fly P2 2 hr PROPOSED ops-review #11
56 Enable MQTT 5.0 on Mosquitto broker P2 1 day PROPOSED tech-roadmap
40 Performance regression gates (load test baseline comparison) P2 1 wk PROPOSED ops-review #7

v0.6.3 -- Visualization and UI Polish

Originally in v0.6.0, deferred to make room for SDR/TC&C. These are visualization/UX upgrades that don't compete with the modular pipeline work and can ship once the architecture has settled.

# Item Priority Effort Status Source
46 CesiumJS opt-in 3D globe alongside existing Leaflet 2D map P1 3 wk PROPOSED exec-summary
47 HTMX for non-real-time pages (stations, campaigns, settings) P2 2 wk PROPOSED tech-roadmap
207 Legacy subprocess flowgraph pipeline removal (after one full cycle of TALOS_PIPELINE=modular as default in v0.6.2) P1 4 hr PROPOSED always-on-sdr

v0.7+ -- Advanced Capabilities

Long-term items. Revisit priorities when v0.6 ships.

# Item Priority Effort Status Source
60 Regional Director sharding (partition by org for 500+ stations) P2 3 wk PROPOSED ops-review #8
61 Rolling-horizon re-optimization (reschedule as TLEs update) P3 3 wk PROPOSED tech-roadmap
62 Rust edge agent prototype (10x memory reduction on Pi) P3 4 wk PROPOSED tech-roadmap
64 Conjunction alerting via CCSDS 508.0 (CDM) P3 3 wk PROPOSED exec-summary
65 NATS evaluation (if MQTT 5.0 shared subscriptions insufficient) P3 2 wk PROPOSED tech-roadmap

Done (v0.3.0 and earlier)

# Item Version Source
-- Astro UXDS design system with unified dark theme v0.3.0 --
-- GitLab CI/CD pipeline (18 jobs, DAG, SAST, secret detection) v0.3.0 --
-- MkDocs Material documentation site on GitLab Pages v0.3.0 --
-- Campaign E2E tests (33 tests) + smoke tests v0.3.0 --
-- Organization model with RBAC (owner/operator/viewer) v0.2.0 --
-- Campaign system with per-station assignments v0.2.0 --
-- Org-scoped MQTT topics and API endpoints v0.2.0 --
-- Magic link email authentication v0.2.1 --
-- Alembic database migrations v0.2.1 --
-- SatNOGS API client with response caching v0.1.0 --
-- Hardware-in-the-loop testing (RPi + USRP B200mini + G-5500) v0.3.0 --
-- Fix N+1 queries in Director (eager loading) v0.2.1 arch-review #7
-- Ground track caching in Director v0.2.1 ops-review
-- Director MQTT heartbeat v0.2.0 ops-review #3

How to Use This Document

To approve an item: Change its status from PROPOSED to APPROVED.

To reprioritize: Move rows between version sections or change the Priority column.

To defer: Change status to DEFERRED and move to a later version.

To drop: Change status to DROPPED and add a note explaining why.

After implementation: Change status to DONE, add the version it shipped in, and move to the Done section.

Source references: - arch-review = docs/research/01-architecture-review.md - sec-review = docs/research/02-security-review.md - ops-review = docs/research/03-operations-scalability.md - satnogs-review = docs/research/04-satnogs-integration.md - exec-summary = docs/research/00-EXECUTIVE-SUMMARY.md - tech-roadmap = docs/research/05-technology-roadmap.md - phasma-integration = docs/research/06-phasma-integration.md - code-review = Independent code review, 2026-04-05 (32 findings: 4 critical, 7 high, 12 medium, 9 low) - conops-review = ConOps review, 2026-04-05 (reactive model validation; v0.5.1 scheduling dropped) - multi-sat = v0.5.5 multi-satellite campaign design notes - always-on-sdr = docs/research/08-always-on-sdr.md (always-on SDR + parallel flowgraph architecture) - tcnc = docs/research/09-tcnc-integration.md (TC&C uplink path through YAMCS)