Context
Hubble Network operates a global network of satellites and BLE gateways that receive short advertisements from millions of low‑power devices. Because many receivers overlap, the same device packet is often received by several satellites/gateways, and receivers occasionally retry uploads. Our ingestion API returns everything it has, unfiltered.
Your job: build a small web app that lets an operator explore these events without the browser falling over.
The API
GET https://takehome.hubble.com/api/events
It returns all events in a single JSON array (~246 000 records, ~64 MB uncompressed, ~14 MB gzipped). There is no documented pagination or filtering. The API is what it is; if you think it should be different, tell us in your README.
Event shape:
{
"id": "evt_8f3a…", // unique per receiver observation
"deviceId": "dev_01HZX…", // the BLE device
"seq": 4127, // per-device packet sequence number (uint16, wraps at 65535)
"receivedAt": "2026-08-30T11:42:07.318Z",
"receiverId": "sat-17" | "gw-berlin-03",
"receiverType": "satellite" | "gateway",
"rssi": -87, // signal strength, dBm
"location": { "lat": 52.51, "lng": 13.39 } | null,
"payload": { "battery": 0.83, "temperature": 21.4 } // may be partially missing
}
Real telemetry is messy. Treat the shape above as what the API intends to return, not as a guarantee.
Requirements
- Load the data and keep the UI usable while doing so (progress indication, no frozen tab).
- Deduplicate the events. We deliberately don't define "duplicate" — decide what it means for this domain, implement it, and explain the rule and its tradeoffs in the README.
- Explore: a list/table of deduplicated events with
- text search by
deviceId, - filter by
receiverType, - sort by
receivedAtandrssi. Interactions must stay responsive on the full dataset.
- text search by
- Summary panel: unique devices, raw vs. deduplicated count, events per receiver type.
- Handle the ugly parts: malformed/missing fields, network errors, a user who navigates away mid‑load. (Tip: DevTools → Network → throttling / offline is enough to test this.)
Bonus — only if you have time left
We'd rather see 3 things done well than 6 done badly.
- Map of last known device positions.
- Events‑per‑hour chart.
- Filters/sort persisted in the URL.
- Streaming parse (start showing data before the response finishes).
Constraints
- Start from scratch — there is no starter repo. Any framework (React/Vue/Svelte/Angular/none). TypeScript preferred.
- Use whatever tools you use at work, including AI assistants. We will discuss the code with you in a follow‑up call, so make sure you can explain every decision in it.
Deliverable
- A GitHub repo (public, or private with access for us — details in the recruiter's email).
npm ci && npm run devmust work.npm run buildmust pass.npm run preview(or an equivalentstart) must serve the production build — that's what we'll measure against. If you deploy (Vercel etc.), add the link.- A
README.mdwith:- how to run it,
- your deduplication rule and why,
- what you'd change about the API if you could,
- what you'd do next with more time,
- which parts you leaned on AI for and what you changed or rejected.
What we want to read
We're not grading feature count. We're looking for engineers who know the difference between something that was quickly pushed out and something that was thoughtfully designed, regardless of the tool used to write it. Tradeoffs you name are worth more than features you ship.
The follow‑up call
We'll ask you to walk us through it, and we'll dig into the dedup logic and the performance decisions.
Questions
If you have questions, please reach out to the recruiter.