<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: shutdown.fyi</title>
    <description>The latest articles on DEV Community by shutdown.fyi (@shutdownfyi).</description>
    <link>https://dev.to/shutdownfyi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4135945%2Fc2c29cfc-e650-4c56-9b32-8f6cc446b253.png</url>
      <title>DEV Community: shutdown.fyi</title>
      <link>https://dev.to/shutdownfyi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shutdownfyi"/>
    <language>en</language>
    <item>
      <title>Telling censorship from outages: probing Russia's internet from both sides of the border</title>
      <dc:creator>shutdown.fyi</dc:creator>
      <pubDate>Mon, 21 Sep 2026 14:37:19 +0000</pubDate>
      <link>https://dev.to/shutdownfyi/telling-censorship-from-outages-probing-russias-internet-from-both-sides-of-the-border-3c28</link>
      <guid>https://dev.to/shutdownfyi/telling-censorship-from-outages-probing-russias-internet-from-both-sides-of-the-border-3c28</guid>
      <description>&lt;p&gt;When something stops loading in Russia, there are two very different explanations. The service might be down for everyone. Or it might be up everywhere except inside Russia. Outage trackers built on user complaints can't tell these apart: a spike of "YouTube isn't working" looks the same in both cases.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://shutdown.fyi/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=launch_en" rel="noopener noreferrer"&gt;shutdown.fyi&lt;/a&gt;, an open tracker for 38 services, 8 carriers and 85 regions. The core idea is simple: &lt;strong&gt;check the same address from inside Russia and from abroad, step by step, and compare where each attempt stops.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Two vantage points, four steps
&lt;/h2&gt;

&lt;p&gt;Every 5 minutes, for every service, we probe up to two hostnames. We use the real websites, not CDN hosts: a TLS handshake to an image CDN tells you nothing. The steps are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DNS&lt;/strong&gt; — does the name resolve, and to what?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TCP&lt;/strong&gt; — can we open a connection to port 443?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TLS&lt;/strong&gt; — does the handshake with the correct SNI complete?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP&lt;/strong&gt; — do we get a response?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The same sequence runs from a server in Russia and from one in Helsinki. The verdict comes from the &lt;em&gt;difference&lt;/em&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;From Russia&lt;/th&gt;
&lt;th&gt;From abroad&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;all steps pass&lt;/td&gt;
&lt;td&gt;all steps pass&lt;/td&gt;
&lt;td&gt;open&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;stops at DNS / TCP / TLS&lt;/td&gt;
&lt;td&gt;passes&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;restricted&lt;/strong&gt;, and we know at which step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fails&lt;/td&gt;
&lt;td&gt;fails&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;down&lt;/strong&gt; — the service itself is broken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP 403/451 from the service&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;the service itself refuses Russian users&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A real result for youtube.com today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"ru"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"142.251.155.4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"pingMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tcpMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"abroad"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tlsMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TCP connects in 20 ms, so the route is fine and the server is up. Then the TLS handshake just hangs, but only from Russia. That pattern (&lt;code&gt;blocked-tls&lt;/code&gt;) is what filtering on the server name in the TLS ClientHello looks like. Telegram, WhatsApp and Instagram fail one step earlier (&lt;code&gt;blocked-tcp&lt;/code&gt;): the IP addresses themselves are unreachable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo76b7xoxhzddwq1s0vyt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo76b7xoxhzddwq1s0vyt.png" alt="What opens from Russia right now" width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Two axes instead of one status
&lt;/h2&gt;

&lt;p&gt;This forced a design decision. A status page usually has one colored dot. Here that's a lie in either direction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a service restricted for months is "nothing new", so an anomaly detector shows it as normal;&lt;/li&gt;
&lt;li&gt;the same service is obviously not reachable, so a reachability check shows it as broken forever.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So every entity has two independent axes. &lt;strong&gt;Access&lt;/strong&gt; is measured only by active probes and exists only for services; a carrier or a region has no "reachability" we can measure. &lt;strong&gt;Incident&lt;/strong&gt; answers "is it worse than usual &lt;em&gt;for this hour&lt;/em&gt;?" and comes from complaints, OONI measurements, IODA traffic data and BGP visibility.&lt;/p&gt;

&lt;p&gt;"Usual for this hour" matters. Complaints at 3 a.m. and 8 p.m. differ by an order of magnitude. The baseline is the median of the same hour over previous days. It has a floor of half the weekly median, so three complaints on a quiet night don't read as "10× above normal".&lt;/p&gt;

&lt;h2&gt;
  
  
  Bugs that taught us something
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Levels are not counts.&lt;/strong&gt; We aggregate eight public outage detectors. Some publish an hourly series. Others publish only "complaints in the last 24 hours". We wrote that 24-hour number into the current hour on every run, so a daily sum added up 24 copies of a daily level. One source reported 18 messages a day on its own page, and we showed ~450. The fix: a rolling 24-hour level becomes 1/24 per hour. A day total plus a chart becomes "take the sum from the total, the shape from the chart".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The bank that wasn't blocked.&lt;/strong&gt; Our Russian probe flagged a major Russian bank as "restricted": TLS timed out from Russia and worked from Helsinki. The bank isn't blocked in Russia. Its anti-bot protection drops connections from hosting IP ranges, and our probe lives in a datacenter. Domestic services on the government "whitelist" now get "no conclusion" instead of "restricted" when only our Russian vantage fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flapping.&lt;/strong&gt; Incidents closed on the first "ok" snapshot and reopened on the next spike. The feed filled with six-minute "outages". Now an incident closes only after 15 minutes of normal readings, and anything shorter is hidden.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;ркн&lt;/code&gt; inside a word.&lt;/strong&gt; The news matcher for "РКН" (the Russian telecom regulator) matched inside «подчеркнул» ("emphasized"), so a mayor's speech ended up on a status page. Word boundaries in Cyrillic need explicit lookarounds; &lt;code&gt;\b&lt;/code&gt; doesn't know Cyrillic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open data
&lt;/h2&gt;

&lt;p&gt;Everything is available as JSON without a key, CC BY 4.0:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://shutdown.fyi/api/status/service/youtube.json | jq &lt;span class="s1"&gt;'.probe.hosts[] | {host, verdict}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's an embeddable widget too. Docs, examples and the full methodology are on GitHub: &lt;a href="https://github.com/Chumbayoumba/shutdown-fyi-open" rel="noopener noreferrer"&gt;Chumbayoumba/shutdown-fyi-open&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you do network measurement, I'd love feedback on the vantage-point approach. A single Russian vantage in a datacenter is the obvious weak spot, and residential probes would make the verdicts much stronger.&lt;/p&gt;

</description>
      <category>networking</category>
      <category>opendata</category>
      <category>monitoring</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
