Files
Matthew Toussain 823a80f663 feat(server): add principal authorization and tRPC extension seams (#151)
* refactor(ui): share neutral capability contracts across editions

Move SiriusCapability, SiriusPrincipal, the capability snapshot/provider types,
and the Community catalog into a React-free contracts module so the browser
registry and the incoming server registry cannot hold divergent catalogs. The UI
extension modules re-export the same names, so consumers are unchanged.

Pin the Community catalog to documentation/product/edition-boundary.yaml with a
contract test, since that file is the source of truth for the edition boundary.

Co-authored-by: mtoussain <mtoussain@gmail.com>

* feat(ui): add server principal resolver and tRPC extension seam

Mirror the browser extension registry on the server. A SiriusServerExtension
declares tRPC namespaces with their required capabilities, at most one principal
resolver, and optional session enrichers; registered.ts is the build-time
overlay a private build replaces.

protectedProcedure now enforces the capabilities declared for the procedure's
namespace, so a contributed namespace is authorized without patching Core
procedures. Principal resolution fails closed: a resolver that throws yields no
principal instead of inheriting another edition's capability set. Community
resolves any authenticated session to its static catalog, leaving Community
behavior unchanged.

root.ts cross-checks declared namespaces against the composed router in both
directions, so an overlay that ships a router without declaring it (or declares
a namespace it never serves) fails at startup rather than at request time. The
routers stay object literals so tRPC keeps inferring client types.

Co-authored-by: mtoussain <mtoussain@gmail.com>

* test(ui): run extension contract tests in CI

Add the server and capability suites alongside the UI registry suite behind a
single test:extensions script, and make it a blocking CI step so the contracts
private builds overlay are actually guarded.

Co-authored-by: mtoussain <mtoussain@gmail.com>

* docs: record the server-side extension seam in ADR-004 and the task board

Co-authored-by: mtoussain <mtoussain@gmail.com>

* fix(server): allow private principal resolver to replace Community fallback

* test(server): prove private resolver can replace Community fallback

* docs: record resolver replacement semantics and freeze server contracts at v1

Co-authored-by: mtoussain <mtoussain@gmail.com>

* fix(server): split overlay router slot out of the declaration slot

Contributing routers from registered.ts cycles: trpc.ts imports the registry to
enforce capabilities, so an overlay router module importing createTRPCRouter
loads while trpc.ts is still initializing and dies with 'Cannot access
createTRPCRouter before initialization'. This was found by composing a private
extension against the seam.

Routers now come from registered-routers.ts, which only root.ts imports, so they
load after trpc.ts is ready. root.ts still cross-checks declared namespaces
against composed ones, and a contract test keeps the declaration modules free of
tRPC imports.

Co-authored-by: mtoussain <mtoussain@gmail.com>

* docs: document the overlay module split and cycle constraint

Co-authored-by: mtoussain <mtoussain@gmail.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-18 07:57:14 -07:00
..