Files
knox/internal/db/schema.go
T
david 8c054094a1 feat: M3 peer gossip protocol
Refs #1

- peers table (peer_id, addr, cursor, last_handshake)
- watch serves HTTP API: GET /v1/ping (knowledge vector),
  GET /v1/log?node=&after= (cursor-paged pull), POST /v1/obs/batch
- anti-entropy sweep (Run): ping, pull what we lack, push own obs;
  echo suppressed by node_id ownership; reconcile-on-pull
- config via KNOX_PEERS / KNOX_PEER_ADDR; knox gossip status
- db: GossipObservation wire type, PushObservations, ObservationsAfter,
  KnowledgeVector, peer upsert/list
- integration tests: bidirectional convergence + idempotency
2026-08-29 04:53:26 -07:00

152 lines
5.4 KiB
Go

package db
const Schema = `
-- IMMUTABLE LOG: every observation is append-only, never mutated
CREATE TABLE IF NOT EXISTS observations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
fingerprint TEXT NOT NULL,
source_id TEXT NOT NULL,
source_path TEXT,
project TEXT,
content_type TEXT,
title TEXT,
summary TEXT,
collected_at TEXT NOT NULL DEFAULT (datetime('now')),
created_at TEXT,
line_start INTEGER,
line_end INTEGER,
confidence REAL DEFAULT 0.5,
ingester_version TEXT DEFAULT '1',
trigger TEXT,
provenance TEXT DEFAULT '{}',
node_id TEXT NOT NULL DEFAULT '',
hcl INTEGER,
received_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_obs_fingerprint ON observations(fingerprint);
CREATE INDEX IF NOT EXISTS idx_obs_collected ON observations(collected_at);
CREATE INDEX IF NOT EXISTS idx_obs_created ON observations(created_at);
CREATE INDEX IF NOT EXISTS idx_obs_source ON observations(source_id, source_path);
CREATE INDEX IF NOT EXISTS idx_obs_project ON observations(project);
CREATE INDEX IF NOT EXISTS idx_obs_trigger ON observations(trigger);
-- MATERIALIZED CACHE: current state, reconstructable from observations
CREATE TABLE IF NOT EXISTS entries (
fingerprint TEXT PRIMARY KEY,
source_id TEXT NOT NULL,
source_path TEXT,
project TEXT,
content_type TEXT,
title TEXT,
summary TEXT,
first_seen TEXT,
last_seen TEXT,
ref_count INTEGER DEFAULT 1,
last_confidence REAL DEFAULT 0.5
);
CREATE INDEX IF NOT EXISTS idx_entries_project ON entries(project);
CREATE INDEX IF NOT EXISTS idx_entries_source ON entries(source_id);
CREATE INDEX IF NOT EXISTS idx_entries_seen ON entries(last_seen);
-- PROVENANCE LINKS: multi-hop lineage between entries
CREATE TABLE IF NOT EXISTS provenance_links (
id INTEGER PRIMARY KEY AUTOINCREMENT,
descendant_fp TEXT NOT NULL,
ancestor_fp TEXT NOT NULL,
hop_distance INTEGER NOT NULL,
relation TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_pl_descendant ON provenance_links(descendant_fp);
CREATE INDEX IF NOT EXISTS idx_pl_ancestor ON provenance_links(ancestor_fp);
-- REFLECTION STATE: per-session tracking
CREATE TABLE IF NOT EXISTS sessions (
session_id TEXT PRIMARY KEY,
project TEXT,
title TEXT,
status TEXT DEFAULT 'active',
agent_count INTEGER DEFAULT 0,
started_at TEXT,
ended_at TEXT,
last_seen TEXT DEFAULT (datetime('now')),
indexed INTEGER DEFAULT 0
);
-- SOURCE REGISTRY
CREATE TABLE IF NOT EXISTS sources (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
enabled INTEGER DEFAULT 1,
last_event TEXT,
created_at TEXT DEFAULT (datetime('now'))
);
INSERT OR IGNORE INTO sources (id, name) VALUES ('opencode-session', 'Opencode Session Diffs');
INSERT OR IGNORE INTO sources (id, name) VALUES ('opencode-log', 'Opencode Log Files');
INSERT OR IGNORE INTO sources (id, name) VALUES ('git', 'Git History');
INSERT OR IGNORE INTO sources (id, name) VALUES ('filesystem', 'File System Watcher');
-- KNOWLEDGE THREADS: why observations exist, what questions they answer
CREATE TABLE IF NOT EXISTS threads (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
motivation TEXT,
status TEXT DEFAULT 'active',
priority TEXT DEFAULT 'medium',
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now')),
resolved_at TEXT,
tags TEXT DEFAULT '[]',
provenance TEXT DEFAULT '{}',
cluster_key TEXT
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_threads_cluster ON threads(cluster_key) WHERE cluster_key IS NOT NULL AND cluster_key != '';
-- SETTINGS: key-value store for runtime configuration
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT
);
-- PEERS: other knox nodes known to this one (gossip ring)
CREATE TABLE IF NOT EXISTS peers (
peer_id TEXT PRIMARY KEY, -- remote node_id
addr TEXT, -- e.g. http://192.168.1.20:8931
name TEXT,
last_handshake TEXT,
cursor INTEGER DEFAULT 0, -- remote's max_hcl we've seen (knowledge)
created_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_threads_status ON threads(status);
CREATE TABLE IF NOT EXISTS thread_observations (
thread_id INTEGER NOT NULL REFERENCES threads(id) ON DELETE CASCADE,
observation_id INTEGER NOT NULL REFERENCES observations(id) ON DELETE CASCADE,
relevance TEXT,
added_at TEXT DEFAULT (datetime('now')),
PRIMARY KEY (thread_id, observation_id)
);
CREATE TABLE IF NOT EXISTS thread_notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
thread_id INTEGER NOT NULL REFERENCES threads(id) ON DELETE CASCADE,
note TEXT NOT NULL,
created_at TEXT DEFAULT (datetime('now'))
);
-- TOPICS: TF-IDF clustered groups of related observations
CREATE TABLE IF NOT EXISTS topics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
keywords TEXT,
score REAL DEFAULT 0,
created_at TEXT DEFAULT (datetime('now'))
);
`