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

@@ -1,4 +1,4 @@
using MongoDB.Entities;
using MongoDB.Entities;
using PrivaPub.Infrastructure.Geo;
using PrivaPub.Infrastructure.Http;
@@ -18,12 +18,17 @@ namespace PrivaPub.Federation.Objects
readonly IFederationHttp _http;
readonly IGeoLocator _geo;
readonly IConnectedAddresses _addresses;
readonly ICdnRanges _cdnRanges;
readonly IEdgeHints _edges;
public InstanceDescriber(IFederationHttp http, IGeoLocator geo = default, IConnectedAddresses addresses = default)
public InstanceDescriber(IFederationHttp http, IGeoLocator geo = default, IConnectedAddresses addresses = default, ICdnRanges cdnRanges = default,
IEdgeHints edges = default)
{
_http = http;
_geo = geo;
_addresses = addresses;
_cdnRanges = cdnRanges;
_edges = edges;
}
public JobKind Kind => JobKind.DescribeInstance;
@@ -145,35 +150,57 @@ namespace PrivaPub.Federation.Objects
.Modify(s => s.RegistrationMode, instance?.RegistrationMode)
.Modify(s => s.Languages, instance?.Languages)
.Modify(s => s.Country, instance?.Geo?.Country)
.Modify(s => s.City, instance?.Geo?.City)
.Modify(s => s.Latitude, instance?.Geo?.Latitude)
.Modify(s => s.Longitude, instance?.Geo?.Longitude)
.Modify(s => s.Asn, instance?.Geo?.Asn)
.Modify(s => s.Cdn, instance?.Geo?.Cdn)
.Modify(s => s.CdnDomain, instance?.Geo?.CdnDomain)
.Modify(s => s.CdnSource, instance?.Geo?.CdnSource)
.Modify(s => s.Ipv6, instance?.Geo?.Ipv6)
.Option(o => o.IsUpsert = true)
.ExecuteAsync(token);
return facts != default;
}
// Where the address we reached sits, and the CDN in front of it, found by its published ranges, then by the
// fingerprint in the responses we just got, then by a network that carries only a CDN.
InstanceGeo Locate(string host)
{
var reached = _addresses?.Of(new Uri(Address($"https://{host}/")).Host);
var reachedHost = new Uri(Address($"https://{host}/")).Host;
var reached = _addresses?.Of(reachedHost);
var fix = reached == default ? default : _geo?.Locate(reached);
if (fix == default)
var (cdn, cdnSource) = Cdn(reached, reachedHost, fix?.Asn, _cdnRanges, _edges);
if (fix == default && cdn == default)
return default;
return new InstanceGeo
{
Country = fix.Country,
City = fix.City,
Latitude = fix.Latitude,
Longitude = fix.Longitude,
Asn = fix.Asn,
AsnOrg = fix.AsnOrg,
Cdn = CdnNetworks.Of(fix.Asn),
Ipv6 = reached.AddressFamily == AddressFamily.InterNetworkV6,
Source = _geo.Source,
Country = fix?.Country,
City = fix?.City,
Latitude = fix?.Latitude,
Longitude = fix?.Longitude,
Asn = fix?.Asn,
AsnOrg = fix?.AsnOrg,
Cdn = cdn?.Name,
CdnDomain = cdn?.Domain,
CdnSource = cdnSource,
Ipv6 = reached?.AddressFamily == AddressFamily.InterNetworkV6,
Source = _geo?.Source,
LocatedAt = DateTime.UtcNow
};
}
public static (CdnProvider Cdn, string Source) Cdn(System.Net.IPAddress address, string host, int? asn, ICdnRanges ranges, IEdgeHints edges)
{
if (ranges?.Of(address) is { } listed)
return (listed, "ranges");
if (edges?.Of(host) is { } hinted)
return (hinted, "headers");
if (CdnCatalog.OfAsn(asn) is { } network)
return (network, "asn");
return default;
}
//where an https address is fetched from: itself, except in tests, which point it at a plain-http peer
protected virtual string Address(string url) => url;
}