← Open file formats
Trellis format

.trellis

ZIP-based archive format with a plugin-keyed JSON document and optional binary payloads for embedded image assets.

Contents

1. Container layout

ZIP archive with a .trellis suffix. Most of the document lives in JSON. Binary files appear only when the document embeds image assets or another plugin emits non-JSON payloads.

project.trellis
├── manifest.json
├── document.json
└── plugins/
    └── VECTOR_DOCUMENT@1/
        └── image-assets/<assetId>.bin

If a file has no embedded image assets, it can be entirely JSON-backed inside the ZIP.

2. manifest.json

manifest.json records the container version and the set of serialized plugin entries expected in document.json.

{
  "version": "1.0.0",
  "plugins": [
    { "name": "VECTOR_DOCUMENT@1", "version": "1.0.0" },
    { "name": "VECTOR_LAYER_SYSTEM@1", "version": "1.0.0" },
    { "name": "VECTOR_VIEWPORT_STATE@1", "version": "1.0.0" }
  ],
  "layerCount": 4,
  "createdAt": "2026-03-15T00:00:00.000Z",
  "modifiedAt": "2026-03-15T00:00:00.000Z"
}

version

The current container version written by the shared serializer.

plugins

Array of plugin names and versions. Every listed plugin must appear in document.json with a matching entry.

layerCount

High-level document metadata. It does not replace the canonical vector layer-system payload.

createdAt and modifiedAt

Shared document timestamps.

3. document.json

document.json is keyed by plugin token ID. The file does not flatten everything into one object schema; each entry owns its own payload.

{
  "VECTOR_LAYER_SYSTEM@1": { ... },
  "VECTOR_DOCUMENT@1": { ... },
  "VECTOR_VIEWPORT_STATE@1": { ... }
}

Entry envelope

{
  "pluginName": "VECTOR_DOCUMENT@1",
  "version": "1.0.0",
  "data": {
    "objects": [ ... ],
    "rootOrder": [ ... ],
    "selection": [ ... ],
    "artboards": [ ... ],
    "assets": [ ... ]
  }
}

Each entry must contain a matching pluginName, a version, and a data field. Manifest/document mismatches, missing payloads, or bad entry keys fail preflight.

That envelope is part of the format. A reverse-engineering parser should not jump directly to data; it should first resolve the plugin name and version carried by the envelope itself.

4. Document entry families

A .trellis archive is a set of plugin-owned entries. In practice those entries fall into a few predictable families.

Core document entries

VECTOR_DOCUMENT@1 and VECTOR_LAYER_SYSTEM@1 carry the scene graph, artboards, embedded asset metadata, and vector layer definitions.

Editor-state entries

Entries such as VECTOR_VIEWPORT_STATE@1, VECTOR_STYLE_STATE@1, VECTOR_ACTIVE_TOOL_STATE@1, VECTOR_TEXT_DEFAULTS@1, VECTOR_GRID_STATE@1, and VECTOR_SNAPPING_STATE@1 store additional editor state.

Binary-backed entries

VECTOR_DOCUMENT@1 may reference embedded image binaries under plugins/VECTOR_DOCUMENT@1/image-assets/.... Other plugin entries may also own binaries.

Unknown entries

The envelope format allows additional plugin entries beyond the ones documented here. A parser should not assume the set is permanently closed.

5. VECTOR_DOCUMENT@1

VECTOR_DOCUMENT@1 is the scene-graph core of the file. It stores the object map, ordering, artboards, selection state, counters, and embedded image asset metadata.

{
  "objects": [
    {
      "id": "obj_1",
      "type": "path",
      "name": "Path 1",
      "transform": { ... },
      "style": { ... },
      "visible": true,
      "locked": false,
      "parentId": null,
      "layerId": "layer_1",
      "authoring": { ... },
      "commands": [ ... ]
    }
  ],
  "rootOrder": ["obj_1"],
  "selection": [],
  "lastSelectedId": null,
  "artboards": [ ... ],
  "activeArtboardId": "artboard_1",
  "artboardCounter": 1,
  "objectCounter": 24,
  "assets": [ ... ]
}

objects

Array of serialized vector objects. Current object types are path, group, clipGroup, text, and image.

rootOrder, selection, lastSelectedId

Document ordering and current selection state.

artboards and activeArtboardId

Document artboards in order plus the currently active artboard.

artboardWidth and artboardHeight

Compatibility dimensions projected from the active artboard.

artboardCounter and objectCounter

Counters stored as document metadata.

assets

Embedded image metadata including MIME type, dimensions, byte length, checksum, original filename, and a binary filename for the matching asset payload.

This is the main entry. Parsing it is enough to reconstruct the document's objects, hierarchy, artboards, and embedded-asset references. The remaining editor-state entries are not required.

6. Artboards, ordering, and counters

In addition to the object array, the file stores document-wide ordering and artboard metadata directly in VECTOR_DOCUMENT@1.

rootOrder

Back-to-front root object order for the scene graph.

selection and lastSelectedId

Current selected object IDs and the most recently selected object.

artboards

Array of artboard records with id, name, x, y, width, and height.

activeArtboardId

Active artboard identifier.

artboardWidth and artboardHeight

Compatibility dimensions projected from the active artboard.

artboardCounter and objectCounter

Counters stored as document metadata.

rootOrder is the root-level z-order only. Child ordering for grouped content lives on each group or clip-group object’s children array. A parser needs both to reconstruct draw order.

7. Object payloads

Every serialized object carries a shared base shape: id, type, name, transform, style, visible, locked, parentId, and layerId. Each object type then adds its own payload.

Path objects

Store both canonical path geometry and compiled path commands.

Group objects

Store ordered child IDs and act as structural containers in the scene graph.

Clip groups

Extend group payloads with a required clipperId. The clipper must resolve to a child path object.

Text objects

Store UTF-16 content, layoutMode, paragraph spans, style runs, optional area-text frame, story links, text-on-path metadata, and overset state.

Image objects

Store an assetId plus intrinsic dimensions. The actual bytes live in the matching embedded asset binary.

Hierarchy

Objects can live at root level or under a parent group. The file preserves this graph explicitly rather than flattening it away.

Layer membership

Every object carries a layerId. The file depends on the separate vector layer-system payload to make those IDs meaningful.

Bounds

Some serialized object kinds also include cached bounds.

Shared primitives

Every object relies on the same transform and style families. Current transform matrices are six-number 2D affine matrices in column-major form:

{
  "a": 1,
  "b": 0,
  "c": 0,
  "d": 1,
  "tx": 0,
  "ty": 0
}

Current vector styles are serialized as fill/stroke objects with explicit paint kinds and rules:

{
  "fill": {
    "paint": {
      "kind": "solid",
      "color": { "r": 30, "g": 30, "b": 30, "a": 1 }
    },
    "fillRule": "nonZero"
  },
  "stroke": {
    "paint": {
      "kind": "solid",
      "color": { "r": 255, "g": 120, "b": 0, "a": 1 }
    },
    "width": 2,
    "cap": "round",
    "join": "round",
    "miterLimit": 4,
    "transformPolicy": "scaleWithObject"
  }
}

Path shape

{
  "id": "obj_path",
  "type": "path",
  "name": "Path 1",
  "transform": { ... },
  "style": { ... },
  "visible": true,
  "locked": false,
  "parentId": null,
  "layerId": "layer_1",
  "authoring": { ... },
  "commands": [ ... ]
}

Path objects preserve both authoring and commands. The authoring payload is the richer geometric form; commands are a second geometric representation.

commands are arrays of discriminated records. Current command types are moveTo, lineTo, quadTo, cubicTo, closeCubic, and close. Paths can therefore be reconstructed even if a consumer ignores the richer authoring payload.

Group shape

{
  "id": "obj_group",
  "type": "group",
  "name": "Group 1",
  "transform": { ... },
  "style": { ... },
  "visible": true,
  "locked": false,
  "parentId": null,
  "layerId": "layer_1",
  "children": ["obj_path", "obj_text"]
}

Group objects are structural containers. Their children array preserves child ordering inside the group.

Clip group shape

{
  "id": "obj_clip_group",
  "type": "clipGroup",
  "name": "Clip Group 1",
  "transform": { ... },
  "style": { ... },
  "visible": true,
  "locked": false,
  "parentId": null,
  "layerId": "layer_1",
  "children": ["obj_shape", "obj_clipper"],
  "clipperId": "obj_clipper"
}

Clip groups extend the group shape with a required clipperId. The referenced clipper must resolve to a path object.

Text shape

{
  "id": "obj_text",
  "type": "text",
  "name": "Text 1",
  "transform": { ... },
  "style": { ... },
  "bounds": { ... },
  "visible": true,
  "locked": false,
  "parentId": null,
  "layerId": "layer_1",
  "content": "Hello",
  "layoutMode": "area",
  "paragraphSpans": [ ... ],
  "styleRuns": [ ... ],
  "frame": { "width": 320, "height": 180 },
  "storyId": null,
  "previousTextId": null,
  "nextTextId": null,
  "pathLayout": null,
  "overset": false
}

Text objects carry the styled text model directly: text content, paragraph spans, style runs, frame information, threading metadata, text-on-path metadata, and overset state.

Image shape

{
  "id": "obj_image",
  "type": "image",
  "name": "Image 1",
  "transform": { ... },
  "style": { ... },
  "bounds": { ... },
  "visible": true,
  "locked": false,
  "parentId": null,
  "layerId": "layer_1",
  "assetId": "asset_1",
  "intrinsicWidth": 1024,
  "intrinsicHeight": 1024
}

Image objects carry an assetId and intrinsic dimensions. The binary bytes are resolved separately through the embedded asset table.

For reverse engineering, the practical discriminator is: path owns geometry, group owns ordered children, clipGroup adds a required clipper reference, text owns styled text payload, and image owns an embedded-asset reference.

8. Text model and threaded flows

Text ranges

Text objects preserve paragraph spans and style runs, so the file stores structured styled text rather than only plain text.

Point vs area text

Point text must not define a frame. Area text must define one.

Threaded text

Story metadata uses storyId, previousTextId, and nextTextId. The current loader checks reciprocal links and rejects invalid text threading.

Text on path

Optional pathLayout metadata stores path target ID, offset, direction, and related layout controls. The path target must resolve to a real path object.

Overset state

Text objects persist an overset flag.

Image references

Image objects reference image assets by ID. The object payload does not inline the binary asset bytes into the text or image object record itself.

The file stores text content as a UTF-16 JavaScript string, paragraph spans as paragraph-level range/style records, and style runs as character-range style records. For file compatibility, those ranges and links need to remain internally coherent.

9. VECTOR_LAYER_SYSTEM@1

The vector layer system is serialized separately from the scene graph. Objects carry layerId references, and this payload defines what those IDs mean.

VECTOR_LAYER_SYSTEM@1 serializes the current vector layer records, the bottom-to-top layer order, the active layer ID, and the layer counter.

{
  "layers": [
    {
      "id": "layer_1",
      "name": "Layer 1",
      "visible": true,
      "locked": false,
      "opacity": 1,
      "blendMode": "normal"
    }
  ],
  "layerOrder": ["layer_1"],
  "activeLayerId": "layer_1",
  "layerCounter": 1
}

Each serialized vector layer currently carries id, name, visible, locked, opacity, and blendMode.

layerOrder is bottom-to-top. activeLayerId must resolve to a layer in that array. layerCounter is serialized as document metadata.

10. Optional editor-state entries

Files may also include editor-state entries beyond the scene graph and layer system.

VECTOR_VIEWPORT_STATE@1

Stores pan, zoom, and rotation.

VECTOR_STYLE_STATE@1

Stores fill and stroke state.

VECTOR_ACTIVE_TOOL_STATE@1

Stores the current primary tool plus shape subtype, even-scaling, center-draw, polygon, and spiral settings.

VECTOR_TEXT_DEFAULTS@1

Stores default text settings for newly created text objects.

VECTOR_GRID_STATE@1

Stores grid visibility, spacing, subdivisions, major-line cadence, color, and opacity values.

VECTOR_SNAPPING_STATE@1

Stores snapping enablement, individual snapping toggles, and the current tolerance in pixels.

VECTOR_COLORWAY_STATE@1

Stores colorway enablement plus the current list of color anchors.

VECTOR_PATTERN_MODE_STATE@1 and VECTOR_NODE_GRAPH_STATE@1

Store pattern-mode display settings and the serialized node-graph workspace.

Representative small-payload shapes

{
  "VECTOR_VIEWPORT_STATE@1": {
    "pluginName": "VECTOR_VIEWPORT_STATE@1",
    "version": "1.0.0",
    "data": { "panX": 0, "panY": 0, "zoom": 1, "rotation": 0 }
  },
  "VECTOR_STYLE_STATE@1": {
    "pluginName": "VECTOR_STYLE_STATE@1",
    "version": "1.0.0",
    "data": {
      "fill": { ... },
      "stroke": { ... }
    }
  }
}

Other payload families are compact JSON payloads: VECTOR_ACTIVE_TOOL_STATE@1 writes the current tool plus shape/polygon/spiral controls, VECTOR_GRID_STATE@1 writes visibility plus spacing/subdivision/color settings, VECTOR_SNAPPING_STATE@1 writes snapping toggles plus tolerance, VECTOR_COLORWAY_STATE@1 writes enabled plus anchor records, and VECTOR_PATTERN_MODE_STATE@1 writes visual toggles and colors for pattern-mode overlays.

11. Embedded assets and binary routing

Embedded image bytes are stored as plugin-owned binaries under plugins/VECTOR_DOCUMENT@1/image-assets/<assetId>.bin.

Binary filename

Current files use image-assets/<assetId>.bin inside the VECTOR_DOCUMENT@1 plugin directory.

Metadata first

Asset metadata and binary payloads are paired through id and binaryFilename.

Required pairings

Missing asset metadata, missing asset binaries, or image objects that reference unknown assets are all treated as invalid.

Unused asset cleanup

Asset tables may include entries that are not referenced by any object.

Asset metadata shape

{
  "id": "asset_1",
  "mimeType": "image/png",
  "width": 1024,
  "height": 1024,
  "byteLength": 88421,
  "sha256": "7f...ab",
  "filename": "example.png",
  "binaryFilename": "image-assets/asset_1.bin"
}

The binary path is plugin-relative. Inside the ZIP, files place that binary at plugins/VECTOR_DOCUMENT@1/image-assets/<assetId>.bin. Image objects then reference the metadata row through assetId.

12. Consistency rules

A valid .trellis file is more than syntactically valid JSON. The scene graph, layer data, and embedded asset references need to agree with one another.

Artboards

Artboards must have valid IDs, names, dimensions, and a resolvable active artboard.

Paths

Path objects must carry either canonical geometry data or commands.

Groups and clip groups

Groups must declare children. Clip groups must also declare a valid clipperId.

Text

Text payloads are checked for content, frame/layout consistency, reciprocal story links, and valid text-on-path targets.

Images and assets

Image objects must reference existing assets, and asset metadata must have valid MIME type, dimensions, and checksum fields.

Hierarchy topology

Objects should appear in ordering arrays exactly where their parent and root relationships say they do.

In practical terms, the important checks are duplicate IDs, missing geometry, missing children, missing clipperId, invalid text-link references, image objects that point at missing assets, and order arrays that contradict parent links.

13. Compatibility and versioning

  • Files use the .trellis suffix.
  • The loader also accepts .zip containers with the same internal layout.
  • The shared manifest currently writes container version 1.0.0.
  • Legacy files with empty plugin version strings are still accepted, but load with a warning.
  • The loader does not currently branch into separate historical codepaths by document version.
  • The format contract is the contents of the ZIP, not just the filename suffix.