Skip to main content

Session Database

Manages terminal sessions, heartbeats, and crash recovery.
╔═════════════════════════════════════════════════════════════════════╗
║                       SESSION.DB OVERVIEW                            ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                      ║
║   ┌─────────────────────────┐  ┌─────────────────────────┐           ║
║   │  terminal_heartbeats    │  │       terminals         │           ║
║   │                         │  │                         │           ║
║   │ - terminal_id (PK)      │  │ - id (PK)               │           ║
║   │ - instance_id           │  │ - name                  │           ║
║   │ - last_heartbeat        │  │ - created_at            │           ║
║   │ - pid                   │  │ - last_active           │           ║
║   │ - is_alive              │  │ - ended_at              │           ║
║   │ - cols, rows            │  │ - is_active             │           ║
║   │ - cwd                   │  │ - settings              │           ║
║   │ - terminal_name         │  │                         │           ║
║   │ - pty_path              │  │                         │           ║
║   └─────────────────────────┘  └─────────────────────────┘           ║
║                                                                      ║
║   ┌─────────────────────────┐  ┌─────────────────────────┐           ║
║   │   terminal_sessions     │  │  terminal_group_log     │           ║
║   │                         │  │                         │           ║
║   │ - session_id (UNIQUE)   │  │ - group_id              │           ║
║   │ - tab_name              │  │ - group_name            │           ║
║   │ - scrollback            │  │ - tab_ids               │           ║
║   │ - cols, rows            │  │ - terminal_names        │           ║
║   │ - cursor_x, cursor_y    │  │ - created_at            │           ║
║   │ - timestamp             │  │                         │           ║
║   └─────────────────────────┘  └─────────────────────────┘           ║
║                                                                      ║
║                          session.db                                  ║
╚═════════════════════════════════════════════════════════════════════╝

Session State Machine

How terminal sessions transition through states.
╔═════════════════════════════════════════════════════════════════════╗
║                    SESSION STATE MACHINE                             ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                      ║
║   Terminal Created                                                   ║
║        │                                                             ║
║        │ save_terminal()                                             ║
║        │ INSERT (is_alive=1)                                         ║
║        ▼                                                             ║
║   ┌───────────────────┐                                              ║
║   │      ALIVE        │◀─────────────────────────────────────────┐   ║
║   │                   │                                          │   ║
║   │  - Responding     │                                          │   ║
║   │  - Heartbeat OK   │                                          │   ║
║   └───────────────────┘                                          │   ║
║        │           │                                             │   ║
║        │           │ (every 10 seconds)                          │   ║
║        │           │ heartbeat()                                 │   ║
║        │           │ UPDATE last_heartbeat = NOW                 │   ║
║        │           └─────────────────────────────────────────────┘   ║
║        │                                                             ║
║        ├────────────────────────────┐                                ║
║        │                            │                                ║
║        │ User closes tab            │ Heartbeat timeout (30s)        ║
║        │ end_terminal()             │ cleanup_dead_terminals()       ║
║        │ UPDATE is_alive=0          │ UPDATE is_alive=0              ║
║        ▼                            ▼                                ║
║   ┌───────────────────┐    ┌───────────────────┐                     ║
║   │      ENDED        │    │       DEAD        │                     ║
║   │                   │    │                   │                     ║
║   │  - Graceful close │    │  - Stale session  │                     ║
║   │  - User initiated │    │  - No heartbeat   │                     ║
║   └───────────────────┘    └───────────────────┘                     ║
║                                   │                                  ║
║                                   │ App restart                      ║
║                                   │ Socket file exists?              ║
║                                   │ recover_orphaned_pty()           ║
║                                   ▼                                  ║
║                          ┌───────────────────┐                       ║
║                          │    RECOVERED      │                       ║
║                          │                   │                       ║
║                          │  - Reconnected    │                       ║
║                          │  - PTY restored   │                       ║
║                          └───────────────────┘                       ║
║                                   │                                  ║
║                                   │ UPDATE is_alive=1                ║
║                                   └─────────────────────────────────▶┘
║                                                                      ║
╚═════════════════════════════════════════════════════════════════════╝

Heartbeat Flow

How session health is monitored.
╔═════════════════════════════════════════════════════════════════════╗
║                       HEARTBEAT FLOW                                 ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                      ║
║   Frontend (10-second interval)                                      ║
║        │                                                             ║
║        │ invoke("session_db_heartbeat", { terminal_id })             ║
║        ▼                                                             ║
║   ┌───────────────────────────────────────────────────────────────┐  ║
║   │  UPDATE terminal_heartbeats                                   │  ║
║   │  SET last_heartbeat = NOW()                                   │  ║
║   │  WHERE terminal_id = ?                                        │  ║
║   └───────────────────────────────────────────────────────────────┘  ║
║                                                                      ║
║   TIMING PARAMETERS                                                  ║
║   ┌───────────────────────────────────────────────────────────────┐  ║
║   │                                                               │  ║
║   │   Parameter        │ Value   │ Purpose                        │  ║
║   │   ═════════════════│═════════│════════════════════════════════│  ║
║   │   Heartbeat        │ 10 sec  │ Frontend sends heartbeat       │  ║
║   │   interval         │         │                                │  ║
║   │   Stale threshold  │ 30 sec  │ Terminal marked dead if        │  ║
║   │                    │         │ no heartbeat for 30 seconds    │  ║
║   │   Recovery window  │ Startup │ Check for orphaned PTYs        │  ║
║   │                    │ only    │ at app startup                 │  ║
║   │                                                               │  ║
║   └───────────────────────────────────────────────────────────────┘  ║
║                                                                      ║
║   TIMELINE                                                           ║
║   ┌───────────────────────────────────────────────────────────────┐  ║
║   │                                                               │  ║
║   │   0s       10s       20s       30s       40s                  │  ║
║   │   │─────────│─────────│─────────│─────────│                   │  ║
║   │   ▲         ▲         ▲         ✕                             │  ║
║   │   │         │         │         │                             │  ║
║   │   HB        HB        HB        Marked DEAD (no HB for 30s)   │  ║
║   │                                                               │  ║
║   └───────────────────────────────────────────────────────────────┘  ║
║                                                                      ║
╚═════════════════════════════════════════════════════════════════════╝

Crash Recovery Flow

How sessions are recovered after app crash.
╔═════════════════════════════════════════════════════════════════════╗
║                     CRASH RECOVERY FLOW                              ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                      ║
║   App Startup                                                        ║
║        │                                                             ║
║        ▼                                                             ║
║   ┌───────────────────────────────────────────────────────────────┐  ║
║   │  Step 1: Cleanup stale terminals                              │  ║
║   │                                                               │  ║
║   │  UPDATE terminal_heartbeats                                   │  ║
║   │  SET is_alive = 0                                             │  ║
║   │  WHERE last_heartbeat < (NOW - 30 seconds)                    │  ║
║   └───────────────────────────────────────────────────────────────┘  ║
║        │                                                             ║
║        ▼                                                             ║
║   ┌───────────────────────────────────────────────────────────────┐  ║
║   │  Step 2: Find recoverable terminals                           │  ║
║   │                                                               │  ║
║   │  SELECT * FROM terminal_heartbeats                            │  ║
║   │  WHERE is_alive = 0                                           │  ║
║   │  AND last_heartbeat > (NOW - 5 minutes)                       │  ║
║   └───────────────────────────────────────────────────────────────┘  ║
║        │                                                             ║
║        ▼                                                             ║
║   For each terminal:                                                 ║
║        │                                                             ║
║        └───▶ [PTY socket file exists on disk?]                       ║
║        │           │                                                 ║
║        │           │ No ──▶ Mark as DEAD, continue                   ║
║        │           │                                                 ║
║        │           │ Yes                                             ║
║        │           ▼                                                 ║
║        │     [Can connect to socket?]                                ║
║        │           │                                                 ║
║        │           │ No (ECONNREFUSED)                               ║
║        │           │ ──▶ Delete socket file, mark DEAD               ║
║        │           │                                                 ║
║        │           │ Yes                                             ║
║        │           ▼                                                 ║
║        │     [PTY responds to ping?]                                 ║
║        │           │                                                 ║
║        │           │ No (timeout)                                    ║
║        │           │ ──▶ Close socket, mark DEAD                     ║
║        │           │                                                 ║
║        │           │ Yes                                             ║
║        │           ▼                                                 ║
║        │     RECOVERED!                                              ║
║        │     UPDATE is_alive = 1                                     ║
║        │     Attach to session                                       ║
║        │                                                             ║
║        └───▶ Next terminal                                           ║
║                                                                      ║
╚═════════════════════════════════════════════════════════════════════╝

Scrollback Storage

Persist terminal content for recovery.
╔═════════════════════════════════════════════════════════════════════╗
║                   SCROLLBACK PERSISTENCE                             ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                      ║
║   SCROLLBACK JSON FORMAT                                             ║
║   ┌───────────────────────────────────────────────────────────────┐  ║
║   │                                                               │  ║
║   │   {                                                           │  ║
║   │     "lines": [                                                │  ║
║   │       { "cells": [...], "isWrapped": false },                 │  ║
║   │       { "cells": [...], "isWrapped": true },                  │  ║
║   │       ...                                                     │  ║
║   │     ],                                                        │  ║
║   │     "cursorX": 0,                                             │  ║
║   │     "cursorY": 24,                                            │  ║
║   │     "viewportY": 0                                            │  ║
║   │   }                                                           │  ║
║   │                                                               │  ║
║   └───────────────────────────────────────────────────────────────┘  ║
║                                                                      ║
║   SAVE TRIGGERS                                                      ║
║   ┌───────────────────────────────────────────────────────────────┐  ║
║   │  - Periodic (configurable interval)                           │  ║
║   │  - On tab close (graceful shutdown)                           │  ║
║   │  - Before app quit                                            │  ║
║   └───────────────────────────────────────────────────────────────┘  ║
║                                                                      ║
║   RESTORE TRIGGERS                                                   ║
║   ┌───────────────────────────────────────────────────────────────┐  ║
║   │  - Tab reopen (same session_id)                               │  ║
║   │  - Crash recovery (orphaned PTY attach)                       │  ║
║   └───────────────────────────────────────────────────────────────┘  ║
║                                                                      ║
╚═════════════════════════════════════════════════════════════════════╝

Write/Read Operations

╔═════════════════════════════════════════════════════════════════════╗
║                    WRITE/READ OPERATIONS                             ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                      ║
║   WRITE                                                              ║
║   ┌───────────────────────────────────────────────────────────────┐  ║
║   │                                                               │  ║
║   │   Function              │ SQL                 │ Trigger        │  ║
║   │   ══════════════════════│═════════════════════│════════════════│  ║
║   │   save_terminal         │ INSERT OR REPLACE   │ Tab open       │  ║
║   │                         │ terminal_heartbeats │                │  ║
║   │   heartbeat             │ UPDATE              │ 10s timer      │  ║
║   │                         │ last_heartbeat      │                │  ║
║   │   end_terminal          │ UPDATE is_alive=0   │ Tab close      │  ║
║   │   cleanup_dead          │ UPDATE is_alive=0   │ App start      │  ║
║   │                         │ (30s stale check)   │                │  ║
║   │   save_scrollback       │ INSERT/UPDATE       │ Periodic       │  ║
║   │                         │ terminal_sessions   │                │  ║
║   │                                                               │  ║
║   └───────────────────────────────────────────────────────────────┘  ║
║                                                                      ║
║   READ                                                               ║
║   ┌───────────────────────────────────────────────────────────────┐  ║
║   │                                                               │  ║
║   │   Function              │ SQL                                 │  ║
║   │   ══════════════════════│═════════════════════════════════════│  ║
║   │   get_alive_terminals   │ SELECT WHERE is_alive=1             │  ║
║   │   get_max_terminal_num  │ SELECT MAX(terminal_number)         │  ║
║   │   get_scrollback        │ SELECT WHERE session_id=?           │  ║
║   │                                                               │  ║
║   └───────────────────────────────────────────────────────────────┘  ║
║                                                                      ║
╚═════════════════════════════════════════════════════════════════════╝

Design Principles Applied

╔═════════════════════════════════════════════════════════════════════╗
║                      DESIGN PRINCIPLES                               ║
╠═════════════════════════════════════════════════════════════════════╣
║                                                                      ║
║   SIMPLICITY (SMPC)                                                  ║
║   ┌───────────────────────────────────────────────────────────────┐  ║
║   │ [x] Simple heartbeat mechanism (10s UPDATE)                   │  ║
║   │ [x] Binary is_alive state (0 or 1)                            │  ║
║   │ [x] 30-second stale threshold                                 │  ║
║   │ [x] No complex state machine in DB                            │  ║
║   └───────────────────────────────────────────────────────────────┘  ║
║                                                                      ║
║   ORDER FROM CHAOS (OFAC)                                            ║
║   ┌───────────────────────────────────────────────────────────────┐  ║
║   │ [x] Crash recovery via is_alive + socket file existence       │  ║
║   │ [x] cleanup_dead_terminals() on startup                       │  ║
║   │ [x] Scrollback persistence for session restore                │  ║
║   │ [x] terminal_group_log for audit trail                        │  ║
║   └───────────────────────────────────────────────────────────────┘  ║
║                                                                      ║
╚═════════════════════════════════════════════════════════════════════╝

Quick Reference

AspectValue
Location~/Library/Application Support/Monolex/protocols/niia/database/session.db
Primary OwnerSession DB module
Main Tablesterminal_heartbeats, terminal_sessions
Heartbeat Interval10 seconds
Stale Threshold30 seconds
RecoveryAt app startup only