CDNs found by themselves, and servers followed through time
Build / Build (push) Successful in 5m11s
Deploy / privapub.thepra.dev (push) Successful in 5m48s

PrivaPub now finds CDNs three ways, best first: the address ranges the
CDNs publish (Cloudflare, Fastly, Amazon CloudFront, Bunny, Gcore,
Imperva), downloaded daily by CdnUpdater and kept in CdnRangeSet; the
CDN's fingerprint in the responses it already gets from a server
(EdgeHintsHandler on the federation client); and the networks that carry
only a CDN. The fixed ASN list is gone; ASNs shared with plain hosting
(AWS, DataPacket) no longer hide a server. A server's Geo records the
CDN, its domain and how it was found, and weekly snapshots now keep the
city and coordinates too.

Servers through time (ServerPlaces): /instances/:host/history lists a
server's weekly snapshots, a CDN-fronted server's geo names the CDN's
domain and where the server was before it (before_cdn), and
/api/privapub/v1/cdns and /cdns/:domain group servers by CDN with week
by week who joined and who left. Owner decisions recorded in ROADMAP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-04 11:33:39 +02:00
1 parent 5d7edc4a4c
commit 436f7da464
26 files changed
+1159 -62

No files matched your search

+55
View File
@@ -0,0 +1,55 @@
using PrivaPub.Infrastructure.Geo;
using System.Collections.Concurrent;
namespace PrivaPub.Infrastructure.Http
{
public interface IEdgeHints
{
void Remember(string host, CdnProvider cdn);
CdnProvider Of(string host);
}
// The CDN each remote host's responses came through, as their headers tell it (CdnCatalog fingerprints), for the
// weekly description of the server. Like ConnectedAddresses, memory only: what a host answers is never stored per
// request.
public class EdgeHints : IEdgeHints
{
const int MaxHosts = 50_000;
readonly ConcurrentDictionary<string, CdnProvider> _hints = new(StringComparer.OrdinalIgnoreCase);
public void Remember(string host, CdnProvider cdn)
{
if (string.IsNullOrEmpty(host))
return;
if (cdn == default)
{
_hints.TryRemove(host, out _);
return;
}
if (_hints.Count >= MaxHosts)
_hints.Clear();
_hints[host] = cdn;
}
public CdnProvider Of(string host) => host != default && _hints.TryGetValue(host, out var cdn) ? cdn : default;
}
// On the federation client: reads each response's CDN fingerprint for its host. A response without one forgets the
// host's hint, so a server that leaves its CDN is seen as itself again.
public class EdgeHintsHandler : DelegatingHandler
{
readonly IEdgeHints _hints;
public EdgeHintsHandler(IEdgeHints hints) => _hints = hints;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = await base.SendAsync(request, cancellationToken);
if (request.RequestUri is { } uri && (int)response.StatusCode is >= 200 and < 500)
_hints.Remember(uri.Host, CdnCatalog.FromResponse(response));
return response;
}
}
}