The checks engine, the path and the cards: me-web? 0.1 reads the connection

MeWeb.Core runs seven checks concurrently and reports each as it settles:
online or behind a login page, this connection, the public IPv4 and IPv6
seen from v4./v6.meweb, the provider from a built-in map, the router
(UPnP IGD, NAT-PMP and PCP, read-only), address sharing (router WAN
against the echo, TTL hops only as a hint), and the history kept on the
device. The app draws the path from this device to the internet and lights
it node by node, with a card per check and a sheet listing every host it
contacts.

The provider map is iptoasn's table compressed to 2.1 MB by
tools/knowledge.cs and built before every app release; eleven Italian
provider profiles carry each fact with who said it, the source and the
month. The server gains a download page (en, it) and /download/{platform}
from the latest Gitea release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-15 04:39:03 +02:00
1 parent 16a1cecaae
commit 0dffbc27d6
61 files changed
+3750 -149

No files matched your search

+20 -1
View File
@@ -15,6 +15,9 @@ dotnet build src/MeWeb.Android/MeWeb.Android.csproj \
dotnet run --project tools/Screenshots -- dist/screenshots [en,it,...]
dotnet run tools/strings.cs -- check # every language against English
dotnet run tools/strings.cs -- set Key values.json # one key, every language at once
dotnet run tools/strings.cs -- merge keys.json # many keys: {"Key": {"en": "…", "it": "…"}}
dotnet run tools/strings.cs -- import fr fr.json # one language's drafts: {"Key": "…"}
dotnet run tools/knowledge.cs # provider map -> artifacts/knowledge/ip2asn.bin
dotnet run tools/languages.cs # regenerate data/languages.json from CLDR
dotnet publish src/MeWeb.Desktop -c Release -r win-x64 --self-contained -p:PublishSingleFile=true \
-p:IncludeNativeLibrariesForSelfExtract=true -p:EnableCompressionInSingleFile=true
@@ -56,17 +59,33 @@ The Android release key: alias `meweb`, certificate SHA-256
| Path | What |
|---|---|
| `src/MeWeb.Shared` | `Endpoints.cs` (routes and hosts), wire contracts, `MeWebJsonContext`, generated `BuildInfo` |
| `src/MeWeb.Core` | the checks, no UI: `Net/` (address kinds, local network, TTL route), `Router/` (UPnP IGD, NAT-PMP, PCP, read-only), `Knowledge/` (provider map and profiles), `History/`, `Checks/` (`CheckRunner`, `Verdicts`, `ServerProbe`) |
| `src/MeWeb.App` | Avalonia views and view models, `Services/Localizer.cs` (+ `{s:Tr Key}`), `Strings/<code>.json`, `Resources/Colors.axaml` |
| `src/MeWeb.Desktop` | desktop head (`meweb` executable), Windows manifest |
| `src/MeWeb.Android` | Android head (`software.arasaka.meweb`), resources, manifest |
| `src/MeWeb.Server` | `meweb.arasaka.software`: `/api/info`, `/api/ip` |
| `src/MeWeb.Server` | `meweb.arasaka.software`: `/api/info`, `/api/ip`, the download page `/` (en, it) and `/download/{platform}` from the latest Gitea release |
| `data/languages.json` | 113 languages: code, autonym, English name, `rtl`, `reviewed` or `draft` |
| `data/isps/*.json` | provider profiles: AS numbers, and per fact a value, who said it (`basis`), the source URL and the month |
| `tools/knowledge.cs` | downloads iptoasn's `ip2asn-combined.tsv.gz` (cached in `tools/cache`, 12 h) and writes the brotli table `artifacts/knowledge/ip2asn.bin` (~2 MB), embedded by `MeWeb.Core` when present; `tools/release.cs` runs it before every app build |
| `tools/setup.cs` + `setup.json` | pinned build tools |
| `tools/Screenshots` | headless renders of every view in every language |
| `branding/` | the icon (SVG source, PNG, ICO) |
## Pitfalls
- **A TTL ping needs an empty payload off-root.** .NET on Linux falls back to the `ping` utility without
`CAP_NET_RAW` and refuses any custom buffer ("Unable to send custom ping payload"); `SendPingAsync(target, timeout, [],
new PingOptions(ttl, false))` works and reports `TimeExceeded` with the hop's address.
- **A 100.64.x.x hop is not proof of CGNAT.** The owner's Dimensione line has a public IPv4 on the FRITZ!Box and still
shows `100.64.255.255` as hop 2. Only the router's own WAN address compared with the echo decides; hops are a hint.
- **Routers answer the SSDP search they don't implement.** The FRITZ!Box 5530 answers an IGD:2 search while describing
itself as IGD:1; the version comes from the description's `deviceType`. Its PCP answers `NOT_AUTHORIZED` (2).
- **`HeadlessUnitTestSession.Dispatch` builds a fresh `Application` each call**, and `AttachDeveloperTools` throws the
second time: the screenshot tool does the real check and every shot inside one `Dispatch`.
- **Fluent's `Expander` paints its header from resources** (`ExpanderHeaderBackground`, …), not from `Background`; the
`Expander.explain` style overrides them in `Style.Resources`.
- **An address inside right-to-left text reorders around `→`.** `Texts.Isolate` wraps every literal value in U+2068/U+2069.
- **Avalonia 12 Android head:** `[Application] MainApplication : AvaloniaAndroidApplication<App>` plus a non-generic
`AvaloniaMainActivity`; the main view comes through `IActivityApplicationLifetime.MainViewFactory`, checked before
`ISingleViewApplicationLifetime`.