# How Modly's Artifact Registry Manages and Persists Generated 3D Meshes Across Sessions

> Discover how Modly's artifact registry manages and persists 3D meshes across sessions. Learn how Modly stores mesh files and JSON sidecars to survive restarts.

- Repository: [lightningpixel/modly](https://github.com/lightningpixel/modly)
- Tags: internals
- Published: 2026-08-20

---

**Modly persists generated 3D meshes by storing mesh files alongside JSON sidecar files in the workspace, then scanning and rebuilding the asset library on demand to survive application restarts.**

The Modly artifact registry (implemented in `lightningpixel/modly`) provides a file-system-backed persistence layer for 3D mesh generation workflows. Unlike database-centric approaches, the registry treats the workspace directory as the single source of truth, enabling meshes to survive crashes, version upgrades, and cross-session collaboration without complex migration logic.

## Core Persistence Model: Mesh Files Plus Sidecar Metadata

Modly's artifact registry uses a dual-file strategy to track generated meshes. When a workflow node produces a 3D mesh, it writes:

- **The mesh file itself** — typically `.glb`, `.gltf`, or `.obj` format, stored under `Workflows/` or `Exports/`
- **A [`.artifact.json`](https://github.com/lightningpixel/modly/blob/main/.artifact.json) sidecar file** — adjacent to the mesh, containing `artifactId`, `versionId`, provenance chain, and manifest references

For rigged meshes, an additional [`.rigmeta.json`](https://github.com/lightningpixel/modly/blob/main/.rigmeta.json) file may appear with skeletal metadata. This design decouples binary asset storage from searchable metadata, allowing the registry to rebuild its index without parsing large geometry files.

In [`src/shared/types/artifacts.ts`](https://github.com/lightningpixel/modly/blob/main/src/shared/types/artifacts.ts), the provenance structure captures generation lineage:

```typescript
// From artifacts.ts — defines what travels with each generated mesh
interface ArtifactProvenance {
  artifactId: string;        // Stable identifier across versions
  versionId: string;         // Specific generation instance
  sourceWorkspacePath?: string;   // Input that produced this mesh
  manifestWorkspacePath?: string; // Workflow definition
}

```

The sidecar pattern ensures that even if the mesh file is moved or archived externally, its origin and relationships remain discoverable.

## Workspace Scanning and Asset Discovery

When the application initializes or when the UI refreshes, the **Artifact Registry Service** performs a complete filesystem scan rather than maintaining an in-memory database. This approach guarantees consistency with the actual workspace state.

The scan operates through `listWorkspaceAssetLibrary()` in [`electron/main/artifact-registry-service.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/artifact-registry-service.ts):

```typescript
// Simplified flow of the registry's discovery process
async function listWorkspaceAssetLibrary(workspaceRoot: string): Promise<AssetLibraryEntry[]> {
  const roots = ['Workflows', 'Exports'];
  const candidates = await Promise.all(
    roots.map(root => collectFiles(path.join(workspaceRoot, root)))
  );
  
  return candidates
    .flat()
    .filter(p => !isSidecarFile(p))        // Exclude .artifact.json, .rigmeta.json
    .map(p => classifyAssetLibraryCandidate(p))  // Determine capability: mesh, rigged-mesh, etc.
    .filter(c => isSupportedCapability(c))
    .map(c => buildEntry(c));              // Enrich with metadata from sidecar
}

```

The registry filters out internal files by testing for [`.artifact.json`](https://github.com/lightningpixel/modly/blob/main/.artifact.json) and [`.rigmeta.json`](https://github.com/lightningpixel/modly/blob/main/.rigmeta.json) suffixes, ensuring these metadata carriers never appear as standalone library entries.

## Path Security and Normalization

Before any path enters the registry, `normalizeWorkspaceAssetPath()` validates:

- Path is non-empty and contains no encoded escape sequences
- Path does not traverse above the workspace root (no `../` exploits)
- Path resides under approved roots: `Workflows/` or `Exports/`

This security boundary prevents workflow nodes from writing meshes to arbitrary filesystem locations or reading outside the designated workspace.

## Asset Classification and Capability Detection

The `classifyAssetLibraryCandidate()` function determines what operations each asset supports based on:

1. **File extension** — `.glb`/`.gltf` maps to 3D model preview capability
2. **Sidecar presence** — existence of [`.rigmeta.json`](https://github.com/lightningpixel/modly/blob/main/.rigmeta.json) upgrades `mesh` to `rigged-mesh`
3. **Metadata completeness** — availability of `artifactId` enables provenance tracking

Classification results feed into the `AssetLibraryEntry` type defined in [`src/shared/types/assetLibrary.ts`](https://github.com/lightningpixel/modly/blob/main/src/shared/types/assetLibrary.ts), which carries display metadata, openability flags, and preview kind selection.

## Metadata Enrichment from Sidecar Files

The `readMetadata()` function parses adjacent JSON sidecars to populate registry entries:

```typescript
// From artifact-registry-service.ts — sidecar parsing
async function readMetadata(meshPath: string): Promise<Partial<AssetLibraryMetadata>> {
  const sidecarPath = meshPath + '.artifact.json';
  
  try {
    const content = await fs.readFile(sidecarPath, 'utf-8');
    const parsed = JSON.parse(content);
    
    return {
      artifactId: parsed.artifactId,
      versionId: parsed.versionId,
      provenance: parsed.provenance,
      sourceWorkspacePath: parsed.sourceWorkspacePath,
      warnings: validateProvenanceLinks(parsed) // Check for dangling references
    };
  } catch {
    return { warnings: ['Missing or invalid artifact metadata'] };
  }
}

```

Missing or malformed sidecars produce warning annotations rather than fatal errors, allowing legacy meshes to appear in the library with degraded provenance visibility.

## IPC Interface for Cross-Process Access

The registry exposes three IPC channels in [`electron/main/ipc-handlers.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/ipc-handlers.ts) via `registerWorkspaceAssetLibraryIpcHandlers()`:

| Channel | Purpose |
|---------|---------|
| `workspace:library:list` | Retrieve all persisted mesh entries |
| `workspace:library:read` | Fetch metadata and preview info for specific path |
| `workspace:library:open` | Request editor access, validating openability |

Renderer processes invoke these channels to populate UI components without direct filesystem access:

```typescript
// Example: listing persisted meshes from a React/Vue component
const library = await window.ipcRenderer.invoke('workspace:library:list');

if (library.success) {
  const meshEntries = library.entries.filter(
    e => e.preview.kind === '3d-model'
  );
  
  meshEntries.forEach(entry => {
    console.log(`Mesh: ${entry.displayName}`);
    console.log(`Artifact ID: ${entry.artifactId ?? 'untracked'}`);
    console.log(`Generated: ${new Date(entry.mtimeMs).toLocaleString()}`);
  });
}

```

## Reading Specific Mesh Metadata

To inspect a particular mesh's provenance before loading it into the editor:

```typescript
const result = await window.ipcRenderer.invoke('workspace:library:read', {
  workspacePath: 'Exports/character-mesh.glb'
});

if (result.success) {
  const { entry, preview } = result;
  
  console.log('Capability:', entry.capability);      // 'mesh' or 'rigged-mesh'
  console.log('Artifact ID:', entry.artifactId);     // Links to generation record
  console.log('Provenance chain:', entry.provenance?.map(p => p.nodeId));
  
  // preview.kind === '3d-model' indicates GLB/GLTF viewer compatibility
}

```

## Opening Meshes for Editing

The `workspace:library:open` channel validates that an asset is openable before granting editor access:

```typescript
const openResult = await window.ipcRenderer.invoke('workspace:library:open', {
  workspacePath: 'Workflows/prop-export.glb'
});

if (openResult.success) {
  // Proceed to initialize 3D editor with the mesh path
  loadInEditor(openResult.entry.workspacePath);
} else {
  // Common failure: asset capability lacks 'openable' flag
  console.error('Cannot open:', openResult.error.message);
}

```

## Mesh Generation Workflow Integration

Workflow nodes like the mesh exporter ([`src/areas/workflows/nodes/mesh-exporter/processor.ts`](https://github.com/lightningpixel/modly/blob/main/src/areas/workflows/nodes/mesh-exporter/processor.ts)) interact with the persistence layer by:

1. Writing geometry to `Exports/` or `Workflows/` subdirectories
2. Generating fresh `artifactId` and `versionId` values
3. Serializing provenance to adjacent [`.artifact.json`](https://github.com/lightningpixel/modly/blob/main/.artifact.json)

This ensures every generated mesh immediately becomes discoverable through registry scans without explicit registration calls.

## Summary

- **Dual-file persistence** — Meshes live as standard geometry files with [`.artifact.json`](https://github.com/lightningpixel/modly/blob/main/.artifact.json) sidecars carrying identity and provenance
- **Filesystem-as-database** — Registry rebuilds its index from disk scans, eliminating synchronization failures between sessions
- **Security-hardened paths** — All workspace-relative paths validate against traversal attacks and restricted roots
- **Capability-based classification** — File extension plus metadata presence determines preview and editing eligibility
- **IPC-mediated access** — Main process handles filesystem operations; renderer calls typed channels for library operations

## Frequently Asked Questions

### Where does Modly store generated 3D meshes?

Modly writes mesh files to workspace subdirectories `Workflows/` or `Exports/`, with companion [`.artifact.json`](https://github.com/lightningpixel/modly/blob/main/.artifact.json) files in the same directory. This location is configurable per-workspace but always constrained to approved roots for security.

### How does the registry handle meshes created in previous sessions?

Since `listWorkspaceAssetLibrary()` performs fresh filesystem scans rather than consulting a cached database, any mesh files and sidecars remaining in the workspace automatically appear in the current session's asset library, complete with their original provenance metadata.

### What happens if the [`.artifact.json`](https://github.com/lightningpixel/modly/blob/main/.artifact.json) sidecar is missing or corrupted?

The registry treats missing sidecars as non-fatal warnings. The mesh file still appears in the library, but with `artifactId: undefined` and a warning annotation. Users can still open, preview, and edit the mesh; only provenance tracking and version-aware operations degrade.

### Can multiple users share persisted meshes through version control?

Yes. Because the persistence layer uses plain files and JSON sidecars, standard Git workflows apply. Teams can commit `Exports/` directories, and Modly reconstructs the full asset library state on each clone, including generation provenance and version history.