Skip to content

Instantly share code, notes, and snippets.

@crockpotveggies
Created September 24, 2026 05:20
Show Gist options
  • Select an option

  • Save crockpotveggies/bc1747a25161445fd6eca0d9979fe1d4 to your computer and use it in GitHub Desktop.

Select an option

Save crockpotveggies/bc1747a25161445fd6eca0d9979fe1d4 to your computer and use it in GitHub Desktop.
ATsense: ATproto-style Federation for Open Sensor Networks (Draft)

ATsense: ATproto-style Federation for Open Sensor Networks (Draft)

Sep 23, 2026 · @Justin

Summary and goals

ATsense applies ATproto's architecture to every kind of open sensor: each station owns a portable, signed repo describing what it measures, and open relays, feeds and labelers make seismometers, air monitors, weather stations, cameras and microphones discoverable, streamable and searchable through one protocol.

Today each community runs its own silo. PurpleAir, Sensor.Community, CWOP, Safecast, BirdWeather, OpenSky and the FDSN each have their own identity scheme, API, archive and map. The data shapes differ, but the problems are the same:

  • Identity is bound to a platform. A sensor's identity lives in one company's or project's database, and leaves with it.
  • Participation is gated by the aggregator. A backyard station needs registration, API keys or a vendor's cloud to be counted.
  • No cross-domain discovery. You cannot ask for "every sensor of any kind within 5 km of this wildfire" and subscribe to all of them live.
  • Provenance rests on trust in the aggregator, not on signatures and hashes.
  • Derived data is siloed. Smoke detections, bird IDs, earthquake picks and QC flags live in separate systems and cannot reference each other.

Design goals

  1. One small core vocabulary for systems, deployments, datastreams and observations, with domain modules on top.
  2. Every data shape (scalar readings, waveforms, images, audio, detections, mobile tracks) fits the same record-plus-blob pattern.
  3. Any operator, from a national network to one person with a kit, can publish with a portable identity.
  4. Live data at the latency each domain needs, and verifiable historical access from any mirror.
  5. Open, pluggable discovery that works across domains.
  6. Bridges so existing platforms and tools work on day one.

Sensor landscape

Nine representative open networks span five data shapes, and each one exposes a requirement the seismic-only draft missed.

Network Domain Scale (as reported) Cadence and shape Requirement it adds
PurpleAir Air quality Global, consumer hardware Scalar readings every 2 min; dual A/B particle counters; indoor/outdoor flag Redundant sub-sensors, siting context, private sensors, paid central API (pricing)
Sensor.Community Air, noise, weather >14,000 sensors in >70 countries (2021); ~12,000 active in 82 countries (2024) Scalar readings every 2.5 min from DIY kits Heterogeneous hardware: sensor model must be first-class
OpenAQ Air quality aggregator Reference monitors plus low-cost sensors worldwide Raw, hourly and daily aggregates Provider is not owner; licenses change over time; mobile vs stationary; QC flags
Raspberry Shake (FDSN AM) Seismic, infrasound Thousands of stations since 2016 (data center) Continuous waveforms, 50–100 sps High-rate waveforms; already bridged to FDSN; users already "follow" stations
CWOP Weather 3,000–4,000 routinely reporting (2009) Scalar readings every 5–15 min via APRS-IS to NOAA MADIS; manual snow reports via CoCoRaHS Human-entered observations; QC results attached as metadata
Safecast Radiation, air >120 million observations by 2020, CC0 bGeigie Nano logs every 5 s with GPS from cars and bikes Mobile platforms: location per observation, not per station
ALERTCalifornia Wildfire cameras >1,200 cameras (2026); ~8 million images/day (2023) Frames every 10–15 s from rotating PTZ cameras; AI smoke detections Images with a changing view per frame; derived detections; very high blob volume
BirdWeather Bioacoustics >1,000 active stations On-device BirdNET detections plus short audio clips; fixed, mobile, livestream and retrieved recorders Late-arriving batch uploads; edge ML output as primary data; audio privacy
OpenSky Network Aircraft (ADS-B, Mode S) >7,000 receivers; >40 trillion Mode S replies (2025) Messages received from aircraft broadcasting up to 2 Hz (ESSD) Observed object is far from the sensor; many receivers hear the same target

The closest existing standard is OGC API – Connected Systems, whose Part 2 was approved in June 2025. It models Systems, Deployments, Procedures, Sampling Features, Datastreams and Observations on the W3C SOSA/SSN ontology, and supports live and historical access over HTTP, WebSocket and MQTT. ATsense should adopt that vocabulary rather than invent one: Connected Systems standardizes the API a server exposes, and ATsense adds the identity, signing, federation and discovery layers it lacks.

Data shapes

Six data shapes cover every network above; the lexicon handles them all with one segment record whose payload is either inline values or blob chunks.

Shape Examples Typical rate Payload in a segment
Scalar series PurpleAir PM2.5, weather, fixed radiation 1 per s to 1 per 15 min Inline columnar arrays (time offsets + values)
Waveform Seismic, infrasound, magnetometer, raw acoustic 10–48,000 samples/s Blob chunks (miniSEED, FLAC, or generic compressed array)
Media frames Wildfire cameras, all-sky cameras, timelapse 1 frame per 10 s to 30 fps Blob per frame or per video segment, with per-frame view metadata
Track Safecast drives, drones, buoys, balloons 0.2–10 per s Inline arrays with a position per sample
Detection Smoke alert, bird ID, lightning stroke, earthquake pick, decoded aircraft position Sparse One record per detection, referencing the evidence segment
Human observation CoCoRaHS snow depth, manual gauge reading, field note Sparse One record per observation

Cross-cutting requirements

  • Feature of interest vs sensor location. An ADS-B receiver hears aircraft 300 km away; a camera views a ridge 20 km away. Records separate where the sensor is from what it observes, as SOSA/SSN does.
  • Observed properties and units. Properties use a shared vocabulary (CF standard names or QUDT), and units use UCUM codes, so "PM2.5 in µg/m³" means the same thing from every vendor.
  • Raw vs derived vs aggregated. A segment says whether values are raw, corrected (e.g. EPA-corrected PurpleAir) or averaged, and links to its inputs.
  • Late and batch arrival. Recorders retrieved months later, or stations that were offline, publish segments with past timestamps. Observation time and publish time are separate fields.
  • Redundant sub-sensors. PurpleAir's A and B channels are two datastreams on one system, so QC can compare them.
  • Privacy class. Audio and images can capture people. Each datastream declares a privacy class that hosts and relays must respect.

Architecture

The core split holds for every domain: small signed records (descriptions, manifests, detections) travel through repos and the firehose, while bulk payloads (waveform chunks, images, audio) travel as content-addressed blobs and live streams that records point to.

ATproto concept ATsense role Example
DID + handle Identity for a system (sensor or station), an operator, or a fleet did:plc:…, handle pa-1234.purpleair.sense.net
PDS Data host: holds repos, serves blobs and live streams A university server, a vendor cloud, a home Raspberry Pi
Repo Signed tree of a system's records System description, deployments, datastreams, segment manifests
Blob Bulk payload addressed by CID One minute of seismic data, one camera frame, a 3 s audio clip
Relay / firehose Global stream of commits "New segment", "new detection", "deployment moved"
AppView Aggregating service Air quality map, earthquake catalog, smoke detector, bird tracker
Feed generator Virtual network across domains "Every sensor of any kind within 5 km of the Park Fire"
Labeler QC and trust pm-ab-disagree, indoor-sited, verified-reference-monitor
Lexicon Shared schemas sense.core.* plus sense.seismic.*, sense.air.*, sense.imagery.*
flowchart LR
  S[Sensors<br/>any domain] -->|readings, frames| H[Data host PDS]
  H -->|signed commits| R[Relay firehose]
  H -->|live streams| L[Live subscribers]
  H -->|blobs by CID| A[Archives / mirrors]
  R --> V[AppViews]
  R --> F[Feed generators]
  R --> B[Labelers]
  V -->|detections, QC| H2[Their own repos]
Loading

Nothing in the relay or host layer knows about domains. Domain logic lives in lexicon modules and in the AppViews, feeds and labelers built on them.

Identity

Every system (a sensor, station or camera) gets its own DID, and operators and fleets get DIDs too; the system's DID is what data, detections and citations point to, so it survives changes of host, owner or network.

  • System DID. Resolves to the current data host and signing keys. Rotation keys stay with the owner, so neither a host nor a vendor cloud can capture the system.
  • Operator DID. A person or organization. Owns systems via sense.core.operates records, verified two-way as with ATproto domain handles.
  • Fleet DID. A network, project or vendor fleet (FDSN network AM, Sensor.Community, a city's air network). Publishes membership records; a system can belong to several fleets at once.
  • Existing identifiers. FDSN codes, PurpleAir sensor indexes, CWOP IDs and OpenAQ location IDs are stored as externalIds on the system record. Registry labelers (FDSN, CWOP) mark which DID legitimately holds each code.
  • Device keys. Firmware can hold a delegated key that signs segment manifests, revocable without touching history. Vendors can ship devices that generate their DID on first boot and let the owner claim it.
  • Mobile and ephemeral systems. A Safecast unit keeps one DID across drives; each drive is a deployment. A drone or balloon flight is a deployment of a persistent system.
  • Human observers. A person's ordinary ATproto account can publish human observations directly, so a CoCoRaHS volunteer or birder needs no separate system identity.

Lexicons

A small sense.core.* namespace, mapped one-to-one onto OGC Connected Systems concepts, carries everything relays, feeds and archives need; domain modules plug in through open unions and never change the core.

Core namespace

Lexicon Connected Systems equivalent Purpose
sense.core.system System What the device is: type, maker, model, external IDs, default license and privacy class
sense.core.deployment Deployment Where and when: location or "mobile", siting (indoor/outdoor, height), feature of interest
sense.core.datastream DataStream One observed property: unit, shape, nominal rate, encoding, live stream URI, domain extension
sense.core.segment Batch of Observations A time window of data: inline values or blob chunks, processing level, gaps
sense.core.detection Observation (classification result) A derived event pointing to its evidence segments
sense.core.observation Observation A single human-entered reading
sense.core.annotation none Free-form note on a time window of any datastream
sense.fleet.member none Fleet membership, confirmed by a matching system record
sense.feed.generator none Declares a feed service
sense.graph.follow, sense.graph.endorse none Social layer

Domain modules (first set)

Module Extends Adds
sense.seismic.channel datastream FDSN NET.STA.LOC.CHA, orientation, instrument response ref
sense.seismic.pick, sense.seismic.origin detection Phase, polarity, hypocenter, magnitude
sense.air.particle datastream Sensor channel (A/B), size bin, correction applied
sense.weather.station system Siting class, exposure, shielding
sense.imagery.frameView segment Per-frame pan, tilt, zoom, heading, field of view
sense.imagery.smoke detection Bounding box, estimated bearing and range
sense.acoustic.species detection Taxon ID, clip reference, model and threshold
sense.radiation.doseRate datastream Detector tube type, energy calibration
sense.aviation.target detection ICAO24 address, target position and altitude, receivers that heard it

Numbers without floats

ATproto Lexicon has no float type. ATsense uses one fixed-point rule everywhere: values are integers plus a scale exponent, so value × 10^scale is the reading. PM2.5 of 12.4 µg/m³ is 124 with scale: -1; a latitude is 37774900 with scale: -6.

sense.core.datastream

{
  "lexicon": 1,
  "id": "sense.core.datastream",
  "defs": {
    "main": {
      "type": "record",
      "key": "any",
      "record": {
        "type": "object",
        "required": ["system", "observedProperty", "unit", "shape"],
        "properties": {
          "system": { "type": "string", "format": "did" },
          "name": { "type": "string", "maxLength": 200 },
          "observedProperty": { "type": "string", "format": "uri", "description": "CF standard name or QUDT quantity kind URI" },
          "unit": { "type": "string", "description": "UCUM code, e.g. ug/m3, m/s, Cel" },
          "shape": { "type": "string", "knownValues": ["scalar", "waveform", "media", "track"] },
          "nominalRate": { "type": "ref", "ref": "#scaledInt" },
          "encoding": { "type": "string", "knownValues": ["inline", "miniseed3", "flac", "jpeg", "mp4", "parquet"] },
          "liveStream": { "type": "string", "format": "uri" },
          "privacyClass": { "type": "string", "knownValues": ["public", "public-redacted", "restricted"] },
          "deployment": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
          "ext": {
            "type": "union",
            "refs": ["sense.seismic.channel", "sense.air.particle", "sense.radiation.doseRate"]
          }
        }
      }
    },
    "scaledInt": {
      "type": "object",
      "required": ["value", "scale"],
      "properties": {
        "value": { "type": "integer" },
        "scale": { "type": "integer", "minimum": -12, "maximum": 12 }
      }
    }
  }
}

The ext union is open, so new domain modules can appear without a core release; clients ignore extensions they do not know.

sense.core.segment

Record key convention: <datastream key>.<window start>, e.g. pm25-a.2026-09-23T14.

{
  "lexicon": 1,
  "id": "sense.core.segment",
  "defs": {
    "main": {
      "type": "record",
      "key": "any",
      "record": {
        "type": "object",
        "required": ["datastream", "start", "end", "level", "payload"],
        "properties": {
          "datastream": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
          "deployment": { "type": "ref", "ref": "com.atproto.repo.strongRef" },
          "start": { "type": "string", "format": "datetime" },
          "end": { "type": "string", "format": "datetime" },
          "level": { "type": "string", "knownValues": ["raw", "corrected", "aggregated"] },
          "aggregation": { "type": "string", "knownValues": ["mean", "median", "max", "min", "sum"] },
          "derivedFrom": { "type": "array", "items": { "type": "ref", "ref": "com.atproto.repo.strongRef" } },
          "payload": { "type": "union", "refs": ["#inlineValues", "#chunks"], "closed": true },
          "gaps": { "type": "array", "items": { "type": "ref", "ref": "#timeRange" } },
          "clockQuality": { "type": "integer", "minimum": 0, "maximum": 100 },
          "ext": { "type": "union", "refs": ["sense.imagery.frameView"] }
        }
      }
    },
    "inlineValues": {
      "type": "object",
      "description": "Columnar arrays for scalar and track data",
      "required": ["offsetsMs", "values", "scale"],
      "properties": {
        "offsetsMs": { "type": "array", "items": { "type": "integer" }, "maxLength": 4000 },
        "values": { "type": "array", "items": { "type": "integer" }, "maxLength": 4000 },
        "scale": { "type": "integer" },
        "latMicroDeg": { "type": "array", "items": { "type": "integer" } },
        "lonMicroDeg": { "type": "array", "items": { "type": "integer" } }
      }
    },
    "chunks": {
      "type": "object",
      "required": ["items"],
      "properties": {
        "items": { "type": "array", "items": { "type": "ref", "ref": "#chunk" } }
      }
    },
    "chunk": {
      "type": "object",
      "required": ["blob", "start", "end"],
      "properties": {
        "blob": { "type": "blob" },
        "start": { "type": "string", "format": "datetime" },
        "end": { "type": "string", "format": "datetime" }
      }
    },
    "timeRange": {
      "type": "object",
      "required": ["start", "end"],
      "properties": {
        "start": { "type": "string", "format": "datetime" },
        "end": { "type": "string", "format": "datetime" }
      }
    }
  }
}

A PurpleAir hour is one segment with 30 inline values per channel. A seismic hour is one segment listing 60 miniSEED chunks. A Safecast drive is one segment with inline dose rates plus latitude and longitude arrays. Each record's commit time is its publish time, so late uploads are visible as such.

sense.core.detection

{
  "lexicon": 1,
  "id": "sense.core.detection",
  "defs": {
    "main": {
      "type": "record",
      "key": "tid",
      "record": {
        "type": "object",
        "required": ["evidence", "time", "class", "method"],
        "properties": {
          "evidence": { "type": "array", "items": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, "minLength": 1 },
          "time": { "type": "string", "format": "datetime" },
          "class": { "type": "string", "description": "Namespaced label, e.g. seismic:P, smoke, taxon:gbif:2490384" },
          "confidencePct": { "type": "integer", "minimum": 0, "maximum": 100 },
          "method": { "type": "string", "knownValues": ["manual", "automatic"] },
          "model": { "type": "string" },
          "ext": {
            "type": "union",
            "refs": ["sense.seismic.pick", "sense.imagery.smoke", "sense.acoustic.species", "sense.aviation.target"]
          }
        }
      }
    }
  }
}

Because evidence accepts several segments from several systems, one detection can cite a camera frame, a nearby PM2.5 spike and a weather station's wind shift together. That cross-domain link is the thing no existing network can express.

Real-time streaming

Each data shape gets its own commit cadence and live transport, but all share one rule: the firehose carries signed records only, and bulk payloads move over live streams and blob fetches.

Shape Live stream Commit cadence to firehose Fast path
Scalar series Each reading pushed as it is taken Segment appended every 10 min Not needed
Waveform Packets every 0.5–1 s Chunk appended every 60 s < 1 s for earthquake early warning
Media frames Latest frame or video segment URI Frames batched every 60 s Latest-frame endpoint for responders
Track Positions pushed as taken Segment appended every 1–10 min Not needed
Detection Not applicable Committed immediately Detections are small, so the firehose is the fast path

Signing

Live packets carry a sequence number and the previous packet's hash. When the host commits the segment update, it signs over the chunk CIDs or inline values. Subscribers verify their buffered live data against the commit, so tampering shows up within one commit interval.

Firehose volume (illustrative)

Source Assumed count Commits per second
Air and weather datastreams 300,000 ~500
Seismic channels 50,000 ~830
Cameras 5,000 ~80
Detections varies tens to hundreds

About 1,500 commits per second in total, in the same range as Bluesky's firehose. Blobs stay off it: at ALERTCalifornia's ~8 million images a day and an assumed 500 KB per image, one camera network alone produces about 370 Mbit/s.

Subscribing

  • Per datastream: connect to its liveStream URI.
  • Per feed: a live-stream relay resolves a feed to its datastreams and multiplexes them over one connection, tracking changes as systems join or leave the feed.
  • Replay: every subscription takes a cursor, so clients resume after disconnects.

Historical access

History for every shape is the same: signed segment records are the index, and content-addressed blobs are the bulk, so any mirror can serve data and any client can verify it.

  • Segments are the index. One segment per datastream per hour gives a compact, signed map of coverage and gaps. Scalar data is fully contained in its segments, so an air-quality archive is just the repos.
  • Blobs are immutable. Corrections produce new segments with level: corrected and derivedFrom pointing to the raw ones; raw data is never overwritten.
  • Tiered retention. Hosts can drop old raw imagery or audio after publishing aggregated or detection records, as long as an archive has pinned it. A segment whose blobs are no longer hosted anywhere is marked by an archive labeler, not silently lost.
  • Domain archives. EarthScope for seismic, OpenAQ for air quality, NOAA for weather: each becomes a full-history archive node for its domain, serving the ATproto sync API plus blob-by-CID.
  • Backfill and late data. Archives replay repos from cursors. Late uploads (a retrieved acoustic recorder, an offline station) land as normal commits with past observation times.
sequenceDiagram
  participant C as Client
  participant I as Index AppView
  participant M as Mirror or archive
  C->>I: area + time + properties
  I-->>C: datastreams + segments
  C->>M: fetch blobs by CID
  M-->>C: chunks, frames, clips
  C->>C: verify hashes + signatures
Loading

A query such as "PM2.5, wind and camera frames within 10 km of this fire, 6 hours before ignition" returns scalar segments directly from the index and fetches only the frames the client asks for.

Discovery

Discovery works across domains because feeds, labelers and follows all operate on core records; a feed can mix seismometers, cameras and air monitors as easily as it lists one kind.

Feeds as virtual networks

A feed service returns datastreams, detections or segments on request, declared by a sense.feed.generator record. Its core call, sense.feed.getSkeleton, returns record refs plus a cursor, like Bluesky's getFeedSkeleton.

  • Incident: every sensor of any kind within 10 km of an active wildfire, updating as the perimeter moves.
  • Property: all outdoor PM2.5 datastreams in Los Angeles County passing the A/B agreement check.
  • Event: every seismic channel and every building-mounted camera that recorded a given earthquake.
  • Species: acoustic stations that detected a given owl this week.
  • Community: all sensors in a school district, all Raspberry Shakes in schools.

A feed plus a time range plus repo revisions is a reproducible dataset, citable with a DOI.

Labelers as QC and trust

Label Applies to Likely labeler
pm-ab-disagree Air datastream Air QC service comparing PurpleAir A and B channels
indoor-sited Deployment Community or vendor labeler
verified-reference-monitor System Environmental agency
clock-drift Segment Timing QC service
verified-fdsn-code System FDSN registry
faces-blurred Media segment Privacy labeler
ml-false-positive Detection Human review service

Users stack the labelers they trust. A researcher can ask for "outdoor PM2.5 with no pm-ab-disagree label from my two chosen QC labelers", across every vendor at once.

Social graph and provenance

  • Follows: follow a system, a fleet, a feed, a person or a model. Your home view shows new detections, annotations and notable readings.
  • Threads: detections and annotations cite segments, so each wildfire, earthquake or smoke event becomes a thread of machine and human evidence from many domains.
  • Endorsements: "I used this data" signals roll up into usage metrics that operators can show funders.

Interoperability

Adoption depends on bridges in both directions: ingest bridges bring existing networks in with their consent, and facades let existing tools read ATsense without changes.

Bridge Direction Notes
OGC Connected Systems facade ATsense → CS API The main standards-facing view; core lexicons map one-to-one
OGC SensorThings facade ATsense → STA For existing SensorThings clients and dashboards
FDSN web services + SeedLink Both ways Seismic: ObsPy, SeisComP and Earthworm work unchanged
PurpleAir PurpleAir → ATsense Owner-authorized ingest; PurpleAir's per-call API pricing makes owner opt-in the practical route
Sensor.Community Sensor.Community → ATsense Open data; kits could also push to a native host directly
OpenAQ Both ways OpenAQ becomes an air-quality AppView and archive; its provider/owner/license model maps onto operator, fleet and license fields
CWOP / APRS-IS / MADIS Both ways Weather stations publish once and still reach NOAA
Safecast API Safecast → ATsense CC0 data; drives become track segments
BirdWeather BirdWeather → ATsense Detections become sense.acoustic.species records
MQTT Device → host Most hardware already speaks MQTT; the reference host accepts it

Rollout order

  1. Core lexicons and reference host, with MQTT, SeedLink and Connected Systems support.
  2. Mirror open data from Sensor.Community, Safecast, Raspberry Shake and OpenAQ, so discovery works on real data immediately.
  3. Native publishing for kits: firmware for Sensor.Community-style kits and Raspberry Shake that publishes straight to a host.
  4. Operators claim keys from bridges and optionally self-host.
  5. Cross-domain AppViews and feeds, starting with a wildfire incident feed that combines cameras, air quality and weather.

Open problems and next steps

The biggest open questions are privacy for cameras and microphones, restricted data, archive funding and vocabulary governance; the rest are engineering.

  • Restricted and embargoed data. ATproto repos are public by design. Options: publish manifests publicly but encrypt chunks with per-network keys, or add a private record namespace served only to authorized DIDs.
  • Who pays for archives. Pinning every chunk forever costs real money. Likely answer: institutional archive nodes as today, plus pinning agreements tied to citations and endorsements.
  • Fast-path trust. Sub-second early warning cannot wait for a commit. Should EEW stay on dedicated regional systems, bridged in, rather than on this protocol at all?
  • Numeric types. The draft picks integer-plus-scale everywhere. Open: whether waveform chunks that store floats natively (some digitizers, audio) should be exempt, since blobs are opaque to Lexicon anyway.
  • Manifest granularity. Hourly segments are a guess. Slow scalar streams may want daily segments to cut record count; video may want per-minute segments.
  • Metadata and calibration quality. Signing wrong metadata makes it verifiably wrong, and low-cost air sensors need correction factors to be comparable. Labelers help, but validation tools and published corrections should ship with the reference host.
  • Spam and junk sensors. Open participation invites fake or broken stations. Labelers and feed curation handle this the way Bluesky does, but default labelers need committed operators.
  • Privacy for cameras and microphones. Images and audio can capture people and homes. Options: on-device redaction before publishing, a public-redacted class enforced by hosts, and detections-only publishing with raw media kept private.
  • Vocabulary governance. Someone must steward observed-property and class vocabularies. Leaning on CF, QUDT, UCUM and GBIF taxon IDs avoids inventing new ones, but a registry of sense.* modules still needs an owner.
  • Vendor incentives. PurpleAir and similar vendors earn money from their APIs. Owner-controlled publishing may conflict with that model, so bridges should be owner opt-in.
  • Detection floods. Always-on ML models can emit millions of low-confidence detections. Hosts may need rate limits or confidence floors for the firehose.

Next steps

  • Finalize the sense.core.* lexicons and publish the Connected Systems mapping
  • Write the first three domain modules: seismic, air particle, imagery
  • Build a reference host with MQTT, SeedLink and Connected Systems support
  • Stand up one relay and one index AppView
  • Pilot with Sensor.Community, Raspberry Shake and one camera network in the same region
  • Ship a cross-domain wildfire feed and an air A/B QC labeler to prove discovery
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment