S2: Canonicalize manifest URLs and propagate canonical form into the inbox #12

Open
opened 2026-05-12 20:26:28 +02:00 by arne · 1 comment
Owner

Parent

posta/server#10 — Absorb spec §4.1/§4.2 canonicalization and §9 key-management simplification

What to build

Make the manifest the first stop where every identity URL becomes §4.1 canonical,
then propagate that canonical string into every consumer (host dispatch, inbox
opts.URL, runner-side actor-doc seeding). After this slice the daemon has
exactly one notion of "the canonical URL for this identity" and one place where
§4.1 rejection categories surface to the operator.

End-to-end behaviour after this slice:

  • An operator's manifest entry with https://Alice.Posta.NO:443/inbox/ is
    accepted, canonicalized to https://alice.example/inbox (or equivalent per
    §4.1), and the daemon dispatches r.Host of alice.posta.no to the right
    identity runner.
  • A manifest entry with http://x.example, https://x@example.com,
    https://198.51.100.1, https://example.com?a=1, or https://example.com#x
    is rejected at boot with an error message naming the §4.1 rejection category.
  • The inbox's seeded actor doc carries the canonical URL, so the spec library's
    tightened DecodeActorDoc (once it lands per the §4.1 precondition) accepts it.
  • The §7.2 recipient match inside posta.NewHandler compares two canonical
    strings byte-for-byte — no parallel normalizer in the middle.

This slice removes internal/daemon.IdentityHost as a separate normalizer.

Acceptance criteria

  • internal/daemon/manifest.go calls posta.Canonicalize on each identity's
    URL during LoadManifest / Validate. Rejections are surfaced with the
    §4.1 category in the error message.
  • daemon.IdentityHost is removed (or reduced to a trivial helper that
    takes a canonical URL and returns host[:port]). No bespoke
    lowercasing / port-stripping rules remain.
  • The daemon's per-identity dispatch map is keyed on the canonical-derived
    host so r.Host matches reliably across the §4.1-allowed variations.
  • internal/daemon/runner.go passes the canonical URL into
    inbox.Options.URL (and any other consumer that today receives the raw
    manifest URL).
  • internal/daemon/manifest_test.go is updated: existing table cases
    asserting https://Arne.Posta.NO:8443 → arne.posta.no:8443-style host
    derivation now assert the §4.1 canonical-then-host output. New cases
    cover at least: trailing slash strip, IDN U-label, userinfo rejection,
    query/fragment rejection.
  • No call site in the server uses posta.NormalizeURL for manifest /
    identity / daemon-host purposes.
  • go build ./... and go test ./... pass.

Blocked by

posta/spec shipping Canonicalize() and a typed rejection-category error in
pkg/posta. The precondition is documented in posta/spec/TODO.md rather
than tracked as its own issue.

## Parent posta/server#10 — Absorb spec §4.1/§4.2 canonicalization and §9 key-management simplification ## What to build Make the manifest the first stop where every identity URL becomes §4.1 canonical, then propagate that canonical string into every consumer (host dispatch, inbox `opts.URL`, runner-side actor-doc seeding). After this slice the daemon has exactly one notion of "the canonical URL for this identity" and one place where §4.1 rejection categories surface to the operator. End-to-end behaviour after this slice: - An operator's manifest entry with `https://Alice.Posta.NO:443/inbox/` is accepted, canonicalized to `https://alice.example/inbox` (or equivalent per §4.1), and the daemon dispatches `r.Host` of `alice.posta.no` to the right identity runner. - A manifest entry with `http://x.example`, `https://x@example.com`, `https://198.51.100.1`, `https://example.com?a=1`, or `https://example.com#x` is rejected at boot with an error message naming the §4.1 rejection category. - The inbox's seeded actor doc carries the canonical URL, so the spec library's tightened `DecodeActorDoc` (once it lands per the §4.1 precondition) accepts it. - The §7.2 `recipient` match inside `posta.NewHandler` compares two canonical strings byte-for-byte — no parallel normalizer in the middle. This slice removes `internal/daemon.IdentityHost` as a separate normalizer. ## Acceptance criteria - [ ] `internal/daemon/manifest.go` calls `posta.Canonicalize` on each identity's URL during `LoadManifest` / `Validate`. Rejections are surfaced with the §4.1 category in the error message. - [ ] `daemon.IdentityHost` is removed (or reduced to a trivial helper that takes a canonical URL and returns `host[:port]`). No bespoke lowercasing / port-stripping rules remain. - [ ] The daemon's per-identity dispatch map is keyed on the canonical-derived host so `r.Host` matches reliably across the §4.1-allowed variations. - [ ] `internal/daemon/runner.go` passes the canonical URL into `inbox.Options.URL` (and any other consumer that today receives the raw manifest URL). - [ ] `internal/daemon/manifest_test.go` is updated: existing table cases asserting `https://Arne.Posta.NO:8443 → arne.posta.no:8443`-style host derivation now assert the §4.1 canonical-then-host output. New cases cover at least: trailing slash strip, IDN U-label, userinfo rejection, query/fragment rejection. - [ ] No call site in the server uses `posta.NormalizeURL` for manifest / identity / daemon-host purposes. - [ ] `go build ./...` and `go test ./...` pass. ## Blocked by `posta/spec` shipping `Canonicalize()` and a typed rejection-category error in `pkg/posta`. The precondition is documented in `posta/spec/TODO.md` rather than tracked as its own issue.
Author
Owner

This was generated by AI during triage.

Precondition resolved. The spec library landed Canonicalize(s) (string, error) + *CanonicalizeError{Category CanonicalizeReject} in posta/spec commit 5aa3aa3 ("implement SPEC §4.1 URL canonicalization in pkg/posta"), with all 25 conformance vectors passing, and tightened DecodeActorDoc to reject non-canonical url. posta/spec commit 5c19573 realigned the integration tests.

The server's go.mod already uses replace code.bas.es/posta/spec => ../spec, so the new API is reachable without a version bump.

Acceptance criteria above are unchanged. The agent should call posta.Canonicalize on each manifest URL during LoadManifest/Validate, surface *posta.CanonicalizeError's Category in error messages, and key the dispatch map on host[:port] derived from the canonical string.

Category: enhancement
State: ready-for-agent

> *This was generated by AI during triage.* Precondition resolved. The spec library landed `Canonicalize(s) (string, error)` + `*CanonicalizeError{Category CanonicalizeReject}` in `posta/spec` commit `5aa3aa3` ("implement SPEC §4.1 URL canonicalization in pkg/posta"), with all 25 conformance vectors passing, and tightened `DecodeActorDoc` to reject non-canonical `url`. `posta/spec` commit `5c19573` realigned the integration tests. The server's `go.mod` already uses `replace code.bas.es/posta/spec => ../spec`, so the new API is reachable without a version bump. Acceptance criteria above are unchanged. The agent should call `posta.Canonicalize` on each manifest URL during `LoadManifest`/`Validate`, surface `*posta.CanonicalizeError`'s `Category` in error messages, and key the dispatch map on `host[:port]` derived from the canonical string. **Category:** enhancement **State:** ready-for-agent
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
posta/server#12
No description provided.