axiom_locus) that transforms raw ingestion tables into typed, tested, APRS-compliant marts. Marts are the layer downstream consumers — the dashboard heatmap, Explorer, scoring service, and API — read from. Staging and intermediate models are internal scaffolding; only marts are part of the public contract.
Locus uses dbt-core only. There is no dbt Cloud deployment. Local development materializes to DuckDB; production runs against the Locus Postgres database.
Project layers
Theaxiom_locus dbt project has three model layers with fixed materializations:
Staging models normalize column names, cast types, and pass through the APRS envelope. Marts apply the
aprs_envelope macro so every mart row carries record_id, source_uri, source_system, schema_version, normalization_version, acl_tier, occurred_at, ingested_at, and modified_at.
Sources
The dbt project reads from three source tables in thepublic schema:
mart_axiom_events
mart_axiom_events is the unified event surface for Locus. It is the same axiom_events signal the dashboard heatmap and Explorer read from. Each row is one real-world event with H3-cell attribution and full APRS envelope.
Configuration
- Incremental — only rows where
ingested_atis newer than the current watermark are added on each run. - Append-only —
event_id(anmd5of{source}:{record_id}) is unique and stable. Re-running dbt is idempotent. - Schema-evolution-safe — new columns added to the model are appended to the table without rebuilds.
Columns
Current event sources
Part A ships with two unioned sources:
Additional event sources land in Part B without changing the row shape —
on_schema_change='append_new_columns' lets new columns appear without a rebuild.
Querying
mart_axiom_events is a regular Postgres table — query it with any SQL client:
cell_scores to pull current scoring context for any event’s cell:
The aprs_envelope macro
Every mart row carries the APRS envelope via a single macro call, so envelope shape is identical across sources:
schema_version='aprs.locus/1.0.0', normalization_version='1.0.0', acl_tier='research'. Override per source when needed.
Local development
Install the dbt adapters into a Python environment:dev target writes to /tmp/axiom_locus.duckdb. Use any DuckDB client to inspect compiled marts locally.
Targets
The project ships three profiles:
Both Postgres targets materialize into the
public schema by default.
Continuous integration
A GitHub Actions workflow runsdbt deps && dbt build --profiles-dir profiles --target ci on every pull request that touches the dbt project or the workflow file itself. The build executes every staging, intermediate, and mart model plus the not_null / unique schema tests defined in models/marts/schema.yml. PRs that break a model or fail a test are blocked at merge.
Schema tests currently enforced on mart_axiom_events:
event_id—not_null,uniqueoccurred_at—not_nullh3_index—not_null
Roadmap
Three marts are planned. Part A shipsmart_axiom_events; the remaining two land in Part B without changes to the existing mart:
mart_cell_score_composite— replaces the in-app composite computation in the scorer service.mart_metro_overview— replaces the hand-rolled metro overview SQL in the web service and retires the legacyrefresh_metro_overviewpg_cron job.