CDN-fronted servers grouped per CDN, and servers through time
Build / Build (push) Successful in 26s
Deploy / decepub.thepra.dev (push) Successful in 1m21s

A post whose server is behind a CDN has no place of its own: the globe
groups such posts per CDN in labels on a ring above the Arctic, and
hovering one draws its arc to the CDN's group and a ring where the
server was before the CDN. The card's CDN chip names the CDN, its
domain, how PrivaPub found it (published ranges, response headers or
network), the place before the CDN and the CDN's servers week by week
(/api/privapub/v1/cdns/:domain); the author's tooltip lists the server's
changes of place and CDN week by week (/instances/:host/history). Both
are read on the first hover and kept for the session.

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:44:51 +02:00
1 parent 5ec70b3357
commit 620cbef219
12 files changed
+386 -41

No files matched your search

+5 -1
View File
@@ -1,4 +1,4 @@
namespace decePubClient.Models;
namespace decePubClient.Models;
/// <summary>
/// Every PrivaPub route the client calls, relative to AppConfiguration.ApiBaseAddress (b2b.next's naming: the HTTP
@@ -59,6 +59,10 @@ public static class APIs
public static class PrivaPub
{
public const string GET_Instances = "api/privapub/v1/instances";
public const string GET_Cdns = "api/privapub/v1/cdns";
public static string GET_M_InstanceHistory(string host) => $"api/privapub/v1/instances/{Uri.EscapeDataString(host)}/history";
public static string GET_M_Cdn(string key) => $"api/privapub/v1/cdns/{Uri.EscapeDataString(key)}";
public static string GET_M_Provenance(string statusId) => $"api/privapub/v1/statuses/{Uri.EscapeDataString(statusId)}/provenance";
}
+13 -3
View File
@@ -1,4 +1,4 @@
using decePubClient.Models.Mastodon;
using decePubClient.Models.Mastodon;
namespace decePubClient.Models;
@@ -65,10 +65,20 @@ public enum LocationSource
/// <summary>The author's server, known only by country: a point in that country.</summary>
Country,
/// <summary>A located post, within its range of the reader: drawn at the reader.</summary>
NearYou
NearYou,
/// <summary>The author's server is behind a CDN, whose address says nothing of its place: drawn with the CDN's group.</summary>
Cdn
}
public sealed record FeedLocation(double Latitude, double Longitude, LocationSource Source, string Label, double PrecisionKm);
/// <summary>
/// Where a feed item is drawn. For a CDN-fronted server (Source Cdn) Latitude/Longitude mean nothing: the globe groups it
/// under CdnKey, and Before is where the server was before the CDN, when PrivaPub saw it.
/// </summary>
public sealed record FeedLocation(double Latitude, double Longitude, LocationSource Source, string Label, double PrecisionKm, string CdnKey = default,
InstancePlace Before = default)
{
public bool HasPoint => Source is not LocationSource.Cdn;
}
/// <summary>What the client keeps about a server (from /api/privapub/v1/instances), cached for a day.</summary>
public class ServerInfo
+60 -3
View File
@@ -1,4 +1,4 @@
using System.Text.Json.Nodes;
using System.Text.Json.Nodes;
namespace decePubClient.Models.Mastodon;
@@ -27,8 +27,8 @@ public class InstanceDelivery
}
/// <summary>
/// The public projection of a server's place. Precision: city (coordinates to 0.1°, city and network), country (the
/// country only) or cdn (the CDN's name only).
/// The public projection of a server's place. Precision: city (coordinates to 0.1°, city and network), country (located
/// only to a country) or cdn (the CDN's name, its domain, how PrivaPub found it, and where the server was before it).
/// </summary>
public class InstanceLocation
{
@@ -39,10 +39,67 @@ public class InstanceLocation
public double? Longitude { get; set; }
public string Network { get; set; }
public string Cdn { get; set; }
public string CdnDomain { get; set; }
/// <summary>ranges (the CDN's published addresses), headers (its fingerprint in the server's answers) or asn.</summary>
public string CdnSource { get; set; }
public InstancePlace BeforeCdn { get; set; }
public string Source { get; set; }
public DateTime? LocatedAt { get; set; }
}
/// <summary>Where a server was in a week (an ISO week, "2026-W40").</summary>
public class InstancePlace
{
public string Week { get; set; }
public string Country { get; set; }
public string City { get; set; }
public double? Latitude { get; set; }
public double? Longitude { get; set; }
}
/// <summary>/api/privapub/v1/instances/{host}/history: one weekly snapshot of a server.</summary>
public class InstanceWeek
{
public string Week { get; set; }
public DateTime? TakenAt { get; set; }
public bool Reachable { get; set; }
public string Software { get; set; }
public string Version { get; set; }
public long? UsersTotal { get; set; }
public long? UsersActiveMonth { get; set; }
public long? LocalPosts { get; set; }
public bool? OpenRegistrations { get; set; }
public InstanceLocation Geo { get; set; }
}
/// <summary>/api/privapub/v1/cdns: a CDN as a group of servers, named by its own domain (Key).</summary>
public class CdnSummary
{
public string Key { get; set; }
public string Name { get; set; }
public string Domain { get; set; }
public int Servers { get; set; }
public int Ranges { get; set; }
public DateTime? RangesUpdatedAt { get; set; }
public List<string> DetectedBy { get; set; } = [];
}
/// <summary>/api/privapub/v1/cdns/{key}: the servers behind a CDN and, newest first, week by week who joined and left.</summary>
public class CdnDetail : CdnSummary
{
public List<string> Hosts { get; set; } = [];
public bool HostsTruncated { get; set; }
public List<CdnWeek> Weeks { get; set; } = [];
}
public class CdnWeek
{
public string Week { get; set; }
public int Servers { get; set; }
public List<string> Joined { get; set; } = [];
public List<string> Left { get; set; } = [];
}
/// <summary>/api/privapub/v1/statuses/{id}/provenance: how a remote post reached PrivaPub. Path is local for a local post.</summary>
public class Provenance
{