5.7 KiB
5.7 KiB
Route Access Matrix
Date: 2026-03-30
Purpose: Make high-sensitivity API audiences explicit and reduce ambiguous protectedProcedure usage on broad read routes.
Audience Classes
self-service: authenticated users can only read or mutate data that belongs to their linked resource or accountauthenticated-safe-lookup: authenticated users can access a deliberately narrow, identity-safe lookup surfaceresource-overview: users withviewAllResourcesormanageResourcesplanning-read: users withviewPlanningcontroller-finance: controller, manager, or admin throughcontrollerProceduremanager-write: manager or admin throughmanagerProcedureadmin-only: admin throughadminProcedure
Current Classification
packages/api/src/router/resource.ts
getMyResource:self-servicegetById,getByEid,getHoverCard,getByIdentifier,getByIdentifierDetail,resolveByIdentifier,getChargeabilitySummary:self-serviceunless the caller also hasresource-overviewdirectory:authenticated-safe-lookuplistSummaries,listSummariesDetail,listStaff,resolveResponsiblePersonName:resource-overviewgetSkillsAnalytics,searchBySkills,listWithUtilization,getChargeabilityStats,getSkillMarketplace:controller-finance- create, update, deactivate, batch update, imports for other users:
manager-writeoradmin-only
packages/api/src/router/project.ts
resolveByIdentifier,searchSummaries,getByIdentifier:planning-readsearchSummariesDetail,list,getById,getByIdentifierDetail,getShoringRatio,listWithCosts:controller-finance- create, update, status changes, cover mutations:
manager-write - delete and batch delete:
admin-only isImageGenConfigured,isDalleConfigured: authenticated low-risk configuration checks
packages/api/src/router/timeline.ts
getMyEntriesView,getMyHolidayOverlays:self-service- timeline-wide planning reads and shift previews:
controller-finance - allocation updates, quick-assign, project shifts:
manager-write
packages/api/src/router/allocation.ts
list,listView,listDemands,listAssignments,getAssignmentById,resolveAssignment,getDemandRequirementById,checkResourceAvailability,getResourceAvailabilityView,getResourceAvailabilitySummary:planning-read- mutations already sit behind
manager-write
packages/api/src/router/dashboard.ts
- all current routes are
controller-finance
packages/api/src/router/role.ts
resolveByIdentifier:authenticated-safe-lookuplist,getByIdentifier,getById:planning-read- create, update, delete:
manager-write
Reasoning:
resolveByIdentifierreturns a narrow lookup shape without planning countslist,getByIdentifier, andgetByIdattach planning-linked usage counts, so they must not remain broadprotectedProcedurereads
packages/api/src/router/scenario.ts
getProjectBaseline:planning-readplus explicitviewCosts
Reasoning:
- the route combines staffing baseline data with commercial totals, so both planning and cost audiences are required
packages/api/src/router/estimate.ts
list:controller-finance- drafting, versioning, export generation, and approval writes:
manager-write
packages/api/src/router/system-role-config.ts
- all reads and writes:
admin-only
Reasoning:
- system role defaults define the effective permission model and therefore belong to the smallest operational audience
packages/api/src/router/country.ts
list,resolveByIdentifier,getCityById:authenticated-safe-lookupgetByIdentifier,getById:resource-overview- create, update, metro-city writes:
admin-only
Reasoning:
- minimal country lookups are needed broadly for forms, filters, and location resolution
- detailed country reads include metro-city detail plus
_count.resources, so they should align with broad people-directory visibility
packages/api/src/router/org-unit.ts
list,getTree,resolveByIdentifier:authenticated-safe-lookupgetByIdentifier,getById:resource-overview- create, update, deactivate:
admin-only
Reasoning:
- minimal org-unit lookups are low-risk master data
- detailed org-unit reads expose
_count.resourcesand parent/child context that maps the staffing structure
Assistant Parity Rule
- assistant tool visibility must never widen the audience of the backing router
- router audience is the source of truth; assistant gating mirrors it
- when a route becomes narrower, update assistant visibility in the same hardening slice
search_resourcesmust followresourceOverviewProcedure, not broad authenticated accesssearch_by_skillmust followcontrollerProcedure, not broad authenticated or planning-only access- if
assistant-tools.tsalready has unrelated local edits, prefer updatingpackages/api/src/router/assistant.tsand parity tests first instead of mixing concerns into the tool implementation file
Rollout Discipline
For audience-scoping changes, use this order:
- narrow the backing router procedure first
- add or tighten authorization tests on the router
- align assistant visibility in
packages/api/src/router/assistant.ts - update assistant parity tests
- ship in small isolated commits so regressions can be reverted without undoing unrelated hardening
Immediate Follow-Ups
- monitor whether
viewPlanningshould later split into narrower project-read vs allocation-read audiences - split
allocationfurther into narrower future audiences where resource-capacity and staffing-demand reads diverge - add authorization tests for every route listed above so the matrix is CI-enforced, not just documented