Skip to main content

Overview

The JsonlMirror class provides append-only JSONL storage for captured events with automatic indexing for efficient querying.

Class: JsonlMirror

Constructor

constructor(
  rootDir: string = ".codaph",
  options?: {
    indexWriteMode?: "immediate" | "batch";
    autoFlushEveryEvents?: number;
  }
)
rootDir
string
default:".codaph"
Root directory for mirror storage
options.indexWriteMode
'immediate' | 'batch'
default:"immediate"
Index write strategy:
  • immediate: Flush indexes after every event
  • batch: Buffer index updates for better performance
options.autoFlushEveryEvents
number
default:"50"
Auto-flush indexes after N events (only applies to batch mode)

Methods

appendEvent

Appends an event to the appropriate daily segment.
async appendEvent(
  event: CapturedEventEnvelope
): Promise<MirrorAppendResult>
event
CapturedEventEnvelope
required
Event to append
segment
string
Relative path to segment file (e.g., events/abc123/2026/03/03/segment-20260303.jsonl)
offset
number
Line number within segment
checksum
string
16-character SHA-256 hash of event line
deduplicated
boolean
True if event was deduplicated (already exists)

appendRawLine

Appends a raw JSONL line to the session’s raw log.
async appendRawLine(
  sessionId: string,
  line: string
): Promise<void>
sessionId
string
required
Session identifier
line
string
required
Raw JSONL line (without trailing newline)
Storage location: {rootDir}/runs/{sessionId}/raw-codex.ndjson

flush

Flushes all buffered writes and indexes.
async flush(): Promise<void>

Index Structures

Manifest

Tracks all daily segments. Location: {rootDir}/index/{repoId}/manifest.json
interface RepoManifest {
  repoId: string;
  segments: Record<string, SegmentMeta>;
}

interface SegmentMeta {
  id: string;              // e.g., "20260303"
  relativePath: string;    // Segment file path
  from: string;            // Earliest event timestamp
  to: string;              // Latest event timestamp
  eventCount: number;      // Total events in segment
}

Sparse Index

Tracks sessions, threads, and actors. Location: {rootDir}/index/{repoId}/sparse-index.json
interface SparseIndex {
  repoId: string;
  sessions: Record<string, SparseSessionIndex>;
  threads: Record<string, SparseThreadIndex>;
  actors: Record<string, SparseActorIndex>;
}

interface SparseSessionIndex {
  from: string;
  to: string;
  eventCount: number;
  segments: string[];      // Segment paths containing this session
  threads: string[];       // Thread IDs in this session
  actors: string[];        // Actor IDs in this session
}

interface SparseThreadIndex {
  sessionId: string;
  from: string;
  to: string;
  eventCount: number;
  segments: string[];
}

interface SparseActorIndex {
  from: string;
  to: string;
  eventCount: number;
  sessions: string[];      // Sessions by this actor
  segments: string[];
}

Event ID Index

Enables deduplication and event lookup. Location: {rootDir}/index/{repoId}/event-ids.json
interface EventIdIndex {
  repoId: string;
  events: Record<string, EventIdIndexEntry>;
}

interface EventIdIndexEntry {
  segment: string;         // Segment containing event
  ts: string;              // Event timestamp
  sessionId: string;
  actorId: string | null;
}

Helper Functions

readManifest

async function readManifest(
  rootDir: string,
  repoId: string
): Promise<RepoManifest>

readSparseIndex

async function readSparseIndex(
  rootDir: string,
  repoId: string
): Promise<SparseIndex>

readEventIdIndex

async function readEventIdIndex(
  rootDir: string,
  repoId: string
): Promise<EventIdIndex>

readEventsFromSegments

async function readEventsFromSegments(
  rootDir: string,
  segmentPaths: string[]
): Promise<CapturedEventEnvelope[]>

Storage Layout

.codaph/
├── events/
│   └── {repoId}/
│       └── 2026/
│           └── 03/
│               └── 03/
│                   └── segment-20260303.jsonl
├── runs/
│   └── {sessionId}/
│       └── raw-codex.ndjson
└── index/
    └── {repoId}/
        ├── manifest.json
        ├── sparse-index.json
        └── event-ids.json

Usage Example

import { JsonlMirror } from './lib/mirror-jsonl';
import { createCapturedEvent } from './lib/core-types';

const mirror = new JsonlMirror('.codaph', {
  indexWriteMode: 'batch',
  autoFlushEveryEvents: 100,
});

// Append events
for (let i = 0; i < 200; i++) {
  const event = createCapturedEvent({
    source: 'codex_sdk',
    repoId: 'abc123',
    sessionId: 'session-1',
    threadId: 'thread-1',
    ts: new Date().toISOString(),
    sequence: i,
    eventType: 'item.completed',
    payload: { item: { type: 'reasoning', text: `Step ${i}` } },
  });

  const result = await mirror.appendEvent(event);
  if (result.deduplicated) {
    console.log('Duplicate event:', event.eventId);
  }
}

// Flush at end
await mirror.flush();

Performance Considerations

Buffering

Use batch mode for high-throughput scenarios:
const mirror = new JsonlMirror('.codaph', {
  indexWriteMode: 'batch',
  autoFlushEveryEvents: 50,
});

Deduplication

The mirror automatically deduplicates based on eventId. Duplicate appends return deduplicated: true without writing to disk.

Daily Segments

Events are automatically partitioned by UTC date, balancing:
  • File size: Prevents segments from growing unbounded
  • Query efficiency: Enables date-range filtering at segment level

Build docs developers (and LLMs) love