# I wanted to see which subsea cable my packets cross. The cloud couldn't trace, and the database put Helsinki in Phoenix

> Source: <https://dev.to/affan_shaik/i-wanted-to-see-which-subsea-cable-my-packets-cross-the-cloud-couldnt-trace-and-the-database-put-4e9o>
> Published: 2026-11-05 00:19:20+00:00

**The idea**

When you open a website, your data passes through a dozen routers, a few ISPs, maybe a cable on the ocean floor, and several countries with their own data laws. None of that is visible. My pitch was:

Trace packets from your own computer, work out which networks, countries, and subsea cables they cross, check the route against data-sovereignty laws like GDPR, and draw it live on a 3D Earth.

The constraints: cost $0, use only open data, and say clearly what is measured and what is guessed.

Live demo: packetpulse-eta.vercel.app Code, data and tests: [github.com/Flotrpy/PacketPulse](https://dev.tourl)

Scoping it down so it was buildable

**Measurement:** plain traceroute (ICMP/UDP with rising TTL), written from scratch in Go with raw sockets. Traceroute never sees a cable or a country; everything past the IP addresses and round-trip times is inference.

**Data:** only free sources. TeleGeography's submarine cable map (690 cables), iptoasn (~580k IP ranges), DB-IP Lite City, Natural Earth borders, PeeringDB, AWS/GCP region prefixes, and a hand-curated law directory.

**Laws:** a small compliance engine with profiles like "EU personal data stays in the EEA or adequate countries" (GDPR Chapter V) or "Indian payment data stays in India" (RBI).

**Hosting:** free tiers only. Render runs the Go backend, Vercel serves the globe.

**Iteration 1:** the server can't trace, and shouldn't

The obvious design: the user types a website, the server runs the traceroute, the globe draws it.

The first deploy came back with this in the health check:

```
{"status": "ok", "tracing": false}
```

Render's containers can't open raw ICMP sockets, so traceroute is impossible there. My development sandbox could trace, but its network let only 2 hops through.

Even if it had worked, it would have been the wrong trace. A path from a data centre in Oregon tells you nothing about your path.

Lesson: sometimes the platform limit is pointing at a design flaw.

**Iteration 2:** a small agent on your computer

The fix was to split the work:

The globe stays in the browser.

A small agent, ppagent, runs on your computer and long-polls the server for trace jobs.

You click Trace on the website. Your agent traces from your network and public IP and posts the raw result back.

The server does all the enrichment (geolocation, ASNs, cables, compliance), so the agent stays tiny and needs no data files.

On Windows, the first real run printed one line and quit:

```
level=ERROR msg="trace failed" target=C:\Users\affan\Downloads\ppagent.exe
  err="
```

... target must be a hostname or IP address"

The program received its own path as an argument, treated it as a website to trace, failed, and exited. The agent now ignores its own path and skips anything that isn't a hostname or IP. Setup on Windows is now one line in an Administrator PowerShell:

```
irm https://packetpulse-test.onrender.com/v1/agent/download/start-ppagent.ps1 | iex
```

Lesson: the first run on someone else's machine is the real test.

**Iteration 3:** 1.1.1.1 is in Australia (it isn't)

With the agent working, the first trace to 1.1.1.1 drew an arc from the US to Australia. The IP database lists Cloudflare's address under its registered location. But 1.1.1.1 is anycast: it answers from whichever site is closest, and it replied in about 3 ms.

Physics settles it. Light in fiber covers about 204 km per millisecond, so a 3 ms round trip means the server is at most about 300 km away. Every router location already had to pass this check. The destination didn't, so I added it.

Lesson: a free, exact constraint (the speed of light) beats any database.

**Iteration 4:** the trace crossed the Atlantic, but no cable showed up

This was the real test. I traced from New Jersey to a server in Helsinki. The latency clearly jumped across the ocean, but the globe showed no subsea cable.

Here is the actual trace, with what the GeoIP database said about each router:

Hop Router                          RTT    Database says

2   lo0-100.NWRKNJ-…verizon-gni.net   4.9 ms     Newark, US

6   ewr-bb2-link.ip.twelve99.net    6.6 ms     nothing

7   kbn-bb6-link.ip.twelve99.net    105.9 ms   Solna, SE

9   hls-b4-link.ip.twelve99.net 94.7 ms    Warsaw, PL

11  hetzner-ic-….twelve99-cust.net    102.4 ms   Phoenix, US

14  hel1-speed.hetzner.com          104.8 ms   Helsinki, FI

Three things went wrong.

The router just before the ocean had no location. Hop 6 is the crossing point, but the database didn't know it, so the segment into hop 7 was "unknown". The router name knew, though: Arelion (Telia) names routers with city codes. ewr is Newark, kbn is København, hls is Helsinki, adm is Amsterdam. Some of these weren't in my lookup table. I added them, plus OVH, Hetzner and Verizon codes, and gaps are now bridged from the last router that does have a location.

Phoenix. The database put a Helsinki interconnect in Arizona, which produced two fake "subsea" segments (Stockholm → Phoenix → Helsinki). The RTT check passed it, because 102 ms is plenty of time to reach Phoenix from New Jersey. What it didn't check was the path. Two new rules:

Path check: source → previous router → this router must all fit inside the RTT.

Detour check: if the route goes somewhere and comes straight back, the timings have to pay for the extra distance. Stockholm → Phoenix → Helsinki with almost no added RTT doesn't.

As a side benefit, RTT noise is handled better by using the lowest RTT seen at this hop or any later one. Slow ICMP replies only ever add delay, and the path to a later hop passes through this one.

The result:

**                          Before          After**

Hop 6 (ewr)           unlocated         Newark

Hop 7 (kbn)           Solna                 Copenhagen

Hop 11                    Phoenix           dropped

Subsea segments       2 fake ones (via Phoenix) 1: Newark → Copenhagen

Cable   none    Havfrue/AEC-2 (Wall Township, NJ → Blaabjerg, DK)

Newark → Copenhagen is 6,200 km, so the fastest possible round trip is about 61 ms. The measured jump is 99 ms, which is typical once you add real cable routing and equipment. Havfrue/AEC-2 lands about 50 km from where the trace started, which is what the ranking rewards. This exact trace is now a regression test in the repo.

Lesson: test on a real trace, not the example you built the code around. My test fixture was Mumbai → Marseille over Tata's network, whose router names I'd already taught it.

Iteration 5: what data is it, and is that legal?

Knowing the route is half the question. The other half is what is on it. Classifying traffic by reading packets is out of scope (and creepy), so traffic is tagged at ingestion from rules on what's already known: destination host, port, network, cloud provider, and an app label from the agent.

```
"data_classification": {"labels": ["payment_system_data"], "source": "rules",
                        "rule_ids": ["india-payment-gateways"]}
```

The compliance engine then walks the route point by point. For the recorded trace from Mumbai (Jio 5G) to AWS Frankfurt, tagged payment_system_data, the RBI profile raises an alert at the German destination, with the law, the entry time, and a suggested fix ("terminate payment flows in India: use AWS ap-south-1"). If the location is uncertain, as with the Jeddah cable landing in that example, the alert is marked suspected instead.

Iteration 6: making the globe look like Earth

The first globe was a blue dot-matrix sphere. It looked fine as a demo, but not like a real place. The current one uses satellite imagery lit by the real sun for the current time. Day and night, city lights, clouds and the glint on the oceans are all computed in one shader, with a few lines of solar maths (declination plus the equation of time).

The paths are the performance part. Every hop-to-hop segment is an instance of one ribbon mesh, so 1,000 paths draw in 6 draw calls, and a live latency update for one path uploads about 600 bytes.

What I'd tell myself at the start

Ask where the measurement happens before building it. The cloud couldn't trace, and the trace that mattered was never going to come from the cloud anyway.

Physics is your best validator. The speed of light in fiber rejected Australia and Phoenix for free. Databases are hints.

Router names are better geolocation than databases. Network operators name routers with airport and city codes, and those beat the free databases on backbone hops.

Check the path, not the point. Each location can look plausible on its own while the route through it is impossible.

Test on real traces from real networks. A 14-hop trace from a home connection found more bugs than any fixture I wrote.

Honest limits

Everything past the IPs is inferred. Cable names come with probabilities. "Havfrue/AEC-2" is the best fit, not an observation, and I have no ground truth for which cable a packet actually used.

Geolocation is city-level at best. Some routers stay unlocated, and a few wrong locations will still pass the checks.

Traceroute sees only the forward path, and networks can hide hops (MPLS tunnels, filtered ICMP).

The compliance verdicts are a model, not legal advice. The law directory is informational.

Small sample. The subsea fixes were driven by one real transatlantic trace (now a regression test) plus a handful of runs to London, Amsterdam and Roubaix. The Windows agent has been run on one machine.

Free hosting. The server sleeps after 15 minutes and keeps traces in memory, so the first visit is slow and restarts clear the globe.

**Try it On Windows, open PowerShell as Administrator and run:**

```
irm https://packetpulse-test.onrender.com/v1/agent/download/start-ppagent.ps1 | iex
```

Then open packetpulse-eta.vercel.app and click London or Helsinki under Across the Atlantic. Traces need an agent token; the README explains how to run your own server with Docker in two commands.

[git clone https://github.com/Flotrpy/PacketPulse](https://dev.tourl)

```
cd PacketPulse
docker build -t packetpulse .
docker run --rm -p 8080:8080 -e PACKETPULSE_ADMIN_TOKENS="me:$(openssl rand -hex 24)" packetpulse
```

macOS and Linux agents, the API, and how the cable detection works are all in the README.

Feedback is very welcome, especially from anyone who runs a backbone network and can tell me which cable my packets actually took.
