Nodeterm Project File Model (V3): Workspace Index and Project Structure

Nodeterm's v3 project file model splits workspace data into a central index (workspace.json) stored in the user data directory and individual project files (.nodeterm/project.json) that contain serialized canvas state, separating project references from node data.

The eneskirca/nodeterm repository uses a sophisticated two-tier storage system in its v3 architecture. This design decouples the workspace index—which tracks project locations—from the actual project content, enabling efficient remote-SSH support and atomic saves. Understanding this structure is essential for developers integrating with nodeterm's storage layer or troubleshooting workspace issues.

The V3 Workspace Index (workspace.json)

The workspace index acts as the central registry for all projects without storing actual node data. According to the source code in src/core/workspace-files.ts, this file splits an in-memory workspace into the v3 index plus local project files for writing.

Index Location and Format

The index resides at app.getPath('userData')/workspace.json and follows this strict schema:

{
  "version": 3,
  "entries": [
    {
      "projectId": "project-1",
      "cwd": "/home/user/my-repo"
    },
    {
      "projectId": "project-2",
      "ssh": {
        "host": "example.com",
        "user": "alice",
        "port": 22
      }
    }
  ]
}

Each entry contains only the project identifier and connection details. The version: 3 field indicates the schema revision, as noted in src/main/index.ts at line 564.

Local vs. Remote References

The v3 index supports two distinct reference types:

  • Local projects: Use the cwd field containing the absolute path to the project directory
  • Remote SSH projects: Use the ssh object with host, user, and port properties

Neither entry type stores canvas data; both reference external .nodeterm/project.json files.

Project File Structure (.nodeterm/project.json)

Individual project files contain the complete canvas definition. The Project interface defined in src/shared/types.ts (lines 613-682) dictates the schema for these files.

Core Fields

Field Type Purpose
id string Stable UUID linking the project to its index entry
title string Human-readable name displayed in the tab bar
color string Hex color code for UI accents
nodes NodeState[] Serialized React Flow nodes (terminals, stickers, groups)
viewport Viewport Camera position and zoom level

Optional Configuration

Project files may include additional metadata fields:

  • icon: Custom icon definition (see src/shared/project-icon.ts)
  • collapsed: Boolean indicating sidebar tab state
  • kanban: Kanban board configuration with columns and labels
  • defaultPermissionMode: Agent permission handling settings
  • defaultAccountId: Default Claude account for agent nodes
  • defaultProjectView: Enum value 'canvas' or 'kanban' for default view
  • settings: Additional per-project settings (see project-settings.ts)

Data Flow: Loading, Saving, and Migration

The WorkspaceStore class in src/core/workspace-store.ts manages all v3 file operations, including the v2 to v3 migration logic at line 98.

Loading the Workspace

WorkspaceStore.load() executes a two-phase loading process:

  1. Reads workspace.json into memory
  2. Iterates through entries and loads each .nodeterm/project.json file referenced by projectId

This separation allows nodeterm to load individual projects on demand rather than parsing a monolithic file.

Persisting Changes

The v3 model uses differential saving to minimize disk I/O:

  • WorkspaceStore.writeProject(): Updates only the specific .nodeterm/project.json file when canvas data changes
  • WorkspaceStore.saveIndex(): Rewrites workspace.json only when the project list changes (add, remove, or reorder)

V2 to V3 Migration

On first run after upgrading, nodeterm executes an automatic migration:

  1. Assembles a v2 file from the legacy layout
  2. Writes the new v3 index to workspace.json
  3. Creates individual project files in their respective directories
  4. Backs up the original file with a *.bak extension

The test suite in src/core/workspace-store.test.ts validates this round-trip conversion and migration flow.

Programmatic Usage Examples

Interact with the v3 model using the WorkspaceStore API:

import { WorkspaceStore } from './core/workspace-store';

// Load the complete workspace (index + all projects)
const ws = await WorkspaceStore.load();

// Retrieve a specific project
const project = await WorkspaceStore.readProject('project-1');

// Modify canvas data
project.nodes.push({ 
  id: 'term-5', 
  kind: 'terminal', 
  data: { /* terminal config */ } 
});

// Persist changes to the project file only
await WorkspaceStore.writeProject(project);

Inspect the index directly for debugging:

// Read workspace.json without loading projects
const index = await WorkspaceStore.readIndex();
console.log(index.version);  // → 3
console.log(index.entries);  // Array of project references

Create new projects programmatically:

const newProject = {
  id: 'proj-new',
  title: 'My New Canvas',
  color: '#4A90E2',
  nodes: [],
  viewport: { x: 0, y: 0, zoom: 1 }
};

// Write the project file
await WorkspaceStore.writeProject(newProject);

// Register in the index
await WorkspaceStore.addIndexEntry({ 
  projectId: newProject.id, 
  cwd: '/home/me/new-proj' 
});

Key Source Files

Summary

  • The v3 model uses a split architecture: workspace.json stores references while .nodeterm/project.json files store canvas data
  • The workspace index supports both local (cwd) and remote (ssh) project references via the entries array
  • Project files contain React Flow nodes, viewport states, and optional kanban configurations according to the Project interface
  • WorkspaceStore provides atomic operations for reading individual projects without loading the entire workspace
  • Automatic migration converts v2 workspaces to v3 format, backing up legacy files before transformation

Frequently Asked Questions

Where is the nodeterm workspace index stored?

The v3 workspace index is stored at app.getPath('userData')/workspace.json, which resolves to the operating system's user data directory followed by the nodeterm application folder. This location ensures the index persists across application restarts while remaining separate from project-specific data.

What is the difference between the workspace index and project files in v3?

The workspace index (workspace.json) contains only lightweight metadata: the version number (3) and an array of entries mapping projectId values to either local directories (cwd) or SSH connections. Project files (.nodeterm/project.json) contain the heavy data: React Flow node states, viewport coordinates, kanban boards, and project settings. This separation enables nodeterm to load project lists instantly while deferring heavy canvas data loading until needed.

How does nodeterm handle remote SSH projects in the v3 model?

Remote projects use the ssh field in the index entry instead of cwd, specifying host, user, and port. The project file itself resides on the remote filesystem at .nodeterm/project.json relative to the SSH user's home directory or specified path. The v3 index tracks the connection parameters, while the remote filesystem stores the canvas data, allowing seamless switching between local and remote workspaces.

What happens during the v2 to v3 migration?

When nodetect detects a legacy v2 workspace, it automatically migrates data by parsing the old monolithic file, extracting individual projects, and writing them to separate .nodeterm/project.json files in their respective directories. It then creates the new v3 workspace.json index and backs up the original file with a .bak extension. This process is idempotent and preserves all project data while optimizing the storage structure for the new architecture.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →