Skip to content

System Overview

This page is the technical architecture reference for the demo repo. For the end-to-end data flow and walkthrough path, start with Project Overview. For per-component runtime behavior, see Infrastructure Layers.

The platform is a single monorepo provisioned entirely through Terraform
HashiCorp Terraform — provisions all platform infrastructure through five modules in `terraform/`.
. Five modules compose the stack; each owns shared resources in its layer rather than duplicating infrastructure definitions.

PathContents
terraform/

Root module and five submodules under terraform/modules/

services/ingest-sync/

Ingest-sync Lambda
Ingest-sync Lambda (`services/ingest-sync/`) — polls GeoNet on schedule and copies recent RINEX into the data lake (workaround for no source-bucket event subscription).
— GeoNet → data lake sync

services/query-api/

Query API
Query API Lambda (`services/query-api/`) — serves `/catalog` and `/query` from processed S3 keys and Parquet/JSON files.
— catalog and time-range queries

services/reprocess-api/

Reprocess API
Reprocess API Lambda (`services/reprocess-api/`) — accepts `/reprocess` jobs, writes DynamoDB records, enqueues Reprocess_Queue.
— job submission and status

Container image (external)

Processor Lambda
Processor Lambda container — runs PyTECGg calibration per SQS message; adopted when a Batch on Fargate parallel-execution quota increase was rejected. Image from `ghcr.io/platformfuzz/tec-processor-image` mirrored to ECR.
ghcr.io/platformfuzz/tec-processor-image, mirrored to ECR
Amazon Elastic Container Registry — stores the mirrored processor Lambda container image at deploy time.
at deploy

web/

Vite SPA — charts, IPP
Ionospheric Pierce Point — geographic location where the satellite signal intersects the ionospheric shell; plotted on the portal IPP map.
maps, reprocess panel

scripts/Deploy helpers (ECR mirror, Amplify zip upload)

Zip Lambdas use Python 3.14. The processor runs as a container image, not a zip deployment.

ModuleOwnsDepends on
ingest

S3
Amazon Simple Storage Service — data lake bucket for raw ingest and processed TEC output; S3 events trigger processing.
data lake,

ingest-sync Lambda
Ingest-sync Lambda (`services/ingest-sync/`) — polls GeoNet on schedule and copies recent RINEX into the data lake (workaround for no source-bucket event subscription).
ingest-scheduler

EventBridge Scheduler
AWS EventBridge Scheduler — triggers ingest-sync Lambda on a recurring UTC schedule (default: hourly).
, IAM
AWS Identity and Access Management — execution roles and policies for Lambdas, Scheduler, API Gateway, and S3.
invoke role

ingest (Lambda ARN/name)
processing

SQS
Amazon Simple Queue Service — buffers ingest and reprocess messages between S3/API and the processor Lambda.
/ DLQ
Dead-letter queue — SQS queue receiving messages that exceeded maxReceiveCount (5). CloudWatch alarms fire when visible count ≥ 1.
, S3→SQS notification, DynamoDB
Amazon DynamoDB — Jobs table tracks reprocessing job status by `job_id` (queued → processing → completed/failed).
Jobs, ECR
Amazon Elastic Container Registry — stores the mirrored processor Lambda container image at deploy time.
, processor Lambda

ingest (bucket)
presentation

Query
Query API Lambda (`services/query-api/`) — serves `/catalog` and `/query` from processed S3 keys and Parquet/JSON files.
/ reprocess API
Reprocess API Lambda (`services/reprocess-api/`) — accepts `/reprocess` jobs, writes DynamoDB records, enqueues Reprocess_Queue.
Lambdas, API Gateway
Amazon API Gateway — REST API exposing `/catalog`, `/query`, and `/reprocess`. Browser CORS is locked to the Amplify hostname; CLI clients call the API Gateway URL directly.
, Amplify

ingest (bucket read), processing (queues, jobs table)
observability

CloudWatch
Amazon CloudWatch — alarms, dashboard (`event-driven-platform`), and structured Lambda logs for observability.
alarms, SNS
Amazon Simple Notification Service — publishes CloudWatch alarm notifications; subscribe an email endpoint after deploy.
topic, dashboard

ingest, processing (and presentation when enabled)

Layer enable flags gate module creation:

VariableModule
enable_ingestingest
enable_processingprocessing
enable_presentationpresentation

Processing requires ingest. Presentation requires processing. The scheduler and observability modules follow the enabled layers they monitor.

Terraform module dependency diagram: root module fans out to ingest, ingest-scheduler, processing, presentation, and observability with cross-module output dependencies

The modules table above maps each resource to its owning module.

Processing must exist before automated ingest starts — otherwise raw RINEX
Receiver Independent Exchange Format — standard GNSS observation file format synced from GeoNet and calibrated by the processor.
files land in S3
Amazon Simple Storage Service — data lake bucket for raw ingest and processed TEC output; S3 events trigger processing.
with no consumer.

Recommended sequence:

  1. Processing — SQS, processor Lambda, DynamoDB Jobs table (creates ingest bucket dependencies)
  2. Ingest scheduler EventBridge Scheduler
    AWS EventBridge Scheduler — triggers ingest-sync Lambda on a recurring UTC schedule (default: hourly).
    once processing is healthy
  3. Full apply — presentation, observability, and reconciliation

Commands and Amplify options are in Staged Apply.

DecisionRationale
Terraform-only IaCSingle workflow; no drift between tools
Processor as Lambda containerSQS-triggered container image — see design notes below
External container imageBuilt independently; mirrored to ECR at deploy time
Python 3.14 zip LambdasIngest, query, and reprocess use managed runtime python3.14
DecisionRationale
EventBridge Scheduler
AWS EventBridge Scheduler — triggers ingest-sync Lambda on a recurring UTC schedule (default: hourly).
Recurring UTC triggers with dedicated invoke roles
SQS standard queuesProcessing order irrelevant; higher throughput than FIFO
Dual queues (ingest vs reprocess)Separate concurrency caps — operator jobs do not starve ingest
Visibility timeout 900sCovers processor execution and SQS retry window
DecisionRationale
Parquet
Columnar file format — primary processed output for efficient Query API range scans; JSON is a fallback when `pyarrow` is unavailable.
primary output
Efficient range scans; JSON fallback when pyarrow unavailable
Deterministic output keysIdempotent reprocessing via safe overwrites
Prefix-based catalogStations and dates from processed/tec/station=.../year=.../doy=... layout — not S3 Annotations
DynamoDB
Amazon DynamoDB — Jobs table tracks reprocessing job status by `job_id` (queued → processing → completed/failed).
for jobs
Low-latency job_id lookups; no joins required
NaN sanitization in Query APINon-finite floats become JSON null for browser compatibility

All Lambdas emit structured JSON logs with trace_id
UUID v4 correlation ID — propagated in SQS messages and structured logs for end-to-end request tracing.
, duration_ms, outcome, and error fields on failure. The observability module provisions alarms (DLQ depth, processor health, queue backlog, ingest-sync errors), an SNS topic, and the event-driven-platform dashboard.

Alarm names, subscription steps, and dashboard verification are in Alarms and Dashboard.