feat(base): handles hidden until their people go public, every answer about a new handle counted (ADR 0020, D5)

A handle's page and profiles.byHandle answer as for a handle nobody has
until its person goes public: a handle fixed by a publish, or a display
name, a bio, a link or an avatar (isPublic). A person's own profile, and
the host's own lookups (personWithHandle), are unchanged.

So every answer about a new handle now tells that an account has it,
and countHandleCheck counts them together on the register, 30 an hour
per person (or attempt) and 60 per network, refused beyond with
ProfileRefusal.tooManyHandleChecks: profile.checkHandle and saving a
profile with a handle new for the person, simpleLogin.checkHandle and
signUp, keys.set with a handle, and Halkyon Learn's inbox.block and
unblock, which look people up by handle. A person's own handle and what
is no handle aren't counted. The SimpleLogin limiters of commit 3 give
way to it, so a network's 60 are shared between people signed in and
people new. The register's checkHandle, saveProfile,
signUpWithSimpleLogin and setFirstKeys carry the visitor's network from
the member that asked.

Halkyon Learn's handle dialog words the new refusal (English, German).

Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-23 22:39:14 +02:00
commit 7eb1961b9f
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -48,9 +48,9 @@ final class _Register implements AccountRegister {
final simpleLoginLinked = <UuidValue>{};
SimpleLoginProof? simpleLoginProof;
/// The handles checked for someone new and what each check says, the
/// sign-ups of people new with SimpleLogin, and the proofs keys were
/// unlocked with, as members bound them.
/// The handles checked (for someone new, or signed in) and what each
/// check says, the sign-ups of people new with SimpleLogin, and the proofs
/// keys were unlocked with, as members bound them.
final handlesChecked = <String>[];
ProfileRefusal? handleRefusal;
final simpleLoginSignUps = <({UuidValue attempt, String handle})>[];
@ -145,9 +145,10 @@ final class _Register implements AccountRegister {
required String displayName,
required String bio,
required List<String> links,
}) => _answer(
'saveProfile',
() => profiles[person] = Profile(
String? network,
}) => _answer('saveProfile', () {
networks.add(network!);
return profiles[person] = Profile(
handle: handle,
address: '$handle@learn.test',
displayName: displayName,
@ -155,15 +156,20 @@ final class _Register implements AccountRegister {
links: links,
pageUrl: 'https://learn.test/@$handle',
handleFixed: false,
),
);
);
});
@override
Future<ProfileRefusal?> checkHandle(
Session session,
UuidValue person,
String handle,
) => _answer('checkHandle', () => null);
String handle, {
required String network,
}) => _answer('checkHandle', () {
networks.add(network);
handlesChecked.add(handle);
return handleRefusal;
});
@override
Future<Profile> checkLinks(Session session, UuidValue person) =>
@ -314,7 +320,9 @@ final class _Register implements AccountRegister {
required UuidValue attempt,
required String handle,
required KeysSetup setup,
required String network,
}) => _answer('signUpWithSimpleLogin', () {
networks.add(network);
final account = const Uuid().v7obj();
add(account);
simpleLoginSignUps.add((attempt: attempt, handle: handle));
@ -334,8 +342,10 @@ final class _Register implements AccountRegister {
required UuidValue person,
required UuidValue attempt,
required KeysSetup setup,
required String network,
String? handle,
}) => _answer('setFirstKeys', () {
networks.add(network);
firstKeys[person] = (attempt: attempt, handle: handle);
});
@ -1333,6 +1343,7 @@ void main() {
final carla = signedUp.authUserId;
expect(register.asked, ['signUpWithSimpleLogin']);
expect(register.networks, hasLength(1));
expect(register.simpleLoginSignUps.single, (
attempt: attempt,
handle: 'carla',
@ -1532,6 +1543,7 @@ void main() {
);
expect(register.asked, ['setFirstKeys']);
expect(register.networks, hasLength(1));
expect(register.firstKeys, {ben: (attempt: attempt, handle: 'ben')});
});
@ -1651,6 +1663,7 @@ void main() {
);
expect(register.profiles[ben]!.toJson(), saved.toJson());
expect(register.networks, hasLength(1));
expect(
(await endpoints.profiles.byHandle(asAnna(), 'ben')).toJson(),
saved.toJson(),
@ -1659,6 +1672,19 @@ void main() {
expect(await PublicProfile.db.count(session), 0);
});
test('have their handles checked at the register, the device '
'network passed on to be counted there', () async {
register.handleRefusal = ProfileRefusal.tooManyHandleChecks;
expect(
await endpoints.profile.checkHandle(asBen(), 'Ben'),
ProfileRefusal.tooManyHandleChecks,
);
expect(register.asked, ['checkHandle']);
expect(register.handlesChecked, ['Ben']);
expect(register.networks, hasLength(1));
});
test("have their page at the register's address, where this "
"server's sends its visitors", () async {
await endpoints.profile.save(asBen(), 'Ben', 'Ben', '', const []);