feat(profile): profile pages on the account domain, and links verified by rel="me"

A profile now has a page on the web, at the root of the account domain
(https://halkyon.eu/@raisondetredev where the operator set ACCOUNT_DOMAIN,
else on the instance's web server), so the address belongs to the
identity rather than to Learn and stays when other services share it
(ADR 0011). The page shows what the app's profile sheet shows, in English
or German, without JavaScript or cookies, asks search engines not to list
it, marks every link rel="me" so other sites can verify links to it, and
sends a visitor who asked on another host to its own address. Caddy
forwards /@… to the web server, and serves nothing else on the account
domain; compose now passes ACCOUNT_DOMAIN on, which it never did.

A link is verified as Mastodon verifies one: its page links back to the
profile's with rel="me". profile.checkLinks fetches each link's page, at
most ten times an hour, over HTTPS on port 443 and to public addresses
only, connecting to the address it checked and verifying the certificate
for the host, with three redirects, 1 MiB and ten seconds at most, and
keeps what links back on the profile (verifiedLinks, a column of a table
no device syncs). A changed link loses its check, a new handle all of
them. The app shows the page's address to copy, checks a link saved by
itself and on Check links, and marks verified links in the form and in
others' profiles; a check answered after a newer save only adds its
checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-15 20:29:30 +02:00
commit e8ddb8b950
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -1899,6 +1899,9 @@ class AppLocalizationsDe extends AppLocalizations {
String get profileSheetNotFound =>
'Niemand auf dieser Instanz hat diesen Handle.';
@override
String get profileSheetLinkVerified => 'Verlinkt zurück auf dieses Profil';
@override
String get profileSheetUnreachable =>
'Das Profil konnte nicht geladen werden. Prüfe deine Verbindung.';
@ -1949,6 +1952,39 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get publicProfileAddLink => 'Link hinzufügen';
@override
String publicProfilePage(String url) {
return 'Deine Profilseite: $url';
}
@override
String get publicProfilePageCopy => 'Adresse kopieren';
@override
String get publicProfilePageCopied => 'Adresse kopiert.';
@override
String get publicProfileVerifyHint =>
'Ein Link bekommt ein Häkchen, sobald seine Seite mit rel=\"me\" auf deine Profilseite zurückverlinkt, wie Mastodon Links prüft.';
@override
String get publicProfileCheckLinks => 'Links prüfen';
@override
String get publicProfileLinkVerified => 'Verlinkt zurück auf dein Profil';
@override
String publicProfileLinksChecked(int count, int verified) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other:
'Geprüft: $verified von $count Links verlinken zurück auf dein Profil.',
one: 'Geprüft: $verified von 1 Link verlinkt zurück auf dein Profil.',
);
return '$_temp0';
}
@override
String get publicProfileRemoveLink => 'Link entfernen';
@ -2048,6 +2084,10 @@ class AppLocalizationsDe extends AppLocalizations {
String get profileProblemAvatarInvalid =>
'Dieses Bild lässt sich nicht verwenden: Wähle ein PNG-, JPEG-, WebP- oder GIF-Bild.';
@override
String get profileProblemTooManyLinkChecks =>
'Die Links wurden zu oft geprüft. Versuche es in einer Stunde noch einmal.';
@override
String get publicProfilePicture => 'Bild';