registry: verified domains (DNS TXT check and badge) #341
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
Depends on
#343 registry: re-check verified domains and withdraw lost ones
torbscript/language
#19 registry: "elsewhere" owners and the signed owner file
torbscript/language
Reference
torbscript/language#341
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
RELEASE.md section 7.1: an organisation proves a domain with a DNS
TXTrecord at_torb.<domain>; the registry checks it at creation and shows a badge ("pub.dev's verified publisher without Google's console"). Split from #19; builds on its owner file and endpoint pattern.What exists today (main
787f63f)std/network'sResolver/lookup(UDP with TCP fallback, system servers or a given list) andstd/dns'sRecordType.Txt/RecordData.Txt(strings)exist and are the means; the registry has no DNS code and no variable for name servers.index/<owner>/owner.trbwith an unuseddomain:field;OwnerView.domain.Source
Design record point
7.1 row A verified domain. Open question 9 stays as recommended: the domain is a badge on a plain owner; a dotted owner name is not introduced.
Design (decided by the loop, see the comment of 2026-10-12)
TXTrecord at_torb.<domain>with a stringtorb-owner=<owner>(any one of its strings; several records allowed). The owner name binds it to one owner; nobody who does not control the domain's DNS can create it, so no per-request nonce is needed.PUT /api/1/owners/<owner>/domainwith{domain},DELETEto withdraw. Organisations only, roleowner, token actionowners,Torb-Code(proved), as for #19's endpoint. The lookup runs in the request; success stores the domain, rewritesowner.trb(domain:) and the badge pages; failure stores nothing and the422says what was found (no record / record for another owner / lookup failed).std/dnsDomainName(IDNA to the ASCII form, stored lowercase), at least two labels, no IP literal, at most 253 bytes. One owner per domain (UNIQUE; another owner's claim is a409); an owner may hold several domains (at most 5).Resolverwith the system's servers by default;REGISTRY_DNS_SERVERS(comma separatedip[:port]) overrides it for tests and deployments. One lookup per claim, the resolver's default timeouts, never more than 2 lookups running at once. DNSSEC is out (DNS.md); the limits of an unsigned answer go into the 7.6 text.packages/<owner>/<name>/index.html,src/packages.trb) shows "verified: acme.com" beside the owner when the owner has a domain, escaped, no new script;GET /api/1/owners/<owner>showsdomains.Acceptance criteria
PUT/DELETE /api/1/owners/<owner>/domainas designed, against a fake name server on loopback (REGISTRY_DNS_SERVERS) intools/registry/tests: verified, no record, record for another owner, name error, no answer, domain of another owner (409), IP literal and one-label names (400), person owner (400), non-owner member (403), missing codeOwnerView, in the signedowner.trb(domain:, signature still verifies) and as the badge on the package page of every package of the owner; a package page of an unverified owner is unchangedtools/registry/README.md(API,REGISTRY_DNS_SERVERS, the record's exact form), RELEASE.md 7.1/7.11 ("as built" note, "Not built yet" updated) and generatedskills/in step;sh tools/gates.sh agreen in forge CISeed impact
No. (Uses existing
std/networkandstd/dns; if the lookup shows a defect in them, that is a separate issue.)Size
One PR, two Conventional Commits: (1)
feat(registry): resolver setting, check, endpoints, storage, owner file; (2)feat(registry): badge on the package page, docs. Worker: opus/high.Dependencies
Needs #19 (owner file with
domain:, endpoint pattern,OwnerView). Definition of ready is met; the labelreadyfollows when #19 has landed.[loop:torbscript] refine: split from #19 (see the decision comment there). Definition of ready met in content (state of main, design record point, testable criteria, seed impact: no, size); depends on #19, so the label
readyfollows when it has landed, next: waiting (depends on #19)[loop:torbscript] refine: dependency #19 landed (PR #347), label
readyset, next: implement[loop:torbscript] claim implement by session:loop-torbscript-20261012 until 2026-10-13T04:05Z
[loop:torbscript] implement: the worker ended on a session limit after opening PR #355 (head
fd737ff3); all required forge CI jobs are green, review of the merge gate requested, next: waiting (merge gate review of PR #355)[loop:torbscript] PR #355 merged by argus (2026-10-13 03:43Z, head
fd737ff3, all gates green). No After-merge steps in the PR; issue closed by the merge. Non-blocking review notes: (1)claimDomainconsumes the Torb-Code before the lookup, so a failed lookup (422) burns a one-time code; (2)foo.local/foo.internalpass the non-IP check. Both harmless for the badge; candidates for a follow-up if the loop picks them up.