Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Data Model

Overview

The data model defines how Nexus persists and manages state using SQLite. This includes VM configurations, workspace metadata, networking, and operational state.

An abstraction layer over SQLite allows swapping to Postgres or etcd for clustering.

Schema

-- Nexus Database Schema (Pre-Alpha)
--
-- During pre-alpha, schema changes are applied by:
-- 1. Updating this file
-- 2. Deleting the database file
-- 3. Restarting the daemon (schema recreates automatically)

-- Application settings (key-value store)
CREATE TABLE settings (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL,
    type TEXT NOT NULL CHECK(type IN ('string', 'int', 'bool', 'json'))
);

-- Tags for organizational categorization
CREATE TABLE tags (
    name TEXT PRIMARY KEY,
    description TEXT,
    color TEXT,       -- hex color for UI display (e.g., "#FF5733")
    text_color TEXT   -- hex color for contrast (e.g., "#FFFFFF")
);

-- VMs: Firecracker microVM instances
CREATE TABLE vms (
    id TEXT PRIMARY KEY,
    name TEXT UNIQUE,
    role TEXT NOT NULL CHECK(role IN ('portal', 'work', 'service')),
    state TEXT NOT NULL CHECK(state IN ('created', 'running', 'stopped', 'crashed', 'failed')),
    cid INTEGER NOT NULL UNIQUE,          -- vsock context ID
    vcpu_count INTEGER NOT NULL DEFAULT 1,
    mem_size_mib INTEGER NOT NULL DEFAULT 128,
    config_json TEXT,                      -- full Firecracker config snapshot
    pid INTEGER,                           -- Firecracker process ID (NULL when not running)
    socket_path TEXT,                      -- Firecracker API socket (NULL when not running)
    uds_path TEXT,                         -- vsock UDS base path
    console_log_path TEXT,
    created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
    updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
    started_at INTEGER,
    stopped_at INTEGER
);

-- Master images: read-only btrfs subvolumes
CREATE TABLE master_images (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL UNIQUE,
    subvolume_path TEXT NOT NULL UNIQUE,
    size_bytes INTEGER,
    created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
);

-- Workspaces: btrfs subvolume snapshots assigned to VMs
CREATE TABLE workspaces (
    id TEXT PRIMARY KEY,
    name TEXT UNIQUE,
    vm_id TEXT,                            -- NULL if unattached
    subvolume_path TEXT NOT NULL UNIQUE,
    master_image_id TEXT,                -- master image this was snapshotted from
    parent_workspace_id TEXT,              -- NULL if snapshotted from base
    size_bytes INTEGER,
    is_root_device INTEGER NOT NULL DEFAULT 0 CHECK(is_root_device IN (0, 1)),
    is_read_only INTEGER NOT NULL DEFAULT 0 CHECK(is_read_only IN (0, 1)),
    attached_at INTEGER,
    detached_at INTEGER,
    created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
    FOREIGN KEY (vm_id) REFERENCES vms(id) ON DELETE SET NULL,
    FOREIGN KEY (master_image_id) REFERENCES master_images(id) ON DELETE RESTRICT,
    FOREIGN KEY (parent_workspace_id) REFERENCES workspaces(id) ON DELETE SET NULL
);

-- VM boot history: tracks each boot/shutdown cycle
CREATE TABLE vm_boot_history (
    id TEXT PRIMARY KEY,
    vm_id TEXT NOT NULL,
    boot_started_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
    boot_stopped_at INTEGER,
    exit_code INTEGER,
    error_message TEXT,
    console_log_path TEXT,
    FOREIGN KEY (vm_id) REFERENCES vms(id) ON DELETE CASCADE
);

-- vsock routes: inter-VM communication mediated by Nexus
CREATE TABLE routes (
    id TEXT PRIMARY KEY,
    source_vm_id TEXT NOT NULL,
    target_vm_id TEXT NOT NULL,
    source_port INTEGER NOT NULL,
    target_port INTEGER NOT NULL,
    created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
    FOREIGN KEY (source_vm_id) REFERENCES vms(id) ON DELETE CASCADE,
    FOREIGN KEY (target_vm_id) REFERENCES vms(id) ON DELETE CASCADE,
    UNIQUE (source_vm_id, source_port)
);

-- vsock services registered by guest agents
CREATE TABLE vsock_services (
    id TEXT PRIMARY KEY,
    vm_id TEXT NOT NULL,
    port INTEGER NOT NULL,
    service_name TEXT NOT NULL,
    state TEXT NOT NULL DEFAULT 'stopped' CHECK(state IN ('listening', 'stopped')),
    created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
    FOREIGN KEY (vm_id) REFERENCES vms(id) ON DELETE CASCADE,
    UNIQUE (vm_id, port)
);

-- Network bridges
CREATE TABLE bridges (
    name TEXT PRIMARY KEY,
    subnet TEXT NOT NULL,      -- CIDR notation (e.g., "172.16.0.0/24")
    gateway TEXT NOT NULL,     -- gateway IP (e.g., "172.16.0.1")
    interface TEXT NOT NULL,   -- host interface name
    created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
);

-- VM network configuration
CREATE TABLE vm_network (
    vm_id TEXT PRIMARY KEY,
    ip_address TEXT NOT NULL,
    bridge_name TEXT NOT NULL,
    FOREIGN KEY (vm_id) REFERENCES vms(id) ON DELETE CASCADE,
    FOREIGN KEY (bridge_name) REFERENCES bridges(name) ON DELETE RESTRICT
);

-- Firewall rules for VMs (nftables-based)
CREATE TABLE firewall_rules (
    id TEXT PRIMARY KEY,
    vm_id TEXT NOT NULL,
    rule_order INTEGER NOT NULL,
    action TEXT NOT NULL CHECK(action IN ('accept', 'drop', 'reject')),
    protocol TEXT CHECK(protocol IN ('tcp', 'udp', 'icmp', 'all')),
    source_ip TEXT,
    source_port TEXT,
    dest_ip TEXT,
    dest_port TEXT,
    description TEXT,
    created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
    FOREIGN KEY (vm_id) REFERENCES vms(id) ON DELETE CASCADE,
    UNIQUE (vm_id, rule_order)
);

-- Tags (organizational)
CREATE TABLE vm_tags (
    vm_id TEXT NOT NULL,
    tag_name TEXT NOT NULL,
    PRIMARY KEY (vm_id, tag_name),
    FOREIGN KEY (vm_id) REFERENCES vms(id) ON DELETE CASCADE,
    FOREIGN KEY (tag_name) REFERENCES tags(name) ON DELETE CASCADE
);

CREATE TABLE workspace_tags (
    workspace_id TEXT NOT NULL,
    tag_name TEXT NOT NULL,
    PRIMARY KEY (workspace_id, tag_name),
    FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE,
    FOREIGN KEY (tag_name) REFERENCES tags(name) ON DELETE CASCADE
);

-- Indexes
CREATE INDEX idx_vms_role ON vms(role);
CREATE INDEX idx_vms_state ON vms(state);
CREATE INDEX idx_workspaces_vm_id ON workspaces(vm_id);
CREATE INDEX idx_workspaces_base ON workspaces(master_image_id);
CREATE INDEX idx_vm_boot_history_vm_id ON vm_boot_history(vm_id);
CREATE INDEX idx_vsock_services_vm_id ON vsock_services(vm_id);
CREATE INDEX idx_routes_source ON routes(source_vm_id);
CREATE INDEX idx_routes_target ON routes(target_vm_id);
CREATE INDEX idx_firewall_rules_vm_id ON firewall_rules(vm_id);
CREATE INDEX idx_vm_tags_tag ON vm_tags(tag_name);
CREATE INDEX idx_workspace_tags_tag ON workspace_tags(tag_name);

-- Partial index: workspace can only be attached to one VM at a time
CREATE UNIQUE INDEX idx_workspace_current_attachment
    ON workspaces(vm_id) WHERE vm_id IS NOT NULL AND detached_at IS NULL;

-- Partial index: each VM has only one root device
CREATE UNIQUE INDEX idx_vm_root_device
    ON workspaces(vm_id) WHERE vm_id IS NOT NULL AND detached_at IS NULL AND is_root_device = 1;

Diagrams

Core Entity Relationships

erDiagram
    vms ||--o{ workspaces : "has attached"
    vms ||--o{ vm_boot_history : "boot history"
    vms ||--o{ vsock_services : "runs services"
    vms ||--o| vm_network : "has network"
    master_images ||--o{ workspaces : "snapshot of"
    workspaces ||--o{ workspaces : "derived from"
    bridges ||--o{ vm_network : "provides connectivity"
    vms ||--o{ firewall_rules : "has rules"
    vms ||--o{ routes : "source"
    vms ||--o{ routes : "target"

    vms {
        text id PK
        text name
        text role
        text state
        int cid
        int vcpu_count
        int mem_size_mib
        int pid
    }

    master_images {
        text id PK
        text name
        text subvolume_path
        int size_bytes
    }

    workspaces {
        text id PK
        text name
        text vm_id FK
        text subvolume_path
        text master_image_id FK
        text parent_workspace_id FK
        int is_root_device
        int is_read_only
    }

    routes {
        text id PK
        text source_vm_id FK
        text target_vm_id FK
        int source_port
        int target_port
    }

    vsock_services {
        text id PK
        text vm_id FK
        int port
        text service_name
        text state
    }

    bridges {
        text name PK
        text subnet
        text gateway
    }

    vm_network {
        text vm_id PK
        text ip_address
        text bridge_name FK
    }

    firewall_rules {
        text id PK
        text vm_id FK
        int rule_order
        text action
        text protocol
    }

VM State Machine

stateDiagram-v2
    [*] --> created: POST /v1/vms
    created --> running: POST /v1/vms/:id/start
    created --> failed: Boot failure (automatic)
    created --> [*]: DELETE /v1/vms/:id
    running --> stopped: POST /v1/vms/:id/stop
    running --> crashed: (automatic)
    stopped --> running: POST /v1/vms/:id/start
    stopped --> [*]: DELETE /v1/vms/:id
    crashed --> running: POST /v1/vms/:id/start
    crashed --> [*]: DELETE /v1/vms/:id
    failed --> [*]: DELETE /v1/vms/:id

States

StateDescription
createdVM record exists, Firecracker process not started
runningFirecracker process active, VM booted
stoppedVM gracefully stopped via API, can be restarted
crashedVM terminated unexpectedly, can be restarted
failedVM failed to boot (e.g., bad workspace image)

Valid Transitions

FromToTrigger
createdrunningPOST /v1/vms/:id/start
createdfailedAutomatic (boot failure)
created(deleted)DELETE /v1/vms/:id
runningstoppedPOST /v1/vms/:id/stop
runningcrashedAutomatic (unexpected termination)
stoppedrunningPOST /v1/vms/:id/start
stopped(deleted)DELETE /v1/vms/:id
crashedrunningPOST /v1/vms/:id/start
crashed(deleted)DELETE /v1/vms/:id
failed(deleted)DELETE /v1/vms/:id

Constraints

  • Cannot delete running VM: Must stop first (returns 409 Conflict)
  • Cannot start running VM: Already running (returns 409 Conflict)
  • Cannot manually transition to crashed or failed: Set automatically by Nexus
  • Failed VMs can only be deleted: Boot failure requires recreating with a working workspace