DSH Toolbox

DSH storage layout reference

What DSH writes to disk, measured on a real install (0.2.1-rc.2). The payload, the list entry and the title are three separate things on disk. Treat them as one and you get an empty-looking session list while every file is still there.

Measured 2026-11-13 on DSH 0.2.1-rc.2, Windows.

Session formats

v4legacy
File namesession.v4.jsonl.zstdsession.jsonl.zstd
Produced byDSH 0.2.x web / CLI / TUIOlder desktop runtime
Frame layoutMulti-frame, append-onlyMulti-frame, append-only
CoexistenceA single session folder can hold both files
Largest observed22,832,359 bytes in one file21,360,396 bytes in one file

Both formats are streams of independently decompressible zstd frames, appended as the session grows. Tools should treat them as opaque bytes.

Directory layout

The workspace directory name is a slug of the absolute workspace path, not the workspace id. Two machines with different paths get different directory names for the same workspace.

~/.dsh                          DSH data root (web / CLI / TUI)
├─ sessions/
│  └─ --C-Users-you-my~0020project--/  slug of "C:\Users\you\my project"
│     └─ session-<uuid>/         one folder per session, name used verbatim
│        ├─ session.v4.jsonl.zstd
│        └─ session.jsonl.zstd   (legacy, may coexist)
└─ storages/
   ├─ workspace.json             workspaces + session list
   ├─ session_projcache.json     titles + per-turn stats (aggregate)
   ├─ session_projcache/sessions/<id>.json   same data, per session
   └─ schedule.json              device-local reminders (do not sync)

Path slug rule

Derived from the absolute path: drop the colon, turn backslashes into hyphens, encode spaces as ~0020, wrap in double hyphens. C:\Users\you\my project → --C-Users-you-my~0020project--

workspace.json (unit version 2)

This is the file that decides whether a session appears in the list. Syncing payloads without it makes sessions invisible.

FieldMeaningMerge rule
global.workspaceIdsRegistered workspacesUnion
global.archivedSessionIdsHidden sessionsUnion (idempotent)
global.pinnedSessionIdsPinned sessionsUnion
tables.workspaces.<id>.pathAbsolute path on this machineKeep local; never take the remote value
tables.workspaces.<id>.titleDisplay nameLocal if set
tables.workspaces.<id>.sessionIdsOrdered session listUnion
tables.workspaces.<id>.createdAt / updatedAtTimestampsmin / max

session_projcache.json (unit version 3)

Titles, turn counts and token usage live here, not in workspace.json. DSH keeps an aggregate table and a per-session file for the same data; both count as “has a title”. Rows carry {ver, seq, val}, so merging compares ver first, then seq.

FieldMeaning
tables.sessions.<id>.identity.cwdAbsolute path of the session workspace (authoritative for mapping sessions to workspaces)
tables.sessions.<id>.rows.titleDisplay title; null means untitled
tables.sessions.<id>.rows.sessionStatsturns, steps, llmMs, decodeTokens…
tables.sessions.<id>.rows.tokenUsageCumulative and last-turn token buckets

Symptom → cause

SymptomCause
Session file exists, DSH does not list itThe session id is missing from tables.workspaces.<id>.sessionIds
Session listed but untitledNo projcache row (aggregate or per-session) for that id
Entry opens into nothingListed id has no payload folder on disk
Session shows an old stateTwo formats coexist; DSH reads one, the other went stale
Archived session still takes spaceArchiving hides, it does not delete
A sync “worked” but the list is emptystorages/ was not synced with sessions/

How to check your own install

The Session Index Doctor runs these checks locally: pick your DSH data root, get the mismatched ids and the one-line fix for each.

Open the Session Index Doctor →