Summary
When the backend runs under the Desktop app, its file routes now require a capability.
GET/HEAD /files/streamandGET /files/preview/...require a file access grant. Grants come from a newPOST /files/accessendpoint, which needs the Desktop capability like the other control APIs.- A grant covers one project file root (email, project, and optional Space and user). It expires after one hour and is signed with a key that exists only in the running backend process, so restarting the app revokes every grant.
GET /files(listing),POST /files(upload) andGET /chat/{project_id}/statusnow require the Desktop capability when it is configured, the same as the other routes the app calls with headers.
Why the grant is in the URL
The app displays files with <img>, <iframe> (PDF and HTML previews) and media elements, and can open them in the system browser. None of these can send the capability header. So when a file is opened, the app requests a grant and adds it to that file's URL.
For HTML previews the grant is a path segment (/files/preview/{grant}/{path}). Relative links and assets inside the previewed page then resolve to URLs that carry the grant, and they keep the Space and user scope.
App changes
- Opening a file in Files or in the file preview requests a grant first (
authorizeBrainFileinsrc/lib/brainFileAccess.ts). Listing URLs don't change, so file identity, selection and the file tree are unaffected. - HTML previews build their base URL from the grant. When a previewed page references other files from the same project root, those URLs get the same grant.
- If no grant can be obtained, for example from a backend that doesn't have this change, the file URL stays as it was and the previous preview URL format is used.
Compatibility
- Web and self-hosted deployments, which run without the Desktop capability, keep their existing router-level authentication, and
/files/streamdoesn't require a grant there. HTML previews use the grant-based URL in every mode.POST /files/accessis available there under the same authentication as the other routes. - The preview route changes from
/files/preview/{email}/{project_id}/{path}to/files/preview/{grant}/{path}. The app in this repository is its only caller. - Works alongside #2044. This PR doesn't change
SecurityHeadersMiddleware, so the framing rules that PR sets for/files/streamand/files/preview/still apply.
Known limitation
If a preview stays open for more than an hour, loading more of the file can fail, for example when scrolling far into a very large PDF or seeking in a long video. Reopening the file gets a new grant.
Testing
- Backend:
pytest tests/app/auth/test_file_access.py tests/app/auth/test_local_control.py tests/app/controller/test_file_controller.py(42 passed). The tests cover allowed and denied requests for streams, previews, listing, upload and grant issuance with the Desktop capability configured, and the behavior without it. - Backend, neighboring suites:
pytest tests/app/workspace_runtime/test_registration.py tests/app/test_main_security_defaults.py tests/app/agent/toolkit/test_terminal_processes.py(42 passed) - Frontend:
vitest run test/unit/lib/brainFileAccess.test.ts test/unit/components/Folder test/unit/lib/filePreviewLoader.test.ts(116 passed),npm run type-check, eslint and prettier on the changed files,git diff --check
Still to verify by hand in the packaged app: PDF, image, video and HTML previews, including an HTML report with relative assets, in both Files and the session preview panel.
Activity
1This pull request is a draft
Merging is unavailable until it is marked ready for review.