Skip to content
simenzzzPublic

About

A federated edge-to-cloud telemetry pipeline for scientific sensor networks. Delay-tolerant transport treats disconnected operation as a first-class case.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Tideway

A federated edge-to-cloud telemetry pipeline for scientific sensor networks.

Tideway ingests high-rate sensor data at edge nodes, compresses and processes it locally, and forwards it across federated zones over delay-tolerant transport — treating disconnected operation as a first-class case, not an error path. It targets deployments where bandwidth is scarce, links are intermittent, and the centralised-cloud model fails for cost, latency, or reachability reasons. When a zone is partitioned it keeps ingesting, querying, and detecting anomalies locally; when the link returns, nodes reconcile and transfer only the difference.

Research-stage, single-maintainer project, built in Rust. The design is recorded as 17 Architecture Decision Records, each grounded in a 23-paper reference corpus. See docs/ for the full design and .claude/ROADMAP.md for milestone status.

Architecture

Six functional domains across three tiers (edge node → zone gateway → cloud):

   sensors
      │  high-rate ingest, reorder, compress, anomaly-score, durably store
      ▼
┌──────────────┐   delay-tolerant     ┌──────────────┐   windowed-aggregate    ┌──────────────┐
│  edge node   │  bundles (BPv7-ish)  │ zone gateway │   query push-down        │    cloud     │
│ tideway-edge │ ───────────────────▶ │tideway-gateway│ ◀──────────────────────  │ tideway-cloud │
└──────────────┘  reconcile-on-       └──────────────┘   signed responses        └──────────────┘
                  reconnect (only the difference crosses the wire)
  • A — Storage & ingest: append-only event log on a pure-Rust LSM, content-ID dedup (exactly-once at rest), time-indexed CQRS read model, at-rest compression.
  • B — Sync & reconciliation: rateless-IBLT set reconciliation over content IDs; CRDTs for control-plane state.
  • C — Edge anomaly detection: streaming robust statistics (median/MAD); quality annotations ride alongside telemetry, never mutating measurements.
  • D — Federated learning: FedDrift-lineage drift detection + weighted-average model aggregation, tolerant of node dropouts.
  • E — Federated query: windowed-aggregate push-down from cloud to zones; partition returns explicitly-labelled partial results.
  • F — Observability: Tideway-native span/metric records that buffer on partition and reconstruct/export (OTLP-shaped) on reconnect.

Full detail: docs/02-architecture-overview.md.

Run it

Requires the pinned Rust toolchain (rust-toolchain.toml, 1.94.1). Build & test:

cargo test --workspace      # ~710 tests
cargo build --workspace --bins

Edge tier — ingest a synthetic multi-sensor stream offline, then query it:

cargo run -p tideway-edge -- --sources 3 --per-source 4000 --window 32 --batch 256

Re-running with the same --path/--seed re-ingests identical records; the content-addressed log deduplicates them (exactly-once at rest).

Zone gateway — two nodes converge after a partition, transferring only the difference (two terminals):

# print each node's key for out-of-band exchange, then:
tideway-gateway --listen  127.0.0.1:9000 --path /tmp/a --seed <hexA> --peer-key <keyB> --shared 1000 --unique 100
tideway-gateway --connect 127.0.0.1:9000 --path /tmp/b --seed <hexB> --peer-key <keyA> --shared 1000 --unique 100

Cloud query — fan a windowed aggregate out to zone gateways and merge:

# zone side (one per gateway):
tideway-gateway --serve-query 127.0.0.1:9001 --path /tmp/a --seed <hexA> --peer-key <cloudKey> --shared 50
# cloud side:
tideway-cloud --seed <cloudHex> --zone <gwKeyA>:127.0.0.1:9001 --from 0 --to 1000 --aggregate sum

A zone that doesn't answer is labelled a partial result (with last-known freshness) rather than failing the query.

Demo Runbook

This is the portfolio-facing path through the system. It uses the three public binaries for the tier demos, then points to focused E2E tests for the library-first domains.

0. Preflight

cargo test --workspace
cargo build --workspace --bins

The workspace uses the pinned toolchain in rust-toolchain.toml. CI also runs cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, cargo deny check, cargo audit, and cargo llvm-cov --workspace --summary-only.

1. Offline edge node

Run one edge node with a deterministic multi-sensor stream:

cargo run --quiet -p tideway-edge -- \
  --path /tmp/tideway-demo/edge \
  --sources 3 \
  --per-source 4000 \
  --window 32 \
  --batch 256

What this demonstrates:

  • ingest runs fully offline;
  • out-of-window late samples are accounted for and merged back, not dropped;
  • records are durably committed to the append-only store;
  • the time-indexed read model answers local windowed queries.

Typical output:

tideway-edge: ingested 12000 samples offline into /tmp/tideway-demo/edge
  in=12000 emitted=9953 late=2047 records=40
  query_all: 40 records across 3 sources
  recent window [3599, 3999] (start-in-window):
    sensor-2: 1 records
    sensor-1: 0 records
    sensor-0: 1 records

Re-run the same command against the same --path and --seed to exercise content-addressed deduplication: identical records are not appended twice.

2. Gateway reconciliation after a partition

Use two terminals. These fixed seeds are demo-only; they intentionally put secrets on the command line.

# Terminal A
cargo run --quiet -p tideway-gateway -- \
  --listen 127.0.0.1:19000 \
  --path /tmp/tideway-demo/gw-a \
  --seed a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1 \
  --peer-key 55154f42065ea5a1bea05463826be2684eb92df92c100027aabaae57ca554207 \
  --eid 1 \
  --peer-eid 2 \
  --shared 1000 \
  --unique 100
# Terminal B
cargo run --quiet -p tideway-gateway -- \
  --connect 127.0.0.1:19000 \
  --path /tmp/tideway-demo/gw-b \
  --seed b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2 \
  --peer-key bc7cbcb5636375fa1d82434d466724d92377f53b980695dd49d26d0ce12205a5 \
  --eid 2 \
  --peer-eid 1 \
  --shared 1000 \
  --unique 100

What this demonstrates:

  • each gateway starts with overlapping but diverged record sets;
  • the TCP DTN path reconciles content IDs after reconnect;
  • only the difference crosses the link;
  • each side converges to the union.

Typical output on each side:

local records: 2
--- converge complete ---
records sent:        1
records received:    1
  of which duplicate: 0
coded symbols:       128
bytes on wire:       11658
local records: 2 → 3

Run the same two commands again. The second pass should be a fixpoint:

records sent:        0
records received:    0
local records: 3 → 3

3. Cloud fan-out query

Start two query-serving zones:

# Terminal A
cargo run --quiet -p tideway-gateway -- \
  --serve-query 127.0.0.1:19001 \
  --path /tmp/tideway-demo/zone-a \
  --seed a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1 \
  --peer-key 814722de71c5b14e748dff322ae7f7c415cee558766495292cd6c4c0a6a9df28 \
  --eid 1 \
  --shared 64 \
  --unique 32
# Terminal B
cargo run --quiet -p tideway-gateway -- \
  --serve-query 127.0.0.1:19002 \
  --path /tmp/tideway-demo/zone-b \
  --seed b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2 \
  --peer-key 814722de71c5b14e748dff322ae7f7c415cee558766495292cd6c4c0a6a9df28 \
  --eid 2 \
  --shared 64 \
  --unique 32

Query both zones from the cloud coordinator:

cargo run --quiet -p tideway-cloud -- \
  --seed eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee \
  --zone bc7cbcb5636375fa1d82434d466724d92377f53b980695dd49d26d0ce12205a5:127.0.0.1:19001 \
  --zone 55154f42065ea5a1bea05463826be2684eb92df92c100027aabaae57ca554207:127.0.0.1:19002 \
  --from 0 \
  --to 1000 \
  --aggregate sum

Typical output:

query 1: SumI64
aggregate: SumI64(5024)
completeness: complete (2 zone(s) answered)

Stop Terminal B, then run a partitioned query with B's endpoint unavailable:

cargo run --quiet -p tideway-cloud -- \
  --seed eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee \
  --zone bc7cbcb5636375fa1d82434d466724d92377f53b980695dd49d26d0ce12205a5:127.0.0.1:19001 \
  --zone 55154f42065ea5a1bea05463826be2684eb92df92c100027aabaae57ca554207:127.0.0.1:19999 \
  --from 0 \
  --to 1000 \
  --aggregate sum \
  --query-id 2

Typical output:

query 2: SumI64
aggregate: SumI64(2512)
completeness: PARTIAL — 1 of 2 zone(s) missing:
  zone 0d5e8c45d185f6812a60d63c6b9177615d883ea2bb06055532c0ae05ffb5be75 missing; never answered

That is the intended disconnected-query behavior: the coordinator returns the reachable aggregate immediately and labels the missing zone instead of hiding the gap or failing the whole query.

4. Library-first domains

These domains are implemented as libraries and integrated through E2E tests rather than separate daemons:

cargo test -p tideway-quality --test quality_e2e -- --nocapture
cargo test -p tideway-fl --test fl_round_e2e -- --nocapture
cargo test -p tideway-obs --test observability_e2e -- --nocapture
cargo test -p tideway-cloud --test daemon_query_e2e -- --nocapture
cargo test -p tideway-gateway --test gateway_e2e -- converge_after_partition_transfers_only_the_difference --nocapture

Those tests demonstrate anomaly annotations that preserve telemetry, federated learning rounds that tolerate zone dropout and drift reassignment, observability records that buffer through partition and export OTLP-shaped JSON, cloud query complete/partial behavior over TCP, and gateway reconciliation over the DTN path.

Optional performance evidence:

cargo test -p tideway-store --test throughput -- --ignored --nocapture
cargo test -p tideway-query --test scale -- --ignored --nocapture

Headline results

  • ~710 tests pass; 88.73% line coverage (cargo llvm-cov, workspace-wide).
  • Ingest: ~14–17M samples/s single-threaded — ~14–17× the provisional 10⁶/s target, well inside a 1–4 GiB memory envelope.
  • Storage: ~29M samples/s (~557 MiB/s) writing batched record-frames, and it recovers every Sync'd record after a hard crash — pure-Rust LSM (fjall), no FFI.
  • Reconciliation: rateless IBLT transfers ≈ O(difference) bytes regardless of dataset size (~93 B per differing record at 0.1% divergence), estimate-free.
  • Rust throughout, no FFI, #![forbid(unsafe_code)] across the workspace; CI runs fmt / clippy -D warnings / test + cargo deny + cargo audit + coverage.

Repository layout

crates/    11 domain library crates (tideway-core, -codec, -store, -ingest, -sec,
           -recon, -dtn, -query, -obs, -quality, -fl)
bins/      3 tier binaries: tideway-edge · tideway-gateway · tideway-cloud
docs/      requirements, architecture, glossary, design docs, and 17 ADRs
spikes/    throwaway decision-closing benchmark harnesses (kept as gate evidence)

Status & documentation

Research-stage; all six domains across all three tiers are built and tested, and all 17 ADRs are Accepted. Deferred by design (and why) is tracked in the roadmap: deployment-gated tuning (no real deployment exists yet), explicitly-dropped acceleration (XDP/FPGA, contrary to the modest-hardware assumption), and hardening the cooperative-zone threat model excludes (Byzantine peers, extra transports).

License

Apache-2.0 OR MIT.

About

A federated edge-to-cloud telemetry pipeline for scientific sensor networks. Delay-tolerant transport treats disconnected operation as a first-class case.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages