Skip to content

11. Layer 8 — Persistence

Contents of DISL 0.1

The persistence layer declares exactly how the diagrams users draw are stored as DID definitions: which format, which files, how identifiers are generated, how elements and keys are ordered, which view data is kept, how precise numbers are, how old DID definitions are migrated, and how several people can edit at once. The goal is that any two conforming runtimes produce identical files for the same diagram, that files diff and merge well in version control, and that stored diagrams remain readable for decades. The structure these settings configure, and the rules a writer follows, are specified by DID (DID-specification.md).

11.1 Overview

{
  "persistence": {
    "format": "json",
    "files": { "mode": "split", "model": "{name}.sm.json", "view": "{name}.sm.view.json" },
    "ids": { "strategy": "uuid-v7", "prefix": { "State": "st_", "Transition": "tr_" } },
    "references": "id",
    "ordering": { "elements": "type-then-id", "keys": "canonical" },
    "omitDefaults": true,
    "precision": { "canvas": 2 },
    "timestamps": { "format": "rfc3339", "timezone": "utc" },
    "view": { "store": ["bounds", "waypoints", "labelOffsets", "collapsed", "zIndex", "rotation", "viewport"],
              "styleOverrides": ["fill", "stroke.color", "font.weight"] },
    "metadata": ["languageVersion", "createdAt", "modifiedAt", "generator"],
    "migrations": [],
    "collaboration": { "mode": "crdt", "engine": "yjs" }
  }
}

11.2 Formats and encoding

Property Type Description
format "json" (default), "yaml", "jsonl", "cbor", "plugin:<name>" Serialization. All formats encode the same logical DID definition (11.4). jsonl writes one element per line, which merges particularly well. cbor is for large binary-efficient storage.
encoding "utf-8" Only UTF-8 is allowed.
indent int or "tab" Default 2. 0 writes minified JSON.
newline "lf", "crlf" Default "lf".
finalNewline bool Default true.
compression "none", "gzip", "zip-bundle" zip-bundle stores model, views and binary assets (images) in one archive with a manifest.
mediaType string Media type of DID definitions.

YAML output MUST quote strings that would otherwise be read as other types (the "Norway problem": no, yes, on, off, ~, numeric-looking strings, dates) and MUST NOT use anchors, aliases or tags.

11.3 Files

Property Type Description
mode "single" (default), "split", "per-element", "bundle" single: one file with model and views. split: model and view data in separate files, so layout edits never touch the model file. per-element: one file per top-level element in a directory plus an index (for very large models edited by teams). bundle: see compression: "zip-bundle".
model, view, index pattern File name patterns; {name} is the diagram's name, {id} and {type} the element's id and type for per-element.
assets {mode, dir} Binary assets: "inline" (base64), "external" (files in dir, referenced by relative URI), "bundle".
lock bool Write a lock file while open (for file-based single-user editing).

11.4 Stored structure

What a stored diagram looks like, independently of format and file split, is specified by DID, the Diagram Definition Language (DID-specification.md, section 3): the element, relation and view records, and the node and edge view data. Every DID definition names the DISL specification it was made with in its language object. The settings in this section and in 11.5–11.7 are declared by a specification and configure that structure; DID specifies what a conforming writer produces from them.

11.5 Identifiers

Property Type Description
strategy "uuid-v4", "uuid-v7" (default), "ulid", "nanoid", "sequential", "natural", "cel" How new ids are generated (DID, section 4). uuid-v7 and ulid are time-ordered, which keeps id-sorted files in creation order and helps merges. sequential uses per-type counters (State_1) and is only suitable for single-user editing. natural derives ids from key attributes (unique by constraint). cel evaluates expression.
prefix string or map TypeRef → string Type prefixes ("st_"), making ids self-describing in diffs.
expression Expression For cel: context create, must return a string; uniqueness is enforced by appending -2, -3, …
stable bool Ids never change once assigned, even if natural keys change (the first derived id is kept). Default true.
pattern regex Allowed id syntax; default ^[A-Za-z0-9_.:#-]{1,128}$.

11.6 View data and style overrides

view.store lists which kinds of view data are persisted: bounds, waypoints, anchors, labelOffsets, collapsed, zIndex, rotation, params, ports, viewport, guides, pinned, settings. Anything not listed is recomputed on load (by layout or defaults). A pure layout-driven language stores nothing but the model. DID, section 5, specifies the stored form.

view.styleOverrides is "none" (default), "all", or a list of style property paths users may override per element ("fill", "stroke.color", "stroke.dash", "font.size"). Overrides are stored in NodeView.style / EdgeView.style and have the highest precedence (6.1).

11.7 Ordering, precision and canonical form

Property Type Description
ordering.elements "creation", "id", "type-then-id", "tree", "cel" Order of elements and relations. tree writes parents before children (depth-first, siblings by order). Default "id".
ordering.orderBy Expression For cel: sort key per element.
ordering.keys "canonical", "alphabetical" Key order within objects. canonical: the order of this specification's tables (id, type, parent, slot, order, attributes, ports; then x-); attribute keys in metamodel declaration order.
omitDefaults bool Do not write attribute values equal to their default, view sizes equal to the notation default, or empty collections. Default true.
precision {canvas, numbers} Decimal places for canvas coordinates (default 2) and for number attributes without precision facet (default: shortest round-trip representation).
timestamps {format, timezone} format: "rfc3339" (default) or "epoch-ms"; timezone: "utc" (normalise to Z) or "preserve" (keep the offset entered).
canonical bool Follow RFC 8785 (JSON Canonicalization Scheme) number and string serialization. Default true.

A conforming writer applies these settings as DID, section 6, specifies, including its determinism requirement: the same logical DID definition and specification MUST produce byte-identical output. Suppressions of constraint problems are stored as DID, section 3, describes.

11.8 Metadata and specification embedding

metadata lists the metadata a DID definition carries in meta: languageVersion (always written in language.version), createdAt, modifiedAt, generator, authors (from env.user if the user consents), title (mirrors a diagram attribute), checksum.

definition controls how DID definitions refer to their specification: { "embed": "none" | "reference" | "inline", "uri": "https://…/statemachine-1.2.0.dis" }. reference writes language.definition with the URI of the specification and its integrity hash; inline embeds the whole specification (self-contained archives). The property names definition are kept unchanged from the earlier format.

11.9 Migrations

Migrations upgrade DID definitions written with older language versions. They run on load, before validation, in version order.

{
  "migrations": [
    {
      "from": ">=1.0.0 <1.1.0", "to": "1.1.0",
      "doc": "Renamed 'label' to 'name'; transitions got explicit priorities.",
      "steps": [
        { "renameAttribute": { "type": "State", "from": "label", "to": "name" } },
        { "setAttribute": { "type": "Transition", "attribute": "priority", "value": "0", "when": "!has(element.attributes.priority)" } }
      ]
    },
    {
      "from": ">=1.1.0 <2.0.0", "to": "2.0.0",
      "steps": [
        { "renameType": { "from": "EndState", "to": "FinalState" } },
        { "transform": { "type": "State", "cel": "element.attributes.with({'entryAction': element.attributes.?entry.orValue('')})" } },
        { "deleteAttribute": { "type": "State", "attribute": "entry" } },
        { "convertView": { "system": "canvas", "x": "value * 2.0", "y": "value * 2.0" } }
      ]
    }
  ]
}
Step Fields Effect
renameType from, to Changes type of elements, relations and references.
renameAttribute type, from, to Renames stored attribute keys (including in subtypes).
deleteType type, mode ("delete", "keep-as-unknown") Removes elements of a type (and attached relations).
deleteAttribute type, attribute
setAttribute type, attribute, value (Expression), when Sets a value, for example a default for a new required attribute.
convertAttribute type, attribute, cel Converts a value; value is the old value.
transform type, cel Returns a replacement attributes map; element is the raw element record as a map.
retype type, to, when Changes the type conditionally.
convertView system, x, y, w, h Converts stored view coordinates (unit or origin changes).
renamePort, renameSlot
plugin name, args Custom migration.

Migration expressions run in the migration context (12.3), which sees raw records as maps (element, value, document) rather than typed elements, because the old DID definition does not conform to the new metamodel. A migration that fails aborts loading; the original file is never modified until the user saves. Runtimes SHOULD tell users that a diagram was migrated and to which version, and MAY keep a backup.

DID definitions with a newer minor or patch language version than the loaded specification are opened with a warning; newer major versions are opened read-only or refused.

11.10 Collaboration

Property Type Description
mode "none" (default), "lock", "ot", "crdt" Single user; pessimistic element locks; operational transformation; conflict-free replicated data types.
engine "yjs", "automerge", "plugin:<name>" CRDT or OT engine.
granularity "element", "attribute" Conflict unit. attribute merges concurrent edits of different attributes of one element.
text "replace", "merge" Concurrent edits of text/string attributes: last writer wins or character-level merge.
ordering "fractional-index" Order keys for ordered children and lists that merge without renumbering.
presence bool Share cursors and selections.
conflicts "last-writer-wins", "report" Whether semantic conflicts (both sides valid, the merge invalid) are reported as constraint problems attributed to the merge.

The CRDT state is a transport concern; the persisted DID definition remains the canonical form (DID, section 6). Constraints are re-evaluated after merges.

11.11 Import and export

persistence.export and persistence.import declare conversions: [{ "format": "svg" | "png" | "pdf" | "did-fragment" | "plugin:<name>", "label", "doc", "options" }]. did-fragment writes a DID fragment (DID, section 7). Image exports render the notation exactly as on screen (without interaction states) at a chosen scale; pdf honours canvas.page. Constraints with timing: "export" run before export.

Illustrated by

Referenced from