inou/docs/SCHEMA.sql

93 lines
4.5 KiB
SQL

-- Inou Database Schema
-- Created: 2025-12-29
-- All IDs are 64-bit integers (hash of UUID)
-- All timestamps are Unix epoch (INTEGER)
-- String fields are encrypted unless noted
--------------------------------------------------------------------------------
-- ENUMS (defined in code, documented here)
--------------------------------------------------------------------------------
-- sex (ISO 5218): 0=unknown, 1=male, 2=female, 9=not_applicable
-- relation: 0=self, 1=parent, 2=child, 3=spouse, 4=sibling, 5=caregiver, 6=medical_professional, 7=other
-- status: 0=active, 1=deleted, 2=pending, 3=revoked, 4=processed, 5=in_review, ...
--------------------------------------------------------------------------------
-- DOSSIERS
--------------------------------------------------------------------------------
CREATE TABLE dossiers (
dossier_id INTEGER PRIMARY KEY,
email_hash TEXT UNIQUE, -- plaintext, for lookup
email TEXT, -- encrypted
name TEXT, -- encrypted
date_of_birth DATE, -- plaintext
sex INTEGER DEFAULT 0, -- enum
phone TEXT, -- encrypted
language TEXT DEFAULT 'en', -- plaintext
timezone TEXT, -- plaintext
auth_code INTEGER, -- 6-digit code
auth_code_expires_at INTEGER, -- epoch
last_login INTEGER, -- epoch
invited_by_dossier_id INTEGER, -- FK
created_at INTEGER -- epoch
);
CREATE UNIQUE INDEX idx_dossiers_email_hash ON dossiers(email_hash);
--------------------------------------------------------------------------------
-- DOSSIER ACCESS (sharing permissions)
--------------------------------------------------------------------------------
CREATE TABLE dossier_access (
accessor_dossier_id INTEGER, -- who has access
target_dossier_id INTEGER, -- whose dossier
relation INTEGER DEFAULT 0, -- enum
is_care_receiver INTEGER DEFAULT 0, -- boolean
can_edit INTEGER DEFAULT 0, -- boolean
status INTEGER DEFAULT 0, -- enum
created_at INTEGER, -- epoch
accessed_at INTEGER, -- epoch
PRIMARY KEY (accessor_dossier_id, target_dossier_id)
);
CREATE INDEX idx_access_accessor ON dossier_access(accessor_dossier_id);
CREATE INDEX idx_access_target ON dossier_access(target_dossier_id);
--------------------------------------------------------------------------------
-- ENTRIES (all data: imaging, labs, vitals, genome, uploads, etc.)
--------------------------------------------------------------------------------
CREATE TABLE entries (
entry_id INTEGER PRIMARY KEY,
dossier_id INTEGER NOT NULL, -- FK
parent_id INTEGER, -- FK self-ref (hierarchy)
product_id INTEGER, -- FK to products (optional)
category TEXT NOT NULL, -- encrypted, deterministic
type TEXT NOT NULL, -- encrypted, deterministic
value TEXT, -- encrypted
ordinal INTEGER DEFAULT 0, -- sort order within parent
timestamp INTEGER NOT NULL, -- epoch
timestamp_end INTEGER, -- epoch (for ranges)
status INTEGER DEFAULT 0, -- enum
tags TEXT, -- encrypted
data TEXT -- encrypted JSON
);
CREATE INDEX idx_entries_dossier_category_type ON entries(dossier_id, category, type);
CREATE INDEX idx_entries_dossier_timestamp ON entries(dossier_id, timestamp);
CREATE INDEX idx_entries_parent ON entries(parent_id, ordinal);
--------------------------------------------------------------------------------
-- AUDIT
--------------------------------------------------------------------------------
CREATE TABLE audit (
audit_id INTEGER PRIMARY KEY,
dossier_id INTEGER, -- who performed action
action TEXT, -- encrypted
target_dossier_id INTEGER, -- affected dossier (if applicable)
details TEXT, -- encrypted JSON
timestamp INTEGER -- epoch
);
CREATE INDEX idx_audit_dossier_timestamp ON audit(dossier_id, timestamp);
CREATE INDEX idx_audit_target_timestamp ON audit(target_dossier_id, timestamp);
--------------------------------------------------------------------------------
-- OBJECTS (binary files)
-- Stored on disk at: /objects/{dossier_id}/{aa}/{bb}/{entry_id}
-- where aa/bb are first 4 chars of entry_id for sharding
--------------------------------------------------------------------------------