Living Document Notice
Published 2026-09-13. The evolving architecture, revisions, and connected notes for this dispatch live in the Stax Digital Garden.
The Local-First Data Contract
Summary
Cloud-centric productivity software treats the remote database as the primary source of truth. Under this architecture, the client device behaves as a thin cache that stalls UI updates while waiting for remote HTTP acknowledgments. When the network connection drops or server infrastructure fails, the user interface disables editing capabilities or displays modal blocking spinners.
The local-first data contract redefines the primary path of execution. Every user mutation commits directly to local non-volatile storage before any network serialization occurs. Remote servers serve solely as asynchronous replication relays, ensuring complete application usability regardless of network availability.
Execution Order: Disk Commit Preceding Network Dispatch
Conventional web applications route write requests through an optimistic UI update queue that remains vulnerable to data loss if the browser tab terminates before the background HTTP call completes. The local-first contract enforces a strict sequential invariant:
[ User Input Event ]
|
v
[ Write to Local Non-Volatile Substrate ] ---> (Immediate Success Return to UI)
|
v (Background Event Hook)
[ Append to Local Outbox Log ]
|
v (Asynchronous / Offline-Tolerant)
[ Network Dispatch to Sync Relay ]
Under this operational flow, the write operation completes as soon as local storage flushes the record. The network subsystem operates strictly as an out-of-band background consumer.
Architectural Criteria: Cloud-First vs. Local-First Contract
Enforcing local data authority requires adherence to concrete engineering constraints across the storage layer.
| Metric / Dimension | Conventional Cloud Architecture | Local-First Data Contract |
|---|---|---|
| Write Path Order | Network HTTP POST → DB → Client Ack | Local Disk Flush → UI Ack → Sync Outbox |
| Offline Availability | Read-only mode or blocked inputs | Full read and write without degradation |
| Storage Format | Proprietary server schema | Open file format (CommonMark, SQLite) |
| Latency Profile | 80–400 ms (RTT dependent) | 0.5–4 ms (Local flash memory) |
| Conflict Resolution | Server-side last-write-wins (LWW) | Deterministic merge rules |
| Vendor Dissolution | Potential total data loss | Data persists locally with open formats |
By eliminating network calls from the critical write path, local applications deliver consistent response times unaffected by network latency fluctuations or server load spikes.
Defining the Persistence Contract Interface
The FreeNext storage pipeline formalizes this operational contract through a TypeScript interface that guarantees immediate local durability:
export interface LocalFirstStorageContract {
commitLocalMutation(mutation: DocumentMutation): Promise<CommitReceipt>;
readLocalSnapshot(documentId: string): Promise<DocumentSnapshot>;
getPendingOutboxQueue(): Promise<OutboxEntry[]>;
}
export interface DocumentMutation {
mutationId: string;
documentId: string;
timestampEpochMs: number;
authorFingerprint: string;
baseHash: string;
patchPayload: string; // Unified diff or structural patch
}
export interface CommitReceipt {
mutationId: string;
localSequenceNumber: number;
diskFlushed: boolean;
persistedEpochMs: number;
}
export interface OutboxEntry {
sequenceNumber: number;
mutation: DocumentMutation;
retryCount: number;
lastAttemptEpochMs?: number;
}
export class DurableStorageBroker implements LocalFirstStorageContract {
private localDb: IDBDatabase;
constructor(db: IDBDatabase) {
this.localDb = db;
}
async commitLocalMutation(mutation: DocumentMutation): Promise<CommitReceipt> {
return new Promise((resolve, reject) => {
const tx = this.localDb.transaction(['documents', 'outbox'], 'readwrite');
const docsStore = tx.objectStore('documents');
const outboxStore = tx.objectStore('outbox');
// Update primary local document snapshot
docsStore.put({
id: mutation.documentId,
lastMutationId: mutation.mutationId,
updatedAt: mutation.timestampEpochMs,
});
// Queue for background relay replication
const outboxReq = outboxStore.add({
mutation,
createdAt: Date.now(),
retryCount: 0,
});
tx.oncomplete = () => {
resolve({
mutationId: mutation.mutationId,
localSequenceNumber: Number(outboxReq.result),
diskFlushed: true,
persistedEpochMs: Date.now(),
});
};
tx.onerror = () => reject(tx.error);
});
}
async readLocalSnapshot(documentId: string): Promise<DocumentSnapshot> {
return new Promise((resolve, reject) => {
const tx = this.localDb.transaction('documents', 'readonly');
const req = tx.objectStore('documents').get(documentId);
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error);
});
}
async getPendingOutboxQueue(): Promise<OutboxEntry[]> {
return new Promise((resolve, reject) => {
const tx = this.localDb.transaction('outbox', 'readonly');
const req = tx.objectStore('outbox').getAll();
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error);
});
}
}Prioritizing disk persistence over network dispatch insulates the user archive from external service outages and vendor infrastructure changes.
- Directus Target: freenext
- Garden Source Reference: NXT-1001 - The Application Is Just a Lens, BSN-1002 - Data Sovereignty Invariants, MOC - Data Liberation Workbenches, MOC - The Plain-Text Longevity Standard, MOC - Local-First Systems and Synchronization, MOC - Bosun PKM Tools