.foldervas File Format
Open specification of the document format used by Foldervas — an infinite-canvas organizer. Any application may produce or consume these files.
1. Container
A .foldervas file is a single SQLite 3 database. There is no wrapper, no compression, no magic bytes beyond SQLite's own. Any SQLite library in any language can create or read one.
- All strings are UTF-8.
- All timestamps are ISO 8601 strings (e.g.
2026-07-10T12:34:56.789Z). - Booleans are stored as
INTEGER0/1. - Identifiers are UUID v4 strings (lowercase recommended).
A sibling directory named <filename>.assets/ may exist next to the file. It is an optional performance cache maintained by the reading application (see §10) — generators do not need to create it.
2. Database schema
CREATE TABLE canvas_meta (
key TEXT PRIMARY KEY,
value TEXT
);
CREATE TABLE nodes (
id TEXT PRIMARY KEY, -- UUID
type INTEGER NOT NULL, -- see §3
x REAL NOT NULL,
y REAL NOT NULL,
width REAL NOT NULL,
height REAL NOT NULL,
content TEXT,
background_color TEXT, -- '#rrggbb', 'none', or NULL
top_color TEXT, -- '#rrggbb' accent strip, or NULL
z_index INTEGER DEFAULT 0,
parent_id TEXT, -- NULL = root board
sort_order INTEGER DEFAULT 0,
asset_id TEXT, -- FK into assets.id
is_locked INTEGER DEFAULT 0,
lock_aspect_ratio INTEGER DEFAULT 1,
gallery_columns INTEGER DEFAULT 2,
rich_content TEXT, -- reserved, leave NULL
created_at TEXT,
updated_at TEXT,
-- columns below may be ABSENT in older files; readers must tolerate that
source_url TEXT, -- link cards: the URL
gallery_column_hint INTEGER DEFAULT -1,
file_path TEXT, -- reserved, leave NULL
font_size INTEGER, -- notes: base font px; connectors: stroke weight
icon TEXT, -- folders: emoji icon
connect_start TEXT, -- connectors: source node id
connect_end TEXT, -- connectors: target node id or NULL
caption TEXT, -- file cards: caption text
collapsed INTEGER DEFAULT 0, -- galleries/columns: header-only
alias_target TEXT, -- aliases: target folder id
theme TEXT, -- folders: board theme (see §7)
rotation REAL DEFAULT 0, -- node rotation in degrees (clockwise)
curvature REAL DEFAULT 0, -- connectors: signed apex offset (px); 0 = straight
text_color TEXT, -- text color (hex); NULL = auto by contrast
text_align TEXT, -- free canvas text: left|center|right (NULL = center)
line_spacing REAL -- notes: line-height factor (NULL = 1.45)
);
CREATE TABLE assets (
id TEXT PRIMARY KEY, -- UUID, referenced by nodes.asset_id
filename TEXT NOT NULL,
mimetype TEXT NOT NULL,
data BLOB NOT NULL, -- the binary, or zeroblob(0) if externalized
width INTEGER, -- images: natural px
height INTEGER,
size INTEGER, -- byte length of the binary
created_at TEXT
);
CREATE INDEX idx_nodes_parent ON nodes(parent_id);
CREATE INDEX idx_nodes_type ON nodes(type);
3. Node types
type | Name | What it is | Key columns |
|---|---|---|---|
| 0 | Text | Note card, or free canvas text when background_color = 'none' | content, font_size |
| 1 | Image | Image card; content doubles as the visible caption (empty/NULL = bare image) | asset_id, content |
| 2 | Attachment | Any file (PDF, video, audio, …); content = display filename | asset_id, content, caption |
| 3 | Container | Folder / board: a navigable sub-canvas. Its children are the nodes whose parent_id points at it | content (title), icon |
| 5 | Gallery | Masonry auto-layout with gallery_columns columns (1 column = a vertical list) | gallery_columns, collapsed |
| 7 | Line | Connector (arrow) between nodes — see §6 | connect_start, connect_end |
| 8 | Alias | Shortcut tile that navigates to another folder | alias_target |
Values 4 (Column) and 6 (HStack) are legacy: readers convert them to Gallery on load (a legacy Column becomes a 1-column gallery). Writers must not emit them — use a Gallery with gallery_columns = 1 for a vertical list. Other unknown type values degrade to 0 (Text).
4. Hierarchy & coordinates
parent_id = NULL→ the node lives on the root board.parent_id = <folder id>→ the node lives inside that folder (type 3). Folders nest arbitrarily deep. Cycles are invalid.parent_id = <column/gallery/hstack id>→ the node is a cell of that auto-layout container (see §5).- Each board is an infinite 2-D plane;
x/yis the node's top-left corner in canvas units (1 unit = 1 px at zoom 1). Coordinates may be negative. z_indexorders overlapping free nodes within a board (higher = on top).
5. Auto-layout containers (Gallery)
- Children are laid out automatically; their stored
x/yare ignored while parented. Order comes fromsort_order(ascending). - Galleries distribute children round-robin over
gallery_columns(≥ 1) masonry columns;gallery_column_hint(≥ 0) pins a child to a specific column,-1= automatic. - The container's rendered height is derived from its content — the stored
heightis a minimum only.collapsed = 1renders header-only. - Child
width/heightkeep the node's natural size; images are scaled to the cell width preserving aspect ratio.
6. Connectors (type 7)
connect_start— id of the source node. Required. Must be a node on the same board (parent_idof connector and endpoints must match).connect_end— id of the target node, orNULLfor a free endpoint at the connector's ownx/y.- Endpoints are recomputed from the linked nodes' rectangles at render time; the connector's
width/heightare unused (write0). - Styling:
background_color= stroke color (NULL = theme default),font_size= stroke weight in px (NULL = 2.5). curvature— signed distance (px) from the curve's apex to the straight segment between endpoints;0= straight line. The curve passes through that apex.- A connector whose endpoints no longer exist is simply not drawn.
7. Captions, aliases, folders
- Image captions live in
content: non-empty = card with a caption bar, empty/NULL = bare image. - Attachment captions live in
caption(becausecontentholds the filename). Same rule: non-empty = caption bar. The bar's background isbackground_color. - Folders (type 3):
content= title,icon= optional emoji replacing the folder glyph,top_color/background_color= tile tint. - Aliases (type 8):
alias_targetmust reference a type-3 node. If the target disappears, readers drop the alias. - Folder themes: a folder's
themeholds a theme id (light,sepia,dark,midnight,slate, orcustom:<name>for an imported .fvtheme). Entering the folder renders the app with that theme; subfolders inherit it unless they set their own. Unknown values are ignored.
8. The canvas_meta table
Key/value strings. All keys are optional for writers — readers fall back to the defaults below and clamp out-of-range values.
| Key | Value | Default | Range |
|---|---|---|---|
schema_version | format version | 1 | write 1 |
nodeCount | number of rows in nodes (integrity check) | — | must match if present |
lastModified | ISO 8601 timestamp | — | — |
zoom, offsetX, offsetY | root board viewport | 1, 0, 0 | — |
folderViewports | JSON: {"<folderId>": {"zoom":n,"offsetX":n,"offsetY":n}} | {} | — |
drawings | JSON — see §9 | {} | — |
trash | JSON — deleted subtrees, see below | [] | — |
noteWidth | default width of new notes (px) | 280 | 160–560 |
kanbanWidth | default width of new galleries/columns | 280 | 220–640 |
imageWidth | target longest side of dropped images | 280 | 120–560 |
textHeight | default height of free canvas text | 44 | 28–120 |
defaultFontSize | default note font size (px) | 14 | 11–28 |
defaultNoteColor, defaultFolderColor | #rrggbb or empty (theme default) | empty | — |
documentTheme | document-wide theme id (same values as folder theme); folders with their own theme win inside their board | empty | — |
showGrid | 1/0 | 1 | — |
snapSize | snap grid size (px) | 28 | app-clamped |
Trash — an array of deleted subtrees, restorable by the user:
[{ "id": "<root node id>", "name": "…", "type": 0,
"deletedAt": "<ISO>", "nodes": [ /* full node objects, root included */ ] }]
9. Drawings (strokes)
The drawings meta key holds freehand ink, grouped by surface:
{
"__root__": [ Stroke, … ], // root board
"<folderId>": [ Stroke, … ], // a folder's board
"image:<nodeId>": [ Stroke, … ] // ink drawn ON an image node
}
Stroke = {
"id": "<uuid>",
"points": [x0, y0, x1, y1, …], // flat pairs, length even, ≥ 2
"color": "#rrggbb" | "ink", // "ink" = adapts to light/dark theme
"width": number, // px: 2 | 4 | 8 typical
"opacity": number? // 0–1, default 1
}
- Board strokes use canvas coordinates (same space as node
x/y). - Image strokes (
image:<nodeId>keys) use a normalized space: coordinates are per-mille of the image node's width (0–1000 across the width; y in the same scale), so ink survives image resizing.
10. Assets (binaries)
Two valid ways to store a binary; readers support both:
- Inline (recommended for generators): put the bytes in
assets.data. Single self-contained file. - Externalized: write
zeroblob(0)inassets.dataand place the raw bytes in the sibling directory<filename>.assets/<asset id>(no extension, filename = the asset UUID). Foldervas itself writes this layout for speed and migrates inline blobs to it on save.
mimetypeis required and drives previews (image/*,video/*,application/pdf, …).- Asset ids must be plain base names (UUIDs). Readers reject path-like ids (
/,\,..) for security. - Unreferenced assets (no node's
asset_idpoints at them) may be garbage-collected by readers.
11. Requirements for writers
A generator MUST:
- emit valid SQLite with the tables of §2 (extra columns/tables are ignored by readers, but don't rely on that);
- write
schema_version = '1'and unique UUID ids; - keep
parent_idreferences acyclic and pointing at existing nodes; - store booleans as
0/1and timestamps as ISO 8601.
A generator SHOULD:
- write
nodeCount(readers use it to detect truncated files); - set
assets.width/heightfor images and a realmimetypealways; - keep numeric settings inside the ranges of §8 (readers clamp, but round-trips stay exact if you do).
connect_*/alias_target references are dropped. A partially conforming file opens; a perfectly conforming file round-trips.12. Versioning
This page documents version 1. Future revisions will bump schema_version and be published here with a changelog; version 1 files will remain readable. Applications encountering a version newer than they support should refuse to write to the file (never silently downgrade it).