You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Data exports were restricted to Kilo staff while the feature was being built. This removes the is_admin gate so any signed in user can request and download an export of their own data.
The gate came off in three places: the /data-exports page, the user menu entry that links to it, and all six userExports tRPC procedures. Those procedures move from adminProcedure to baseProcedure, which still requires an authenticated session, so the endpoints are open to signed in users rather than public.
The request throttle also goes back to 24 hours. It had been lowered to 5 minutes for pre-launch testing, with a comment saying to restore it before going live, while the message shown to users already said 24 hours. The code and the copy now agree.
Changes
page.tsx: getUserFromAuth({ adminOnly: true }) becomes adminOnly: false. A signed out visitor still gets notFound().
SidebarUserFooter.tsx: the user menu entry renders for everyone. The is_admin field is dropped from the component's local User type, since nothing else used it.
user-exports-router.ts: request, requestOrganization, exportableOrganizations, list, requestDownloadCode and createDownload move to baseProcedure.
user-exports-router.ts: throttle restored from interval '5 minutes' to interval '24 hours', and the TEMPORARY comment removed.
user-exports-router.ts: two comments reworded. One justified this router doing its own membership check by stating that every procedure here is adminProcedure, which is no longer true. The other cited a Kilo staff elevation as the reason a requester might see an export whose access the list query cannot reproduce, which can no longer happen, so it now cites a revoked export role. The behavior both comments describe is unchanged.
user-exports-router.test.ts: rejects non-admin users becomes allows non-admin users to list their exports, asserting an empty list instead of FORBIDDEN.
page.test.ts: new. Covers the page guard, asserting adminOnly: false, that a signed out visitor is refused, and that a signed in non-admin user renders.
What this does not change
Organization exports still require an owner or admin role on that organization, checked by requireExportableOrganization and re-checked independently by the export Worker. Kilo staff status does not grant access to another organization's data, and the test covering that still passes.
API token and extension callers are still refused by requireWebSession.
The emailed download code step is unchanged.
Verification
No manual testing was run. The dev stack for this change is owned by the reviewer, so verification here was automated only: pnpm --filter web test -- data-export user-exports-router passes 164 tests across 14 suites, alongside format:check, lint and typecheck. The boxes below are the manual paths still worth walking.
Sign in as a non-admin user, confirm Request data export appears in the user menu and the page loads
Request an export as a non-admin user, confirm a second request inside 24 hours is refused
Confirm a non-admin user who is not an owner or admin of any organization sees no organization export buttons
Visual Changes
The user menu entry linking to /data-exports is now visible to all signed in users, where it previously rendered only for is_admin accounts. Screenshots to add: the user menu on a non-admin account, before and after.
Before
After
Reviewer Notes
baseProcedure still requires an authenticated session. createTRPCContext throws UNAUTHORIZED when there is no user, so this widens access from staff to signed in users, not to anonymous callers.
Dropping adminProcedure also stops emitAdminAccessEvent firing for these procedures. That is intended, since they are no longer an admin surface, but it does mean these calls no longer appear in the admin access log.
The new page test is page.test.ts, not .tsx. Jest's testMatch in apps/web/jest.config.ts is **/src/**/*.test.ts, so a .tsx suite is never collected. A page.test.tsx existed on an earlier branch and was deleted in fix(user-data-export): show expired downloads as disabled with a tooltip #5202 without any failure, because it had never run.
The admin-to-base-procedure switch is safe: createTRPCContext already rejects unauthenticated callers, all export authorization remains membership-scoped (requireExportableOrganization / requireDownloadableExport), the 24h throttle restoration matches the user-facing messages, and the new page test's mocks and file extension align with the real modules and jest testMatch.
Verified additionally: baseProcedure authentication guarantee in lib/trpc/init.ts, getUserFromAuth semantics in lib/user/server.ts, FOOTER_MENU_ROUTES sync in AppSidebar.tsx, and DataExportsClient only calling the now-public userExports procedures. No memory-leak vectors (subscriptions, timers, listeners, unbounded caches) introduced by the diff.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Data exports were restricted to Kilo staff while the feature was being built. This removes the
is_admingate so any signed in user can request and download an export of their own data.The gate came off in three places: the
/data-exportspage, the user menu entry that links to it, and all sixuserExportstRPC procedures. Those procedures move fromadminProceduretobaseProcedure, which still requires an authenticated session, so the endpoints are open to signed in users rather than public.The request throttle also goes back to 24 hours. It had been lowered to 5 minutes for pre-launch testing, with a comment saying to restore it before going live, while the message shown to users already said 24 hours. The code and the copy now agree.
Changes
page.tsx:getUserFromAuth({ adminOnly: true })becomesadminOnly: false. A signed out visitor still getsnotFound().SidebarUserFooter.tsx: the user menu entry renders for everyone. Theis_adminfield is dropped from the component's localUsertype, since nothing else used it.user-exports-router.ts:request,requestOrganization,exportableOrganizations,list,requestDownloadCodeandcreateDownloadmove tobaseProcedure.user-exports-router.ts: throttle restored frominterval '5 minutes'tointerval '24 hours', and the TEMPORARY comment removed.user-exports-router.ts: two comments reworded. One justified this router doing its own membership check by stating that every procedure here isadminProcedure, which is no longer true. The other cited a Kilo staff elevation as the reason a requester might see an export whose access the list query cannot reproduce, which can no longer happen, so it now cites a revoked export role. The behavior both comments describe is unchanged.user-exports-router.test.ts:rejects non-admin usersbecomesallows non-admin users to list their exports, asserting an empty list instead of FORBIDDEN.page.test.ts: new. Covers the page guard, assertingadminOnly: false, that a signed out visitor is refused, and that a signed in non-admin user renders.What this does not change
requireExportableOrganizationand re-checked independently by the export Worker. Kilo staff status does not grant access to another organization's data, and the test covering that still passes.requireWebSession.Verification
No manual testing was run. The dev stack for this change is owned by the reviewer, so verification here was automated only:
pnpm --filter web test -- data-export user-exports-routerpasses 164 tests across 14 suites, alongsideformat:check,lintandtypecheck. The boxes below are the manual paths still worth walking.Visual Changes
The user menu entry linking to
/data-exportsis now visible to all signed in users, where it previously rendered only foris_adminaccounts. Screenshots to add: the user menu on a non-admin account, before and after.Reviewer Notes
baseProcedurestill requires an authenticated session.createTRPCContextthrows UNAUTHORIZED when there is no user, so this widens access from staff to signed in users, not to anonymous callers.adminProcedurealso stopsemitAdminAccessEventfiring for these procedures. That is intended, since they are no longer an admin surface, but it does mean these calls no longer appear in the admin access log.page.test.ts, not.tsx. Jest'stestMatchinapps/web/jest.config.tsis**/src/**/*.test.ts, so a.tsxsuite is never collected. Apage.test.tsxexisted on an earlier branch and was deleted in fix(user-data-export): show expired downloads as disabled with a tooltip #5202 without any failure, because it had never run.