Skip to content

The .mtcanvus file format

A .mtcanvus file is a complete copy of one canvas in a single file: the canvas description and every file the canvas uses. This page describes how the file is built, so that you can archive, inspect or index exports with your own tools. To export and import canvases in Canvus, see Snapshots and export.

A .mtcanvus file is not a zip archive. It is a MultiTaction container format: a short header, two compressed JSON blocks that describe the canvas, and the canvas's files stored one after another, unchanged.

Note

Canvus releases before 2.0 exported canvases as zip archives named *.mt-canvus-canvas.zip. Current Canvus versions cannot import these files: dropping one on a folder in the Canvas List fails with "Importing 1.x canvases is not supported".

Warning

A .mtcanvus file is not encrypted, even when the canvas has a password. Anyone who has the file can read its content with the information on this page. Store and transfer it as you would the original files.

Layout

The file is a sequence of blocks, in this order:

Block Content
Start key The 10 bytes multisite followed by a zero byte
Format block A UTF-8 JSON object followed by a zero byte
Canvas metadata A 4-byte length, then the compressed metadata JSON
Canvas items A 4-byte length, then the compressed item JSON
Assets Zero or more asset records, one per file the canvas uses
End key The 4 bytes end followed by a zero byte

General rules:

  • Numbers are unsigned integers in little-endian byte order.
  • Text is UTF-8.
  • A compressed block is in Qt's qCompress format: a 4-byte big-endian uncompressed length, followed by a zlib stream. Skip the first 4 bytes and any zlib library can decompress it.

Format block

The format block describes the container, not the canvas:

{
    "created": "2026-06-19T13:44:56.848",
    "version": "2"
}
  • version: the container version. Canvus 3.0 and later write version 2. Version 1 was written by Canvus 2.x, and Canvus converts it on import.
  • created: the local date and time the file was written.

Canvas metadata

The metadata block is a JSON object that describes the canvas as a whole. For example:

{
    "id": "36b1ebf0-c49b-4f79-bf3e-aa9ad39739fc",
    "name": "Test",
    "type": "mt-canvus",
    "version": "27",
    "state": "active_autosave",
    "preview": "26b417f84ef67060efe115a26b1ba86da7d888f61337c4ed694dded816e16096",
    "size": "0"
}
  • id: the canvas ID.
  • name: the canvas name. On import, Canvus can give the canvas a different name.
  • type: always mt-canvus for a Canvus canvas.
  • version: the version of the canvas data structure. This is not the same as the container version in the format block.
  • preview: the hash of the asset that holds the canvas preview image (see Hashes).

Other fields can appear, for example when the canvas has a password. Ignore fields you do not recognize.

Canvas items

The item block is a JSON object with two fields:

  • rootNodeId: the ID of the canvas's root item.
  • nodes: a flat list of every item on the canvas.

Each item has the same five fields:

{
    "id": "3750d5fa-6577-4f6b-9bda-1307128d29c5",
    "name": "3750d5fa-6577-4f6b-9bda-1307128d29c5",
    "parentId": "55e81c3f-0ac0-47d3-804d-83ade0b71659",
    "type": "canvus_note",
    "attributes": [
        {"name": "location", "value": "MakPRXYgV0U="},
        {"name": "size", "value": "AACWQwAAlkM="}
    ]
}
  • type: the kind of item. Examples are canvus_note, canvus_image_asset, canvus_pdf_asset, canvus_video_asset, canvus_annotation (ink), canvus_anchor, canvus_presentation and canvus_canvas_background. The root item has the type canvus_shared_canvas.
  • parentId: the item this item belongs to. Items placed directly on the canvas have the root item as their parent; ink drawn on an image has the image as its parent.
  • attributes: the item's properties.

An attribute holds a single value in value, or a list of values in values together with a count in size. Both are Base64-encoded binary data in little-endian byte order:

Data type Encoding Example attributes
Number 4-byte float depth, scale, line-width
Point or size Two 4-byte floats (x, y) in canvas pixels location, size
Color Four 4-byte floats (red, green, blue, alpha), from 0 to 1 background-color, text-color, line-color
Text A 4-byte length, the UTF-8 bytes, then zero bytes up to a multiple of 4 title, original-filename, source

For example, the location value MakPRXYgV0U= decodes to the two floats 2298.57 and 3442.03, the position of the note on the canvas.

Items that show a file (images, videos, PDFs, the canvas background) store the file's public hash as hexadecimal text in their source attribute (background-image for the canvas background). The hash links the item to its asset record.

Asset records

Each file the canvas uses is stored once, however many items show it. An asset record is:

Field Size Content
Asset key 4 bytes ast followed by a zero byte
Hash length 4 bytes The length of the hash, 32 in version 2
Hash as given The file's private hash (see Hashes)
File name length 4 bytes
File name as given The original file name, for example Workshop Title.png
File size 8 bytes The number of bytes that follow
File content as given The original file, byte for byte

After the last asset record comes the end key. A reader that meets end instead of ast has reached the end of the file.

Hashes

Canvus identifies each file by two BLAKE3 hashes (version 2):

  • The private hash is the BLAKE3 hash of the file content. Asset records store it, and the metadata preview field uses it.
  • The public hash is the BLAKE3 hash of the 32 bytes of the private hash. Items use it in source.

To find the file behind an item, compute the public hash of each asset record's private hash and match it with the item's source. To check that an asset is intact, compute the BLAKE3 hash of its content and compare it with the private hash.

Read a .mtcanvus file

This Python 3 script lists the canvas and its files, and saves the files and the decompressed JSON to a folder. It uses only the standard library.

import json, struct, sys, zlib
from pathlib import Path

def read_zstring(f):
    out = b""
    while (c := f.read(1)) not in (b"\0", b""):
        out += c
    return out

def read_block(f):
    (length,) = struct.unpack("<I", f.read(4))
    return json.loads(zlib.decompress(f.read(length)[4:]))  # skip the qCompress length

src, dest = Path(sys.argv[1]), Path(sys.argv[2])
dest.mkdir(parents=True, exist_ok=True)
with src.open("rb") as f:
    if f.read(10) != b"multisite\0":
        sys.exit("Not a .mtcanvus file")
    print("Format:", json.loads(read_zstring(f)))
    meta, items = read_block(f), read_block(f)
    print("Canvas:", meta.get("name"), "-", len(items["nodes"]), "items")
    (dest / "metadata.json").write_text(json.dumps(meta, indent=2))
    (dest / "items.json").write_text(json.dumps(items, indent=2))
    while (key := read_zstring(f)) == b"ast":
        (n,) = struct.unpack("<I", f.read(4)); private_hash = f.read(n)
        (n,) = struct.unpack("<I", f.read(4)); name = f.read(n).decode()
        (size,) = struct.unpack("<Q", f.read(8))
        out = dest / f"{private_hash.hex()[:12]}-{Path(name).name}"
        out.write_bytes(f.read(size))
        print(f"  {name} ({size} bytes)")
    if key != b"end":
        sys.exit("Unexpected data before the end key")

Run it with the export file and an output folder:

python3 read_mtcanvus.py "Workshop.mtcanvus" workshop-files

The script reads each file into memory. For very large exports, copy the file content in chunks instead.

Compatibility

  • Canvus reads every container version up to the one it writes. An older Canvus refuses a file written with a newer container version.
  • New fields can be added to the format block, the metadata and the items. Readers should ignore what they do not recognize.
  • The item types and attributes follow the canvas data structure, which changes between releases. The REST API reference describes the same items in a stable, documented form. Use the API when you need to create or change content, and treat .mtcanvus files as read-only archives outside Canvus.