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.
Monorepo layout
Section titled “Monorepo layout”| Path | Contents |
|---|---|
terraform/ | Root module and five submodules under |
services/ingest-sync/ | Ingest-sync Lambda |
services/query-api/ | Query API |
services/reprocess-api/ | Reprocess API |
| Container image (external) | Processor Lambda |
web/ | Vite SPA — charts, IPP |
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.
Five Terraform modules
Section titled “Five Terraform modules”| Module | Owns | Depends on |
|---|---|---|
ingest | S3 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 | ingest (Lambda ARN/name) |
processing | SQS | ingest (bucket) |
presentation | Query | ingest (bucket read), processing (queues, jobs table) |
observability | CloudWatch | ingest, processing (and presentation when enabled) |
Layer enable flags gate module creation:
| Variable | Module |
|---|---|
enable_ingest | ingest |
enable_processing | processing |
enable_presentation | presentation |
Processing requires ingest. Presentation requires processing. The scheduler and observability modules follow the enabled layers they monitor.
The modules table above maps each resource to its owning module.
Staged apply order
Section titled “Staged apply order”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:
- Processing — SQS, processor Lambda, DynamoDB Jobs table (creates ingest bucket dependencies)
- Ingest scheduler — EventBridge Scheduler
AWS EventBridge Scheduler — triggers ingest-sync Lambda on a recurring UTC schedule (default: hourly). once processing is healthy - Full apply — presentation, observability, and reconciliation
Commands and Amplify options are in Staged Apply.
Key design decisions
Section titled “Key design decisions”Provisioning and compute
Section titled “Provisioning and compute”| Decision | Rationale |
|---|---|
| Terraform-only IaC | Single workflow; no drift between tools |
| Processor as Lambda container | SQS-triggered container image — see design notes below |
| External container image | Built independently; mirrored to ECR at deploy time |
| Python 3.14 zip Lambdas | Ingest, query, and reprocess use managed runtime python3.14 |
Eventing and queues
Section titled “Eventing and queues”| Decision | Rationale |
|---|---|
| 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 queues | Processing order irrelevant; higher throughput than FIFO |
| Dual queues (ingest vs reprocess) | Separate concurrency caps — operator jobs do not starve ingest |
| Visibility timeout 900s | Covers processor execution and SQS retry window |
Data layout and API
Section titled “Data layout and API”| Decision | Rationale |
|---|---|
| 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 keys | Idempotent reprocessing via safe overwrites |
| Prefix-based catalog | Stations 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 API | Non-finite floats become JSON null for browser compatibility |
Observability
Section titled “Observability”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.
Related pages
Section titled “Related pages”- Project Overview — what you build and walkthrough path
- Infrastructure Layers — ingest, processing, presentation runtime detail
- Staged Apply — recommended Terraform apply order
- Terraform Variables and Terraform Outputs — configuration reference
- Data Contract — SQS, DynamoDB, S3 keys, and API schemas