Open Science is local-first — every piece of data it produces lives on your own machine. There are no accounts, cloud sync services, or remote databases involved. All persistent state — projects, chat sessions, agent-produced artifacts, notebook run histories, and staged uploads — is written under a dot-directory in your home folder. Understanding this layout helps you back up your research, debug unexpected behavior, or migrate your data to another machine.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/aipoch/open-science/llms.txt
Use this file to discover all available pages before exploring further.
Storage Root Paths
The storage root is the top-level directory that contains everything Open Science writes. Its location follows each platform’s conventions:| Platform | Path |
|---|---|
| macOS | ~/.open-science/ |
| Windows | %USERPROFILE%\.open-science\ |
| Linux | ~/.open-science/ |
storage-root.ts using app.getPath('home') — it does not use app.getPath('userData'), so it is a dot-directory in your home folder rather than the OS app data folder.
When running from source with
npm run dev, Open Science isolates its data to a sibling directory called .open-science-project to avoid polluting your real .open-science tree. The two directories are otherwise identical in structure. app.isPackaged controls which name is used.Directory Layout
The complete directory tree under the storage root looks like this:SQLite Database
File:open-science.db in the storage root.
The database is created automatically on first launch. Open Science applies its schema using CREATE TABLE IF NOT EXISTS DDL at startup — no migration engine is required in the packaged app. The schema is managed via Prisma during development.
The database contains exactly two tables:
Project
Stores user-created research projects.
| Column | Type | Description |
|---|---|---|
id | TEXT (CUID) | Primary key, auto-generated |
name | TEXT | User-assigned project name |
description | TEXT | Optional project description (default: empty string) |
isExample | BOOLEAN | Whether this is a bundled example project |
createdAt | DATETIME | Creation timestamp |
updatedAt | DATETIME | Last modification timestamp (auto-updated) |
ProjectPreviewState
Stores the per-project preview panel UI state so it survives app restarts.
| Column | Type | Description |
|---|---|---|
projectId | TEXT | Primary key — matches a Project.id |
panelState | TEXT | Open/collapsed state of the preview panel |
activeItemId | TEXT | Currently active preview tab ID (nullable) |
items | TEXT | JSON array of open file previews (default: []) |
updatedAt | DATETIME | Last modification timestamp (auto-updated) |
Sessions, messages, artifacts, and notebook runs are not stored in SQLite. They are stored as JSON files and directories on disk (see the sections below). The database only tracks the project list and panel state.
Sessions
Chat sessions are stored as JSON files on disk, one file per session, organized by project ID:manifest.jsonrecords the last-open project and session so the app can reopen to the correct state on next launch.- Session files are written atomically (write to a
.tmpfile, then rename) to protect against data loss during crashes. - Reads are performed on app start — all session files are loaded into memory by the session persistence module.
- If a session file is unreadable or contains invalid JSON, it is renamed to
{sessionId}.json.invalid-{timestamp}as a backup rather than deleted.
Artifacts
Agent-produced output files are organized under theartifacts/ directory by project, session, and message:
projectNameis the logical project bucket. For sessions not yet associated with a named project, the constantDEFAULT_ARTIFACT_PROJECT_NAME = 'default-project'is used as the bucket name.- Pending artifacts (under
.pending/{runId}/) are written while an agent run is active. The renderer finalizes them by callingFinalizeRunArtifactsRequest, which moves them into the durable{messageId}/directory. - Artifact files are never deleted automatically. You are responsible for managing disk space in the
artifacts/tree.
Notebooks
Each notebook session has its own directory undernotebooks/:
run.json — NotebookRunDocument
The run.json file is the authoritative record of everything that has happened in a notebook session. It is a NotebookRunDocument (schema version 1) with the following top-level fields:
| Field | Type | Description |
|---|---|---|
version | 1 | Schema version — always 1 |
projectName | string | Owning project name |
sessionId | string | Notebook session identifier |
workspaceCwd | string | Initial working directory of the kernel |
notebookSessionRoot | string | Absolute path to this session’s directory |
dataRoot | string | Absolute path to the data/ subdirectory |
kernel | NotebookKernelMetadata | Language, Python path, kernel name, last known status |
runs | NotebookRunRecord[] | Ordered list of all cell executions |
updatedAt | number | Unix timestamp (ms) of the last write |
runs captures the cell source code, execution status, stdout/stderr output, structured outputs (text, JSON, error tracebacks), and references to any artifact files or working files created during that execution.
Uploads
User-uploaded files are staged underuploads/ before being sent to the agent:
DEFAULT_UPLOAD_PROJECT_NAME = 'default-project' from src/shared/artifacts.ts. There is no per-user or per-project upload directory selection.
- Staging: when a user attaches a file or pastes an image before sending a message, it is written to
.pending/(the constantPENDING_UPLOAD_SESSION_ID = '.pending'fromsrc/shared/uploads.ts). - Finalization: once the agent runtime assigns a session ID, pending uploads are copied into
{sessionId}/and the staging file is deleted, associating them durably with that conversation. - Orphaned staging files: if the app crashes between staging and finalization, files may remain in
.pending/. These can be safely deleted — they are not referenced by any session. - Upload filenames are sanitized and deduplicated automatically. The original display name is stored separately in the attachment record (
originalName) and is not lost.