refactor(authn): one Principal.CanRead() for the read-plane ACL (spec-ejq.1) graph's gate, web's mayRead and mcpsrv's Gate each hand-spelled 'IsOwner() || IsAgent()' — three copies of the read ACL, which graph's own comment warned is how a corpus leaks when they drift. Define it once as authn.Principal.CanRead and route all three through it. coreauth.Derive keeps its own owner||agent test on purpose: it answers a different question (is this an owner-backed identity to bridge to AUTH_INTERNAL), and coupling it to the read ACL would misroute a future read-only viewer kind to the owner's UserID. Closes spec-ejq.1
feat(authn): unified-login identity, agent token validation, provenance trailers Cookie identity is fail-open to anonymous: a missing, tampered or foreign-key cookie yields no user rather than an error, so anonymous browsing never breaks on a bad cookie. Agent tokens are the opposite. A bad bearer is a permanent 401, a store outage a transient 503, and revoked rows are returned so operators can distinguish a killed token from one that never existed. TokenStore is declared here rather than imported from db/, so the two packages can be built in parallel and service/ wires them together.