In short
A 40 GB warehouse has no business travelling through a browser: put it on the server, and Linkr points at it by path — nothing is copied. The administrator’s job is to bound what that picker can reach, with LINKR_FS_BROWSE_ROOTS. By default no bound is set: it is the deployment’s job to mount only what the server should see.
The problem it solves
Every “file” entry point in a web application assumes the file is on the user’s machine. For a DuckDB file or a Parquet folder an administrator has already placed on the server, that forces an absurd detour: download the file to your machine in order to upload it back. Past the maximum upload size it is not even possible.
Pointing at a server path removes the detour. Nothing moves: Linkr reads the files where they are.
Three file origins, not to be confused
Uploading remains the right answer for small authored files — an import archive, an image, a script. The server path is for large volumes of data. There is also, in browser mode, direct access to folders on your own machine with no copy: that is the client-side equivalent. Both answer the same need — not duplicating gigabytes — each on its own side.
Where it is used
| Screen | What you point at |
|---|---|
| Databases › Add | A .duckdb or .sqlite file, or a folder of Parquet files. |
| IDE › Connections | The same thing, from the project’s coding environment. |
| Concept mapping › Vocabulary | An OHDSI vocabulary folder — several gigabytes, a dozen tables. |
| Project › Folders | The project’s three locations: IDE working directory, code sub-folder, datasets folder. |
In the add-database dialogs a Server file option sits next to the upload, with the hint “Click to browse the server”. A server-side file browser then opens.
Elsewhere, uploading stands alone
The other entry points — archive imports, attachments, IDE files — accept uploads only. That is deliberate: those files come from the user’s machine, and adding a “server” tab everywhere would have helped nowhere.
Bounding what can be reached
This is the administration decision on this page, and it deserves to be made explicitly.
By default the picker sees the whole filesystem
With no configuration, LINKR_FS_BROWSE_ROOTS is empty and no root is enforced: the picker can traverse the entire filesystem the server can see. This is the RStudio Server model — confinement is the deployment’s responsibility: mount into the container only what the server should see.
If your server sees more than that, set roots.
The variable takes a comma-separated list of absolute paths:
LINKR_FS_BROWSE_ROOTS="/data/warehouse,/data/vocabularies"
The picker then never leaves those folders or their sub-folders.
What is checked, and when
Three protections stack, and the last one matters most.
A permission is required
Browsing the server is not open to everyone: pointing a database at a source requires the right to edit databases, and binding a project’s folders the right to edit its settings.
Symbolic links are resolved
A path is resolved before being compared with the roots, so a symbolic link cannot be used to escape an allowed root.
The boundary is re-checked on save
The check done in the picker is only an interface convenience. It is redone when the path is persisted — which is what stops anyone bypassing the picker by sending an arbitrary path directly.
What it does not allow
- Writing anywhere. A database pointed at by path is opened read-only, and cannot be the target of an ETL pipeline.
- Leaking the path in an export. The server path is stripped from exports, like other connection details: a shared entity does not reveal your server’s directory layout.
- Binding a project’s folders without the IDE. If code execution is disabled on the instance, folder bindings are disabled too. Pointing a database, by contrast, still works: attaching read-only data is not running code.
A vocabulary on the server is read as Parquet
A vocabulary folder pointed at on the server is read in Parquet format. A CSV OHDSI export is refused with an explicit message rather than imported empty — a readable refusal beats a silently incomplete import.
A project’s folders
A project exposes three locations, each set separately and described in its settings:
- The IDE working directory — what the file explorer shows, and where terminals and R and Python sessions start.
- The code sub-folder — the part packaged on export and versioning. Set separately precisely so an export never carries datasets that live elsewhere in the IDE folder.
- The datasets folder — reachable from scripts through an environment variable.
All three are independent and machine-local: they do not travel in an export, and another installation will use its own.
What this means for your users
Because these folders can be re-pointed, a script must never derive one location from another — writing ”../datasets” works until the day someone moves a folder. The linkr libraries provide the accessors to use. See IDE.
Going further
- Configuration — an instance’s other environment variables.
- Authentication and permissions — which rights open this picker.
- Databases — the user’s view of the same screens.
- IDE — the project’s folders, seen from the code.