Data contracts
Personal data and runtime control state live in separate databases with separate owners and access paths.
The important boundary is not “two SQLite files.” It is that untrusted relational reads terminate at the personal data plane and cannot cross into credentials, schedules, approvals, or runtime internals.
Database boundary
| Database | Contains | Connection owner | App access |
|---|---|---|---|
.lamarck/data.db | D0 Events, D1 Documents, D2 Tables | Guard service | Read-only relational queries; authorized mutations through System API |
.lamarck/system.db | Connector setup, schedules, runs, approvals, credential records, internal state | Core control-plane modules | Never readable |
App ── system.query / mutate / writeDoc / writeEvent ──→ Guard ──→ data.db
Connector ── bound guard / state / auth capabilities ──→ Host
├─→ data.db
└─→ system.db
App ─────────────────────────────── ✕ ─────────────────→ system.db
An App opens neither file. Guard's query engine owns the only data.db connection used by untrusted SQL, and that engine has no system.db handle or path.
D0 / D1 / D2 contract
Append-only raw Event and audit history. Preserve what arrived and where it came from.
Mutable Markdown current state with stable path identity and an editable working tree.
Mutable SQLite Tables for structured current state, indexes, and derived read models.
The layers are not a promotion ladder where D0 is deleted after processing. Derived D2 data should remain traceable to inputs and rebuildable when interpretation changes.
D0 Events
CREATE TABLE events (
id TEXT PRIMARY KEY,
schema_version TEXT NOT NULL,
source TEXT NOT NULL,
type TEXT NOT NULL,
external_id TEXT,
started_at INTEGER NOT NULL,
ended_at INTEGER,
payload JSON NOT NULL,
created_at INTEGER NOT NULL
);
CREATE UNIQUE INDEX idx_events_dedup
ON events(source, external_id)
WHERE external_id IS NOT NULL;
sourceis Host-injected runtime provenance, not caller input.external_idis optional. When present, it is unique within a Source and makes capture retries idempotent.started_atandended_atdescribe the observed activity;created_atdescribes ingestion.- Database triggers reject UPDATE and DELETE on
events.
Large text may be written to the content-addressed blob store and referenced from an Event payload. Queries return the raw reference; an App calls resolveContentRef only when it needs the full content.
D1 Documents
CREATE TABLE docs (
id TEXT PRIMARY KEY,
content TEXT NOT NULL DEFAULT '',
metadata JSON,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
A Document ID is an extensionless path such as research/weekly-review. Its materialized file is pages/research/weekly-review.md. Database-to-file and file-to-database synchronization are both part of the contract.
An App implicitly owns the Document prefix apps/<app-id>/. Additional exact IDs or prefixes are listed in permissions.writes.docs. Path validation prevents a grant from escaping the pages/ namespace.
D1 is current state. Replacing the content is expected; the related audit Event records that a mutation occurred.
D2 Tables
D2 consists of ordinary user-defined SQLite tables in data.db. Use normal relational design, indexes, foreign keys, and SQL queries.
-- structural change: explicit schema workflow
lamarck promote "
CREATE TABLE sleep_summary (
date TEXT PRIMARY KEY,
deep_minutes INTEGER NOT NULL,
source_event_id TEXT NOT NULL
)
"
-- data read: System API or CLI
SELECT date, deep_minutes
FROM sleep_summary
ORDER BY date DESC;
system.queryaccepts relational SELECT/WITH; PRAGMA, ATTACH/DETACH, VACUUM, transaction control, and writes are rejected.system.mutateaccepts one INSERT, UPDATE, or DELETE on a Table declared inpermissions.writes.tables.system.transactionapplies a serializable statement list atomically under the same grants.- CREATE, ALTER, and DROP are explicit structural actions through
promote/demote, not incidental row writes.
Mutation and audit
Guard validates the active identity and permission ceiling, performs the durable mutation, and appends the corresponding D0 audit record. This keeps current state in D1/D2 while preserving the fact that it changed.
Audit does not turn operational noise into Timeline data. Connector retries, backfill warnings, readiness, and run status remain control-plane records in system.db.