S3: URL-input adapter, canonicalize all call sites, route contacts through inbox.Cache #13
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
posta/server#13
Loading…
Add table
Add a link
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?
Parent
posta/server#10 — Absorb spec §4.1/§4.2 canonicalization and §9 key-management simplification
What to build
The full canonicalization wave on the input side: build the deep URL-input
adapter module, migrate every site that today calls
posta.NormalizeURLto it,and replace the contacts handler's bespoke actor-doc fetch with the inbox's
shared
ActorCache. After this slice, the server has exactly one seam where"what the user typed" becomes a canonical URL, and exactly one cache that fronts
peer actor-doc fetches.
End-to-end behaviour after this slice:
POST /api/v1/contactswithbody.url = "alice.example/inbox/"succeeds,stores
https://alice.example/inbox, and the response body rendersalice.example/inboxas display form.POST /api/v1/contactswithbody.url = "http://x.example"returns400with body field
error = "non-https-scheme". Every §4.1 rejection category(
non-https-scheme,userinfo-present,ip-literal-host,malformed-host,malformed-port,malformed-path,query-present,fragment-present) isreachable and tested.
POST /api/v1/messagescanonicalizes the peer URL before enqueuing, so therecipientfield on the wire envelope is canonical regardless of how theclient typed it.
internal/inbox/ratelimit.go) keys on thecanonical sender URL — two clients hitting the inbox with trailing-slash
variants share a bucket.
posta-server identity add --url alice.example(display form, no scheme)prepends
https://, canonicalizes, and writes the canonical URL to themanifest.
internal/api/contacts.go fetchActorNameis gone. The contacts handlerresolves a peer's display name via the inbox's shared
ActorCache(througha thin new helper such as
FetchDoc(senderURL)on the cache or inbox seam).Two contact creations within the actor cache's positive TTL trigger exactly
one upstream HTTPS GET.
posta.NormalizeURLanymore.Acceptance criteria
URL-input adapter (the deep module):
text (with or without
https://) and returns either the canonical URLor a typed rejection-category value; a
DisplayForm(canonical) stringthat returns the §4.2 display form.
https://beforedelegating to
posta.Canonicalize.SPEC §4.1 (
non-https-scheme,userinfo-present,ip-literal-host,malformed-host,malformed-port,malformed-path,query-present,fragment-present).posta/spec/testdata/vectors/url-canonical/and asserts the listedcanonicalfor accept vectors and the listed reject category forreject vectors. Display-form round-trip is bijective for accept vectors.
Call-site migration:
internal/api/contacts.gouses the adapter on every incoming URL(contact create, contact patch, read-watermark). On rejection, the
handler returns
400with the category string in the response body.internal/api/messages.gouses the adapter on the peer field ofoutbound message creation. Wire
recipientis canonical.internal/inbox/ratelimit.gokeys its per-sender bucket on thecanonical sender URL.
internal/api/search.go(and any other URL-bearing endpoint) ismigrated.
cmd/posta-server/identity.go(theidentity add --urlflag and anyother CLI URL inputs) accept display form and store canonical.
GET/POST /api/v1/contactsrender the contact'surlfield in §4.2 display form. (Storage stays canonical.)posta.NormalizeURLremains in the server.Contacts via inbox.Cache:
fetchActorNameininternal/api/contacts.gois deleted.Inbox.FetchDoc(senderURL)or a method on
*posta.ActorCache) returns the validated*posta.ActorDocfor a given canonical URL. Contacts uses it.URLEqual(doc.URL, url)check (or its successor after thespec lib tightens
DecodeActorDoc) catches actor-doc URL mismatch.ActorFetcher, performs twocontact creations for the same peer URL within the cache's positive
TTL, and asserts exactly one upstream call.
General:
go build ./...andgo test ./...pass.Blocked by
posta/specshippingCanonicalize()and a typed rejection-category error inpkg/posta. The precondition is documented inposta/spec/TODO.md.Precondition resolved.
posta/speccommit5aa3aa3landsCanonicalize(s) (string, error),DisplayForm(canonical) string, and*CanonicalizeError{Category CanonicalizeReject}whose category strings are exactly the eight values listed in the acceptance criteria.DecodeActorDocnow rejects non-canonicalurl.5c19573updated the spec's integration tests.The server's
go.modalready usesreplace … => ../spec, so no version bump is needed.Implementation notes for the agent:
https://when the input has no scheme, callposta.Canonicalize, type-assert*posta.CanonicalizeErrorto surfaceerr.Categoryto HTTP-400 responses.DisplayFormis bijective with canonical for accept vectors — use it for response payload rendering onGET/POST /api/v1/contacts.../spec/testdata/vectors/url-canonical/*.jsondirectly (or via ago:embedif you prefer to detach from the relative path).Inbox.Cache: the existing*posta.ActorCachealready hasResolve(senderURL, keyID); you'll likely want aFetchDoc(senderURL)shaped helper that returns the validated*posta.ActorDocwithout needing akeyID. Plumb it throughInbox.Cache()so the contacts handler shares the inbox's actor cache.Category: enhancement
State: ready-for-agent