TRAM Roadmap
Planned features and known issues. Items are assigned to a version once scope is confirmed; unconfirmed work lives in the backlog at the bottom.
v1.2.3 — SNMP Poll v3 Validation & ASN.1 Decode Hardening
- SNMP poll source — validate SNMPv3 USM on real-device GET and WALK; keep existing walk / yield_rows coverage green
- ASN.1 serializer — decode-path coverage and wording updated for explicit decode-only behavior
- SNMP trap source — deferred; blocked by push-source architecture gap (issue #11, resolved in v1.3.0)
- SNMP trap sink — deferred until a reachable real receiver/test target is available
v1.3.0 — Broadcast Streams & Push-Source Scaling
Full design:
docs/archive/v1.3.0-plan.mdSkips v1.2.4–v1.2.7 due to severity of push-source architecture gap (issue #11). Scope is intentionally limited to HTTP push sources (webhook,prometheus_rw). UDP sources (syslog,snmp_trap) and dynamic K8s service provisioning are v1.3.1.
A — workers: block & multi-dispatch (issue #11)
WorkersConfigPydantic model —count: 1|all(v1.3.0 runtime) +count:Nandlist:forward-compatible in schema; validators: mutual exclusion,count >= 1,listnon-empty + no duplicates; source-type defaults at validation (count: allfor push HTTP,count: 1for all others)BroadcastResultdataclass — accepted/rejected worker URLs,running | degraded | errorstatus- Linter rules L006–L010 — manager mode only; suppressed entirely in standalone; L007 fires on any multi-worker spec (
count > 1,count: all, orlist:) for poll/batch sources; no double-warning on queue sources (L009 for queue, L010 for non-queue);tram validatereadsTRAM_MODEfrom env or--mode WorkerPool._worker_ids— worker_id → URL mapping from health pollWorkerPool.resolve()— v1.3.0:count:1andcount:all;count:Nandlist:raiseNotImplementedErrorWorkerPool.multi_dispatch()— v1.3.0:count:1andcount:allpathsWorkerPool.dispatch()— thin wrapper aroundmulti_dispatch(count=1)for backwards compat- Controller:
_broadcast_placements,_active_placement_group,_make_placement_group_id();degradedpipeline status value
B — Worker Public Ingress Security
create_worker_ingress_app()— minimal FastAPI on:8767; webhooks only, no/agent/*- Coupled shutdown: either listener thread exits →
SIGTERMself → K8s restarts pod - Composite
GET /agent/healthreturnsok: falsewhen ingress thread is dead TRAM_WORKER_INGRESS_PORT(default8767); readiness probe stays on:8766(composite)- Worker StatefulSet:
ingresscontainerPort8767 middleware.pyimport isolation contract enforced bytest_worker_import_isolation.py
C — Unified Pipeline Stats & Load-Aware Dispatch
PipelineStatsdataclass (tram/agent/metrics.py) — thread-safe; records + bytes (bytes_in,bytes_out) + rolling error window; covers both stream and batch runs; replacesStreamMetricsActiveRun.stats: PipelineStatsfor all run typesexecutor.stream_run()andexecutor.batch_run()—statsparam;bytes_infrom source read,bytes_outfrom sink write; batch emitsis_final: truestats report immediately before_post_run_completeRunCompletePayloadgainsbytes_in: int = 0andbytes_out: int = 0;on_worker_run_completewrites them torun_history— this is the authoritative final total path- Worker stats reporting thread (
_stats_loop) — periodic reportsis_final: false; batch completion emitsis_final: true; posts everyTRAM_STATS_INTERVAL(default 30s; replacesTRAM_HEARTBEAT_INTERVAL) POST /api/internal/pipeline-stats—is_final: true→StatsStore.remove(run_id)(load eviction only, not the bytes persistence path); otherwiseStatsStore.update()StatsStore(tram/agent/stats_store.py) — keyed byrun_id; staleness contract:get_by_run_id()stale-aware (reconciler + placement API per-slot);for_worker(),for_pipeline(),all_active()exclude stale (load-scoring and aggregate views); explicitremove(run_id); replacesHeartbeatStoreon_worker_run_complete— writesbytes_in/bytes_outtorun_history; callsStatsStore.remove(run_id)as fallback for crashed runsWorkerPool.load_score()— resolvesworker_url → worker_idvia_worker_idsbeforeStatsStore.for_worker(); fallback toactive_runs × 1 MB proxy
D — PlacementReconciler
PlacementReconcilerbackground thread (tram/agent/reconciler.py) — runs everymin(TRAM_STATS_INTERVAL, 10)s; owns all stale detection, re-dispatch, and reconciling-window timeout; stats endpoint is write-only- Stale slot detection —
StatsStore.get_by_run_id(slot.current_run_id); age >3 × interval→ mark stale → re-dispatch same worker; updatesslot.current_run_idin slots_json + DB (count:all only in v1.3.0) - Reconciling-window timeout — after
2 × interval: matched →running; partial →degraded; none → re-dispatch - Unit tests: stale → re-dispatch, reconciling timeout, partial recovery → degraded
E — Manager Restart Reconciliation
- DB table
broadcast_placements—slots_jsonentries carryrun_id_prefix(immutable) andcurrent_run_id(mutable, updated on each re-dispatch) TramDB:save_broadcast_placement(),get_active_broadcast_placements(),update_broadcast_placement_status(),update_slot_run_id()_boot_load()seeds_broadcast_placementsfrom DB; setsreconcilingstatus- Stats receiver matches incoming stats by
run_id_prefix→ resolves reconciling slot; updatescurrent_run_id - Unit tests: cold restart, partial recovery → degraded, unmatched → re-dispatch;
current_run_idupdated after re-dispatch
F — Placement API
GET /api/pipelines/{name}/placement— iteratesslots_jsonas source of truth; per-slot stats viaStatsStore.get_by_run_id(slot.current_run_id); per-sec fields zeroed for stale; stale slots visibleGET /api/cluster/streams— aggregate totals fromStatsStore.for_pipeline()(non-stale) + group status/counts frombroadcast_placements
G — Manager StatefulSet
- Replace
manager-deployment.yamlwithmanager-statefulset.yaml+volumeClaimTemplates manager-headless-service.yamlfor stable pod DNS- Remove separate manager PVC from
pvc.yaml;existingClaimmigration path invalues.yaml helm/NOTES.txtupgrade migration note
H — Alert cooldown on confirmed delivery only (issue #3)
_fire_webhook()/_fire_email()returnTrue/False_set_cooldown()called only onTrue- Tests: HTTP 500, connection error, SMTP failure → no cooldown
v1.3.1 — Placement Completion & Targeted Backend Follow-ups
Depends on v1.3.0 TCP path being validated end-to-end.
workers.count: Nruntime — logical slot model (slot number ≠ worker assignment);PlacementReconcilerspare-worker gap fill; slot reassignment on failover;WorkerPool.resolve()+multi_dispatch()N-worker pathsworkers.list: [...]runtime — named-worker placement; per-slot pinned re-dispatch on recovery- Dynamic K8s Service provisioning —
kubernetes:pipeline block; manager creates/deletes NodePort Services via K8s API; RBAC Role + RoleBinding;tram[k8s]optional extra source_stem/source_suffixfilename tokens (issue #9) — added to all 6 file-based sinks- Migrate SNMP to pysnmp 7.x (issue #10) — update import paths;
pysmimigration
v1.3.2 — Metrics/Stats Parity & UDP Multi-Worker Streams
Follows v1.3.1 runtime stabilization. Scope is observability model cleanup plus UDP push-source validation.
- Standalone live stats parity — feed local active runs into
StatsStoreso standalone exposes the same live stream/placement-style stats model as manager mode instead of only history + process-local Prometheus metrics - Manager operational metrics — add manager-side Prometheus series for dispatch/re-dispatch attempts, placement status counts, reconcile actions, worker health, and callback receipt (not direct failure counts — failures are inferred from the dispatch/receipt delta); document that
/metricsis process-local and worker scraping is still required for cluster-wide execution metrics - UDP multi-worker streams —
syslogandsnmp_trapsources; per-pipeline NodePort Service viakubernetes: enabled: true(shared selector forcount: all, manual Endpoints forcount: N/workers.list);kubernetes: enabled: truerequired in manager mode - ASN.1 structured decode flattening — BER multi-record split for concatenated files; ordered
message_classesfallback; genericjson_flattenandhex_decodetransforms with configurable ASN.1-oriented options and docs - CDR record shaping — dotted-path support on
unnest,explode,drop,rename,value_map,cast; sharedpath_utilshelpers;select_from_list,coalesce_fields, andprojectprimitives; conditionaldrop; and narrow single-segment wildcard support onhex_decode/json_flattenfor explicit row-semantic CDR pipelines (LTE/SGW/PGW) - Batch reconciliation — dedicated
BatchReconciler, active batch lease tracking, worker-scan adoption after manager restart, and lost-run synthesis through the existing run-finalization path - Incremental large-record batch processing — pipeline-level
record_chunk_size, serializerparse_chunks(...)hook, and incremental ASN.1 BER decode for bounded-memory serial batch processing - Safe staged batch file output — record-safe serial batch sinks (
csv,ndjson) finalize output per source file, delete staged temp artifacts on failure, and support optionalpost_batch_cleanupheap trimming per pipeline
v1.3.3 — UI Revalidation & Backend Contract Sync
Follows v1.3.2 backend work. Scope is intentionally UI-heavy: revalidate every page against the shipped API and remove contract drift.
- Full UI/backend contract audit — verify every page, action, filter, export, and status badge against current API fields and route behavior
- Cluster and placement UX — finalize stream placement, slot health, stale/degraded/reconciling states, and manager-vs-standalone presentation
- Metrics/stats UX sync — align dashboard, cluster streams, run history, and Prometheus guidance with the actual manager/worker vs standalone stats model
- Pipeline detail completeness — schedule, alert, run-history, versioning, placement, and error-policy views all reflect current backend fields without fallback mismatches
- Plugin/templates/settings pages revalidation — remove stale assumptions, ensure live API-backed rendering, and verify empty/error states
- Responsive/browser pass — light/dark mode, mobile/tablet layout, console-clean build, and page-level smoke checks
Backlog (unversioned)
Connector Fixes (deferred from v1.2.4–v1.2.7)
- Kafka source — reconnect, offset commit, consumer group edge cases
- Kafka sink — producer error handling, retry, serializer integration
- OpenSearch sink — bulk write, index template, auth, retry on 429
- ClickHouse source/sink — query execution, batch insert, type coercion
- InfluxDB source/sink — line protocol, bucket/org resolution, token auth
- REST source/sink — auth types, pagination, retry, SSL verify
- gNMI source — subscription modes, path encoding, TLS
- SFTP source/sink — file glob, move-after-read, skip_processed, key auth
- FTP source/sink — passive mode, directory listing, file write
- S3 source/sink — bucket/prefix, multipart upload, credential chain
- MQTT source/sink — QoS levels, reconnect, topic wildcards
- AMQP source/sink — exchange/queue binding, ack/nack, prefetch
- NATS source/sink — subject routing, JetStream, reconnect
Operations & Observability
- Pipeline cloning — copy a pipeline with a name prompt in the UI
- Scheduled alert evaluation — cron-based alert checks independent of pipeline runs
- Dead-letter queue viewer — browse and replay DLQ records via the UI
- Per-sink record counts — run metrics broken down per sink
- Pipeline dependency graph — visualize pipeline chains when A feeds B
- Bulk actions — start/stop/delete multiple pipelines from the list view
- Live log streaming — WebSocket tail of log output for running stream pipelines
- Node health detail page — per-worker pipeline assignments and load in manager mode
Security & Multi-tenancy
- Role-based access — read-only vs admin token scopes (viewer/operator/admin)
- Per-pipeline API key scoping — restrict a key to specific pipelines
- Key upload API —
POST /api/keys/upload/GET /api/keys/DELETE /api/keys/<name> - Audit log — record who triggered, modified, or deleted pipelines
New Connectors & Serializers
- SMTP sink — outbound email delivery (alerts, reports)
- gRPC sink — generic gRPC unary call sink
- Syslog sink — forward records to remote syslog (RFC 5424)
- Kafka schema registry — full Avro + Protobuf with Confluent wire format
- PM-XML source — ingest 3GPP TS 32.435 PM XML files natively
Infrastructure
- Manager HA — standby manager with DB-backed leader election
- Graceful worker drain —
POST /api/workers/{id}/drain; Helm pre-stop hook - Coverage target increase — CI threshold raised to 75%; current coverage ~80%
Released
| Version | Theme |
|---|---|
| v1.3.0 | Broadcast streams; push-source scaling; placement reconciliation; manager StatefulSet |
| v1.2.3 | SNMP Poll v3 validation; ASN.1 decode hardening |
| v1.2.2 | Stability & polish; CI fixes; docs alignment |
| v1.2.1 | Worker callback chain; run metrics propagation; dashboard UX |
| v1.2.0 | Manager + Worker cluster architecture |
| v1.1.4 | PipelineController; melt transform; SNMP fixes |
| v1.1.1 | YAML diff modal; enrich missing file; template fixes |
| v1.1.0 | Pipeline Wizard; Live Metrics; Alert Rules UI; AI Assist; Connector Test |
| v1.0.8/9 | Browser auth; PostgreSQL subchart; shared RWX storage |
| v1.0.7 | Bootstrap 5 web UI; dedicated K8s UI service |
| v1.0.6 | ndjson serializer; per-sink serializer_out |
| v1.0.4 | Schema registry; ClickHouse connector; pipeline update API |
| v1.0.3 | MIB management API; schema management API; standard MIBs in image |
| v1.0.2 | SNMPv3 USM (auth/priv) |
| v1.0.1 | SNMP poll yield_rows; dynamic version |
| v1.0.0 | API key auth; rate limiting; TLS; per-sink retry; circuit breaker; OTel |
| v0.9.0 | Thread workers; batch_size; DLQ; CORBA source; processed-file tracking |