Developers · 5 of 11

The manifest

5 min

mem.json is the contract. Check memVersion first, parse by major version, and ignore fields you do not know. The shapes below are the spec’s.

interface MemManifest {
  memVersion: string;            // "1.0": check the major version before parsing
  tier: "portable" | "full";     // which projection this is
  profile: "bundled" | "linked"; // where the bytes are
  generator: string;             // "memoresse-mem/0.1"
  package: {
    id: string;
    title: string;
    event?: string; date?: string; location?: string;
    primaryThemes?: string[];
    narrativeSummary?: string;   // the arc of the whole memory, in prose
    involvedRoles?: string[];    // portable: roles, never names
    involvedPeople?: string[];   // full only: persona ids
    roleGraph?: MemRoleGraphEntry[];
    declaredEmotion?: DeclaredEmotion; // what the owner said they felt
  };
  assets: MemManifestAsset[];
  segments?: MemSegment[];       // ordered sub-events
  provenance: {
    algorithm: "sha256";
    manifestHash: string;
    assetCount: number;
    anchor?: MemAnchor;          // the OpenTimestamps proof, folded in
  };
  retrieval?: { instructions: string; manifestUrl?: string }; // linked profile only
  presentation?: MemPresentation; // how to play it: pace, why, slideOrder, sections, soundtrack
  song?: unknown;                 // the generated-soundtrack recipe
}

The package: the memory as a whole

title, event, date, location and primaryThemes say what this is. narrativeSummary is the arc of the whole memory in a paragraph. The people appear as involvedRoles with a roleGraph (each role, its closeness band, its coarse relations). declaredEmotion is what the owner said they felt. From this block alone a reader can introduce the memory.

Each asset: one moment

interface MemManifestAsset {
  id: string;
  kind: "photo" | "video" | "voice" | "note";
  caption?: string;
  description?: string;          // sentence-level prose: the user's story, else the caption
  themes?: string[];
  emotionalWeight?: number;      // 0..1, the value only
  sensitivity?: number;          // 0..1
  takenAt?: string;              // ISO 8601
  focalPoint?: FocalPoint;       // { x, y, zoom }: where to look
  focalPointEnd?: FocalPoint;    // optional: where a pan ends
  transcript?: string;           // voice assets: travels inline
  associatedAssets?: string[];   // a voice note -> its photo
  videoUrl?: string; startSec?: number; endSec?: number; // a clip: the kept window
  contentHash: string;           // sha256 hex: the asset's identity
  locator?: MemLocator;          // { kind: "embedded" | "url" | "lockbox", ref, bytes?, mediaType? }
  involvedRoles?: string[]; audioSpeakerRole?: string;   // portable
  involvedPeople?: string[]; audioSpeaker?: string;      // full only
  reactions?: Record<string, string[]>; hearts?: string[]; // full only
}

description is sentence-level prose and the highest-value field for anything that writes or narrates. emotionalWeight and sensitivity are values in 0..1; trust them as given, since the file does not carry what produced them. focalPoint says where to look and focalPointEnd turns that into a pan. A voice or note asset carries its transcript inline. A video clip carries videoUrl with startSec and endSec for the kept window. contentHash is the asset’s identity; locator is one place to fetch its bytes.

Segments and presentation: how it plays

segments is the sequence layer: ordered sub-events, each with a label, a time range and its assetIds. presentation says how to play the file: pace, the wider thread, the one-sentence why, an explicit slideOrder, sections for a card view, the soundtrackAssetId and its soundtrackBpm, and the vibe the sender confirmed. These are declarative values: a player honors them to reproduce the intended experience, or ignores them and still plays. song is the recipe behind a generated track.

The rest

provenance is chapter 7. tier, profile and retrieval are chapter 8. What travels and what does not is chapter 5.