Architecture
Project structure, component hierarchy and data flow.
This document describes how KatanOS is structured and how data moves between UI, services, storage, and Electron.
Stack
Renderer: React 19 + TypeScript + Vite
Desktop shell: Electron 39
Styling: Tailwind CSS
Charts: Chart.js
AI SDK:
@google/genaiPersistence: IndexedDB (
katanos_storage) with localStorage fallback
Repository Layout
KatanOS/
App.tsx
index.tsx
types.ts
pages/
components/
services/
electron/
scripts/
docs/Process Model
KatanOS runs with two isolated contexts.
Electron Main (electron/main.cjs)
electron/main.cjs)Responsibilities:
app/window lifecycle
splash + main window boot flow
IPC handlers (
katanos:*)safeStorage encryption/decryption bridge
filesystem operations for backup/export/import
logging (
errors.log)CSP and permission policy
Discord Rich Presence
Security-relevant BrowserWindow config:
contextIsolation: truenodeIntegration: falsesandbox: truedevTools: false
Renderer (React app)
Responsibilities:
all module UI and interaction logic
domain CRUD through
services/db.tssettings orchestration (via
components/Layout.tsx)local-first state and persistence
notification and lock-screen flows
Renderer accesses privileged operations only through window.katanos preload APIs.
Entry Points
index.tsx
index.tsxmounts
<App />into#rootloads
styles/tailwind.cssforwards renderer errors to main process via
window.katanos.logErrorpatched
console.errorwindow.onerrorunhandledrejection
App.tsx
App.tsxCentral app orchestrator:
initializes storage (
db.init())restores current user (
db.auth.getCurrentUser())lazy-loads page modules
manages active page (
activePage)controls lock behavior (startup + inactivity)
handles exit prompt and close-confirm flow
emits Discord Rich Presence via
window.katanos.setRichPresenceenables temporary dev mode via Konami code (3 minutes)
Navigation Model
Navigation is state-driven, not route-driven.
HashRouteris present for Electron compatibilitypage selection is controlled by
activePageinApp.tsxLayoutbuilds visible nav items frommodulesService.getNavItems(user, lang)
Important implication:
module disablement hides navigation entries but does not enforce route-level hard blocking in
App.tsx
UI Shell Composition
components/Layout.tsx
components/Layout.tsxPrimary authenticated shell containing:
sidebar/topbar
command palette (
Ctrl/Cmd + K)settings modal with tabs:
profile
personalization
preferences
categories
modules
backup
system
info
external-link confirmation modal
AI Scout panel
Page Modules (pages/)
pages/)Dashboard.tsxAgenda.tsxTodo.tsxFinance.tsxContacts.tsxHabits.tsxJournal.tsxBookshelf.tsxVault.tsxGames.tsxLogin.tsx(unauthenticated state)
Cross-Cutting Event Bus
KatanOS uses custom browser events for decoupled UI behavior.
Emitted events include:
katanos:notifykatanos:escapekatanos:openExitPromptkatanos:katana
Consumers include:
NotificationSystemmodule pages that close modals on escape
Appexit prompt handlersvisual/audio easter egg logic
Startup and Shutdown Flow
Startup
Electron creates splash window.
Electron creates hidden main window.
Renderer starts and initializes
db.Renderer calls
window.katanos.signalReady().Main window is shown after splash timing window.
Note:
signalReadycurrently does not shorten the splash minimum duration.
Shutdown
Window close request is intercepted in main process.
Renderer receives
katanos:requestClose.If no user is logged in, close is confirmed immediately.
If user exists, exit prompt opens in
App.tsx.On confirmation, renderer triggers autosave and calls
confirmClose.
Data Flow
Typical CRUD flow:
User action in page/component.
Call to
db.*service.dbserializes collection updates.storage.tswrites to IndexedDB cache store (kv) or fallback localStorage.UI reloads updated data.
Backup/export/import flow uses Electron bridge when filesystem access is needed.
Persistence Boundaries
canonical app data keys are in
services/db.tsunderchronos_*auxiliary per-user UI keys also exist in localStorage (
katanos_*)autosave snapshots are forwarded to Electron via
saveSnapshot
Electron autosave/export folder serialization currently stores a subset of domain files (events, todos, transactions, contacts, habits, journal) plus metadata/users.
Integrations
Runtime integrations used by modules/services:
Open-Meteo (forecast + geocoding)
Nominatim (reverse/place geocoding)
IP geolocation (
ipapi.co)Google Books API
Gemini APIs (places, finance insights, journal analysis, event parsing)
Wikipedia On This Day feed
Testing Structure
Notable test entry points:
services/vault.test.tscomponents/layout/utils.test.tspages/dashboard/utils.test.tspages/finance/utils.test.tspages/finance/useFinanceCharts.test.ts
Run with:
Last updated
Was this helpful?
