Skip to content

State Management ​

Kassie manages state across three layers: server-side state, TUI client state, and Web client state. Each layer uses different strategies optimized for its context.

Server-Side State ​

All server state is held in-memory. There is no external database or cache for state storage.

Session Store ​

Maps session_id to session data:

FieldTypeDescription
session_idstringUUID, unique per login
profileProfileInfoActive database profile
connection*gocql.SessionDatabase connection handle
created_attime.TimeSession creation timestamp

Lifecycle:

  1. Created on successful Login
  2. Used for every authenticated request
  3. Destroyed on Logout or token expiry
  4. Connection pool returned on cleanup

Cursor Store ​

Maps cursor_id to Scylla paging state for pagination:

FieldTypeDescription
cursor_idstringUUID, unique per query
paging_state[]byteScylla paging state token
keyspacestringQuery keyspace
tablestringQuery table
where_clausestringActive filter (if any)
last_accessedtime.TimeFor expiration tracking

Lifecycle:

  1. Created on initial QueryRows or FilterRows
  2. Updated on each GetNextPage call
  3. Expires after 30 minutes of inactivity
  4. Manually cleared on new query to same table

Connection Pool ​

Per-profile database connection management:

Profile "local"  ──> Connection Pool (5 connections)
Profile "staging" ──> Connection Pool (5 connections)
Profile "prod"   ──> Connection Pool (5 connections)
  • Pools are created lazily on first login
  • Shared across sessions using the same profile
  • Health-checked and auto-reconnected
  • Closed when all sessions for a profile end

TUI State (Bubbletea) ​

The TUI uses Bubbletea's Elm Architecture: Model → Update → View.

Global State ​

go
type AppState struct {
    CurrentView    View          // connection, explorer, help
    Profile        *ProfileInfo  // active profile after login
    SessionToken   string        // JWT access token
    RefreshToken   string        // JWT refresh token
    Error          string        // current error message
}

Explorer State ​

go
type ExplorerState struct {
    Keyspaces      []Keyspace    // loaded keyspaces
    Tables         []Table       // tables for selected keyspace
    SelectedKS     string        // active keyspace name
    SelectedTable  string        // active table name
    Schema         *TableSchema  // column definitions
    Rows           []Row         // current page of data
    CursorID       string        // pagination cursor
    HasMore        bool          // more pages available
    Filter         string        // active WHERE clause
    FocusedPane    Pane          // sidebar, grid, or inspector
}

Component State ​

Each component manages its own local state:

ComponentLocal State
SidebarScroll position, expanded keyspaces, search text
DataGridColumn widths, horizontal scroll offset, selected row index
InspectorScroll position, expanded JSON nodes
FilterBarInput text, cursor position, validation error
StatusBarDerived from global + explorer state (no own state)

State Flow ​

User Input ──> Bubbletea Update() ──> New Model ──> View() ──> Terminal
                    │
                    ├── Local state change (navigation, UI toggle)
                    │
                    └── gRPC call ──> Server ──> Response ──> Update Model

Web State ​

The Web client uses a three-layer state model following React best practices.

Server State (TanStack Query) ​

All data fetched from the API lives in the TanStack Query cache:

Query KeyDataStale Time
['profiles']Profile list5 minutes
['keyspaces']Keyspace list5 minutes
['tables', keyspace]Tables for a keyspace5 minutes
['schema', keyspace, table]Table schema/columns5 minutes
['rows', keyspace, table]Initial row queryOn navigation
['rows', keyspace, table, 'filter', where]Filtered rowsOn navigation
['rows', 'page', cursorId]Next page of dataOn navigation

Benefits:

  • Automatic background refetching
  • Cache invalidation
  • Request deduplication
  • Loading and error states built-in

UI State (Zustand) ​

Ephemeral client-side state stored in lightweight Zustand stores:

Auth Store:

FieldTypePersisted
accessTokenstringlocalStorage
refreshTokenstringlocalStorage
expiresAtnumberlocalStorage
profileProfileInfolocalStorage
isAuthenticatedbooleanDerived

UI Store:

FieldTypePersisted
sidebarCollapsedbooleanlocalStorage
inspectorOpenbooleanlocalStorage
selectedRowIndexnumberNo
theme'light' | 'dark' | 'system'localStorage

URL State (React Router) ​

Navigation state encoded in URL search parameters for shareability:

/?keyspace=app_data&table=users&filter=id%3D'abc'&page=2
ParameterTypeDescription
keyspacestringSelected keyspace
tablestringSelected table
filterstringWHERE clause (URL-encoded)
pagenumberCurrent page number

Benefits:

  • Shareable links
  • Browser back/forward navigation
  • Bookmark support
  • State survives page refresh

Web State Flow ​

User Action
    │
    ├── UI change ──> Zustand store ──> React re-render
    │
    ├── Navigation ──> URL params ──> TanStack Query fetch ──> Re-render
    │
    └── Data fetch ──> TanStack Query ──> API call ──> Cache update ──> Re-render

State Persistence Summary ​

StateTUIWeb
JWT tokensIn-memorylocalStorage
UI preferencesNot persistedlocalStorage
Navigation stateIn-memoryURL params
Query cacheNot applicableTanStack Query (memory)
Server sessionsIn-memory (server)In-memory (server)
Pagination cursorsIn-memory (server)In-memory (server)

Released under the MIT License.