Publish a no-unsolicited-messages policy
All checks were successful
Build and Deploy to Production / test (push) Successful in 1m12s
Build and Deploy to Production / build (push) Successful in 1m8s
Build and Deploy to Production / deploy (push) Successful in 52s

The .xyz registry put the domain on serverHold after SURBL listed it in
its ABUSE list. SURBL only delists a site that links to a published
policy against advertising it by unsolicited messages, and a privacy
policy does not count.

Add the policy at /:locale/policy in all three languages, linked from
the footer and listed in the sitemap, and the same text in the README
so it can be read on GitHub while the domain does not resolve. Reports
go to the asxp.io contact form; the site itself has no email.

Assisted-by: Claude:opus-5-5
This commit is contained in:
Sergei Poljanski 2026-10-20 23:35:09 +04:00
commit a7d558f5c5
Signed by: asxpi
GPG key ID: 4F8851660FA4121B
7 changed files with 80 additions and 6 deletions

View file

@ -53,6 +53,9 @@ renderings only for unbuilt sites) and credit format.
degraded, down (nothing answered for two buckets), unknown (worker stale,
or under half the controls answering). Not incidents: never cross out a
cell from this alone.
- `/:locale/policy`: the no-unsolicited-messages policy, also in
`README.md`. Blocklists (SURBL) require a published one to delist; keep
both copies in step.
- `config.ru`: `www.` → `BINGO_HOST` 301.
- `views/`: `layout.erb` (CSS and SEO head), `index.erb` (card, provider
filter, dialogs), `dc.erb`, partials. `assets/`: icons.

View file

@ -22,4 +22,16 @@ of Claude (Anthropic). Part of the list came from the
Credit "Datacenter Bingo (datacenterbingo.xyz)"; the full attribution
line is in the file.
## No unsolicited messages
datacenterbingo.xyz is never advertised by unsolicited email, SMS,
messenger, forum or social media messages. Neither we nor any third party
may promote the site this way. The site sends no email and has no mailing
list, newsletter or contact form, and it carries no ads, affiliate programs
or paid promotion.
If you received a message linking to this site, we did not send it. Please
report it via [asxp.io/contact](https://asxp.io/contact). The same policy
is on the site at [/en/policy](https://datacenterbingo.xyz/en/policy).
Development: `nix develop`, then `bin/test`. See `CLAUDE.md`.

17
app.rb
View file

@ -269,7 +269,8 @@ class BingoWeb < Sinatra::Base
cache_control :public, max_age: 3600
dcs = Bingo::Datacenter.order(:position, :id).eager(:incidents).all
@entries = [[->(l) { "/#{l}" }, Bingo::Board.load.updated_on, '/'],
[->(l) { "/#{l}/status" }, nil, '/en/status']] +
[->(l) { "/#{l}/status" }, nil, '/en/status'],
[->(l) { "/#{l}/policy" }, nil, '/en/policy']] +
dcs.map { |dc| [->(l) { dc_path(dc, l) }, [dc.updated_at, *dc.incidents.map(&:updated_at)].compact.max] }
erb :sitemap, layout: false
end
@ -326,6 +327,20 @@ class BingoWeb < Sinatra::Base
erb :status
end
# Anti-spam policy; blocklists (SURBL) ask for a published one.
get %r{/(ru|en|uk)/policy} do |locale|
@locale = locale
@title = t(:policy_title)
@desc = t(:policy_desc)
@path_for = ->(l) { "/#{l}/policy" }
@x_default = '/en/policy'
url = "#{base_url}/#{locale}/policy"
@ld = JSON.generate('@context' => 'https://schema.org', '@type' => 'WebPage', '@id' => url, 'url' => url,
'name' => @title, 'description' => @desc, 'inLanguage' => locale).gsub('</', '<\/')
cache_control :public, max_age: 3600
erb :policy
end
get '/status.json' do
content_type 'application/json', charset: 'utf-8'
headers 'Access-Control-Allow-Origin' => '*'

View file

@ -86,7 +86,12 @@ module Bingo
status_cols: { 'dc' => 'ЦОД', 'state' => 'Сейчас', 'hours' => '24 часа', 'rtt' => 'мс', 'targets' => 'адр.' },
status_dc: 'Доступность сети', status_hour: '%{h} ч назад: %{p}',
status_desc: 'Доступность крупнейших дата-центров России онлайн: ежеминутные сетевые проверки IXcellerate, Selectel, DataPro, Key Point и других, история за 24 часа и открытые данные.',
status_probes: 'Что проверяем', status_controls_list: 'Контрольные узлы в России', status_no_answer: 'не отвечает', status_source: 'почему этот адрес'
status_probes: 'Что проверяем', status_controls_list: 'Контрольные узлы в России', status_no_answer: 'не отвечает', status_source: 'почему этот адрес',
policy_link: 'Без спама', policy_title: 'Без нежелательных рассылок | Бинго ЦОДов',
policy_desc: 'Бинго ЦОДов никогда не рекламируется нежелательными сообщениями. Сайт не отправляет писем и не ведёт рассылок.',
policy_text: ['datacenterbingo.xyz никогда не рекламируется нежелательными письмами, SMS, сообщениями в мессенджерах, на форумах или в соцсетях. Ни мы, ни кто-либо другой не вправе продвигать сайт таким образом.',
'Сайт не отправляет писем, у него нет рассылок, подписок и форм обратной связи. На нём нет рекламы, партнёрских программ и платного продвижения.',
'Если вы получили сообщение со ссылкой на этот сайт, его отправили не мы. Пожалуйста, сообщите о нём через %{link}.']
},
'en' => {
seo_title: 'Datacenter Bingo — strikes, fires and outages at Russian datacenters',
@ -132,7 +137,12 @@ module Bingo
status_cols: { 'dc' => 'Datacenter', 'state' => 'Now', 'hours' => '24 hours', 'rtt' => 'ms', 'targets' => 'addr.' },
status_dc: 'Network reachability', status_hour: '%{h} h ago: %{p}',
status_desc: "Live reachability of Russia's largest datacenters: minute-by-minute network checks of IXcellerate, Selectel, DataPro, Key Point and others, 24-hour history and open data.",
status_probes: 'What we check', status_controls_list: 'Control hosts in Russia', status_no_answer: 'no answer', status_source: 'why this address'
status_probes: 'What we check', status_controls_list: 'Control hosts in Russia', status_no_answer: 'no answer', status_source: 'why this address',
policy_link: 'No-spam policy', policy_title: 'No unsolicited messages | Datacenter Bingo',
policy_desc: 'Datacenter Bingo is never advertised by unsolicited messages. The site sends no email and runs no mailing list.',
policy_text: ['datacenterbingo.xyz is never advertised by unsolicited email, SMS, messenger, forum or social media messages. Neither we nor any third party may promote the site this way.',
'The site sends no email and has no mailing list, newsletter or contact form. It carries no ads, affiliate programs or paid promotion.',
'If you received a message linking to this site, we did not send it. Please report it via %{link}.']
},
'uk' => {
seo_title: 'Бінго ЦОДів — атаки, пожежі та аварії дата-центрів Росії',
@ -178,7 +188,12 @@ module Bingo
status_cols: { 'dc' => 'ЦОД', 'state' => 'Зараз', 'hours' => '24 години', 'rtt' => 'мс', 'targets' => 'адр.' },
status_dc: 'Доступність мережі', status_hour: '%{h} год тому: %{p}',
status_desc: 'Доступність найбільших дата-центрів Росії онлайн: щохвилинні мережеві перевірки IXcellerate, Selectel, DataPro, Key Point та інших, історія за 24 години та відкриті дані.',
status_probes: 'Що перевіряємо', status_controls_list: 'Контрольні вузли в Росії', status_no_answer: 'не відповідає', status_source: 'чому ця адреса'
status_probes: 'Що перевіряємо', status_controls_list: 'Контрольні вузли в Росії', status_no_answer: 'не відповідає', status_source: 'чому ця адреса',
policy_link: 'Без спаму', policy_title: 'Без небажаних розсилок | Бінго ЦОДів',
policy_desc: 'Бінго ЦОДів ніколи не рекламується небажаними повідомленнями. Сайт не надсилає листів і не веде розсилок.',
policy_text: ['datacenterbingo.xyz ніколи не рекламується небажаними листами, SMS, повідомленнями в месенджерах, на форумах чи в соцмережах. Ні ми, ні будь-хто інший не має права просувати сайт у такий спосіб.',
'Сайт не надсилає листів, не має розсилок, підписок і форм зворотного зв’язку. На ньому немає реклами, партнерських програм і платного просування.',
'Якщо ви отримали повідомлення з посиланням на цей сайт, його надіслали не ми. Будь ласка, повідомте про нього через %{link}.']
}
}.freeze

View file

@ -150,6 +150,17 @@ class BingoTest < Minitest::Test
assert_includes last_response.body, 'hackyourmom</a>'
end
def test_policy_page_in_every_locale_and_footer
{ 'en' => 'never advertised', 'ru' => 'никогда не рекламируется', 'uk' => 'ніколи не рекламується' }.each do |l, text|
get "/#{l}/policy"
assert last_response.ok?
assert_includes last_response.body, text
assert_includes last_response.body, '<a href="https://asxp.io/contact">asxp.io/contact</a>'
end
get '/en'
assert_includes last_response.body, '<a href="/en/policy">No-spam policy</a>'
end
def test_robots_and_sitemap_are_the_bingos_own
get '/robots.txt'
assert last_response.ok?
@ -161,7 +172,8 @@ class BingoTest < Minitest::Test
%w[ru en uk].each { |l| assert_includes last_response.body, "<loc>https://example.org/#{l}/dc/dc-25</loc>" }
assert_includes last_response.body, '<xhtml:link rel="alternate" hreflang="uk" href="https://example.org/uk/dc/dc-7"/>'
%w[ru en uk].each { |l| assert_includes last_response.body, "<loc>https://example.org/#{l}/status</loc>" }
assert_equal 6, last_response.body.scan('hreflang="x-default"').size # card and status URLs only
%w[ru en uk].each { |l| assert_includes last_response.body, "<loc>https://example.org/#{l}/policy</loc>" }
assert_equal 9, last_response.body.scan('hreflang="x-default"').size # card, status and policy URLs only
assert_includes last_response.body, 'hreflang="x-default"'
refute_includes last_response.body, 'changefreq'
assert_match %r{</loc>\n <lastmod>\d{4}-\d\d-\d\dT\d\d:\d\d:\d\dZ</lastmod>\n <xhtml:link}, last_response.body

View file

@ -2,6 +2,6 @@
<p><%= t(:footer) %></p>
<p class="credits"><%= t(:credits_ai) %>
<%== format(Rack::Utils.escape_html(t(:credits_hym)), link: '<a href="https://hackyourmom.com/en/karta-data-czentriv-na-rasii/" target="_blank" rel="noopener">hackyourmom</a>') %></p>
<p><a href="/<%= @locale %>/feed.xml">RSS</a> · <a href="/<%= @locale %>/feed.json">JSON Feed</a> · <a href="/data.json"><%= t(:open_data) %></a> · <a href="/<%= @locale %>/status"><%= t(:status_link) %></a></p>
<p><a href="/<%= @locale %>/feed.xml">RSS</a> · <a href="/<%= @locale %>/feed.json">JSON Feed</a> · <a href="/data.json"><%= t(:open_data) %></a> · <a href="/<%= @locale %>/status"><%= t(:status_link) %></a> · <a href="/<%= @locale %>/policy"><%= t(:policy_link) %></a></p>
<p>© <%= Date.today.year %> <a href="https://asxp.io">asxp.io</a> · <a href="<%= BingoWeb::SOURCE_URL %>" rel="noopener"><%= t(:source_code) %></a></p>
</footer>

17
views/policy.erb Normal file
View file

@ -0,0 +1,17 @@
<main>
<header>
<h1><%= t(:policy_link) %></h1>
<nav class="langs">
<% Bingo::LOCALES.each do |l| %>
<a href="<%= @path_for.(l) %>" hreflang="<%= l %>" lang="<%= l %>" <%== 'aria-current="page"' if l == @locale %>><%= l %></a>
<% end %>
</nav>
</header>
<nav class="crumbs"><a href="/<%= @locale %>"><%= t(:back) %></a></nav>
<% t(:policy_text).each do |para| %>
<p class="lead"><%== format(Rack::Utils.escape_html(para), link: '<a href="https://asxp.io/contact">asxp.io/contact</a>') %></p>
<% end %>
<%== erb :_footer, layout: false %>
</main>