Documentation menu

Documents and the file format

Description: Narratyr stores project data in text files for source control and team editing.

Prerelease. This page describes the files for document schema 0.0.80.

A Narratyr project uses a main .ntproj JSON file and a matching .ntproj.data folder. The main file contains definitions shared across the project. The data folder contains separate JSON files for quests, dialogues, entities, and variables.

Separate text files reduce source control conflicts across a team. They also let you inspect, compare, and merge authored data with existing source control tools.

A project on disk

A project uses a .ntproj file and a sibling folder with the same name plus a .data suffix:

MyGame.ntproj                                 project-level metadata
MyGame.ntproj.data/
├── dialogues/
│   └── supoh-suvam-basut-vivod.ngraph        one file per dialogue graph
├── quests/
│   └── lakur-tusoz-rigav-hazos.ngraph        one file per quest graph
├── entities/
│   ├── character/
│   │   └── nidoh-tabit-ropat-bodiv.nentity   one file per entity, under its type
│   └── item/
│       └── default-item-1.nentity
├── variables/
│   ├── globals.nvars                         variables, grouped by namespace
│   └── player.nvars
├── folders/
│   ├── graph/
│   └── entity/
│       └── performances-path.nstub           one marker per empty editor folder
├── assets/
│   └── portraits/female-2.png                binaries, arranged by asset path
├── docs/
│   └── lore/organizations/white_glove.md     your Markdown files
├── _index                                    generated, ignored by Git
└── .gitignore                                generated

Open the .ntproj file in Narratyr. When you move, rename, or copy a project, move both the .ntproj file and its .ntproj.data folder. Their base names must match.

Combined files. A .ntprojc file stores the authored document in one file. Binary assets and Markdown documents remain in a .data folder. This format works well for backups and solo projects, but the default split format reduces conflicts for teams.

Project file contents

The .ntproj file contains project-wide definitions that other files reference.

Key Contents
id, version Project ID and document schema version
entityTypes, propertySets Entity definitions and reusable groups of fields
enumerations, functionDefinitions Named value sets and functions available to expressions
assets, assetPaths Asset metadata, including ID, path, hash, size, and media type
propertySetPaths Folder tree for property sets
projectInfo Project name, AI prompts, and rich-text settings

Narratyr stores the collections that grow with your story in the data folder.

Compatibility fields. The project file contains empty arrays for graphs, entities, variables, graphPaths, and entityPaths. Narratyr stores those collections as individual files in the data folder.

Data folder contents

  • Graphs: dialogues/<id>.ngraph and quests/<id>.ngraph store one graph per file. Each file contains the graph's name, title, description, notes, editor path, nodes, and edges.

  • Entities: entities/<entityTypeId>/<id>.nentity stores one entity per file, inside a folder for its type. An existing entity keeps its original type, so its file stays in that folder.

  • Variables: variables/<namespace>.nvars stores a JSON array for each variable group. The filename uses the part of the variable name before its last underscore and converts it to lowercase.

    Variable name File
    Player_Level player.nvars
    Act1_JarlQuest_KilledJarl act1_jarlquest.nvars
    jarl_quest_started jarl_quest.nvars
    PlayerLevel globals.nvars

    The last underscore preserves longer prefixes, such as act1_jarlquest. Narratyr routes names without a usable prefix to globals.nvars. The variable editor shows the destination file while you type a name.

  • Empty folders: folders/graph/<id>.nstub and folders/entity/<id>.nstub preserve empty folders from the editor. Folders with items don't require a marker because each item records its editor path.

  • Assets: assets/ contains the binary files and follows the asset paths shown in the editor. The main project file contains their metadata.

  • Docs: docs/ contains your Markdown files without conversion. An external editor can edit the same files that appear in Narratyr.

Generated files

These are files you should leave out of version control.

  • _index: a tab-separated id → kind → path + name list of project items. Narratyr rewrites it during each save. Use it to identify an ID from a source control diff. Git ignores the file, so it doesn't create conflicts.
  • .fs-log.json: a record of pending asset operations. Narratyr uses it to undo disk changes when you cancel an asset edit before saving.
  • .save-journal.json: a temporary record that supports recovery from an interrupted save.

Narratyr writes a .gitignore file into the data folder for convenience. You can use any source control system.

Why projects use separate files in the data folder

Separate files help your team avoid merge conflicts and preserve useful version history for individual items.

Per-item storage reduces the conflict boundary to one graph, entity, or variable group. People working on different dialogues usually edit different files.

This layout also helps Perforce teams. Perforce locks individual files and provides less merge support than Git. A large project file can force the writing team to share one lock.

Entities use separate files for the same reason. File size isn't the concern. Different people can edit different characters without changing the same file.

Identifier-based filenames

Graph and entity filenames use the item's stable ID instead of its human-readable name. The file path doesn't change when the name changes.

Renaming a quest changes a field inside its file but doesn't move the file. If another person edits the quest concurrently, source control can merge the name change with edits to other fields.

Name-based filenames can create duplicate paths when a rename overlaps another edit. Git usually detects a file move, but large changes can appear as a deletion and a new file. Perforce doesn't detect moves during a merge. ID-based filenames avoid both cases.

  • Renames change file contents without changing the path.
  • Concurrent edits can merge when they affect different parts of the file.
  • Duplicate display names work because the ID identifies each item. Separate quests can each contain a fragment named "Intro."

Narratyr generates IDs from 64 random bits and encodes them as four pronounceable chunks, such as nidoh-tabit-ropat-bodiv. This encoding, called a proquint, makes IDs easier to read aloud and recognize in a diff.

Default IDs. Default project items can use readable IDs such as character and default-item-1. The editor assigns proquints to new items. The document format also accepts unique plain-text IDs.

Reading a file

Files use UTF-8 JSON with two-space indentation. Narratyr sorts object keys alphabetically at each level. Stable key order produces consistent text for the same data, which keeps diffs focused on content changes. Within a graph, Narratyr also sorts nodes and edges by ID because their array order has no effect.

This example shows entities/character/nidoh-tabit-ropat-bodiv.nentity:

{
  "externalId": "MiraThorne",
  "fieldValues": [
    { "value": "Mira Thorne", "varId": "full-name-field" },
    { "value": "Female", "varId": "character-gender-field" }
  ],
  "id": "nidoh-tabit-ropat-bodiv",
  "name": "Mira Thorne",
  "path": "/characters/npcs/",
  "profileImage": "gumur-rahab-vapor-jupub",
  "typeId": "character"
}

Each field value uses a field ID instead of its label. Renaming a field in an entity type doesn't rewrite the entities that use it. profileImage contains an asset ID from the project file's assets array. typeId identifies an entry in entityTypes, and externalId provides the stable value used by game code.

This example shows a dialogue node inside a .ngraph file's graphData.nodes array:

{
  "data": {
    "choiceSlots": [],
    "shortText": "Fair enough",
    "speakerEntityId": "nidoh-tabit-ropat-bodiv",
    "text": "Fair enough. Fire's warm, ale's cheap, and I don't ask twice.",
    "type": "dialogue"
  },
  "id": "bizog-nikov-vojul-ribug",
  "position": { "x": 1377, "y": 166.82 },
  "type": "dialogueNode"
}

The outer object contains canvas details such as id, type, position, width, height, and parentId. The data object contains the authored content, and the node type determines its fields. Each edge stores source, target, and sourceHandle. The handle identifies the output used by a branch.

This example shows an empty-folder marker at folders/entity/performances-path.nstub:

{
  "id": "performances-path",
  "path": "/performances/"
}

When merge markers or invalid JSON prevent Narratyr from reading a file, the project fails to open and the error names that file. Narratyr won't skip the file because a later save could then remove the missing item from the project.

Editor folders and disk folders

The editor uses virtual folders. Each graph and entity stores a path value such as /characters/npcs/, and Narratyr rebuilds the editor tree from those values. Each graph directory uses a flat list of files on disk.

Moving a quest to another editor folder changes one field in its graph file. Its location on disk stays the same, so source control doesn't need to detect a file move.

Save behavior

Narratyr serializes and hashes each item during a save. It compares each hash with the prior save and writes files with changed contents. It also removes files for deleted items. Editing a line of dialogue changes one .ngraph file.

This comparison prevents a save from rewriting unchanged files and adding them to a diff.

For a multi-file save, Narratyr first writes each item to a temporary file and records the operation in .save-journal.json. It then moves each temporary file into place with an atomic rename. If the process stops during a save, Narratyr uses the journal and temporary files to finish the operation when the project opens again.

Git workflow

Commit the .ntproj file and its complete .ntproj.data folder. The generated .gitignore already excludes temporary files.

Split storage changes how source control reveals a broken cross-graph reference. One change might delete a dialogue while another change retains a quest node that references it. Git can merge those changes without a text conflict. Narratyr validates references when the project opens and reports the missing target. Check validation results after large merges.

Perforce workflow

Narratyr writes project files directly without calling p4 edit. Configure the workspace for external tools:

  1. Enable the allwrite client option so files stay writable after sync. The default noallwrite option makes synced files read-only and causes saves to fail.
  2. Run p4 reconcile, or "Reconcile Offline Work" in P4V, before you submit changes. This command opens changed, added, and deleted files.

ID-based filenames make reconciliation more direct because edits don't move files. Perforce only needs to find edits, additions, and deletions.

Combined projects

The split format is the default, but Narratyr can also store the authored document in one JSON file. Save As offers these formats:

Extension Format
.ntproj Project file plus a separate data folder
.ntprojc Authored document combined into one file

The extension selects the file format. Narratyr also supports the legacy .rpgproj combined format. It can open and save existing .rpgproj files but doesn't create new ones.

The combined file contains the same authored data as the split files, so conversion preserves the document. Use this format for backups or tools that require one input file. Teams should use the split format to reduce source control conflicts.

Binary assets and Markdown documents stay in the .data folder for both formats.

Schema versions

The version field in the project file identifies the document schema. When the schema gains a field, its definition includes a default value. Newer Narratyr builds use these defaults to open older projects, then save the new fields during the next write.

Before version 1.0, document fields can still change. Keep projects in source control. Tools that read these files should ignore unfamiliar fields and handle missing fields.

Next

Read Dialogues and Quests for details about graph files. Variables and conditions explains shared state and the naming rule for each .nvars file.