circle-exclamation
KatanOS will not receive any updates at the moment, which is why I decided to make it open source.

atomElectron

KatanOS uses Electron for desktop packaging, OS integration, filesystem access, and secret handling.

Files

  • electron/main.cjs: main process and IPC handlers

  • electron/preload.cjs: renderer bridge (window.katanos)

  • electron/renderer.d.ts: TypeScript typings for bridge

  • electron/splash.html + electron/splash.js: splash UI

Security Baseline

Main window webPreferences:

  • contextIsolation: true

  • nodeIntegration: false

  • sandbox: true

  • devTools: false

Navigation policy:

  • external window opens are denied and rerouted to shell.openExternal only for http/https/mailto

Permission policy:

  • geolocation is allowed only for trusted app origins (file://, app, localhost)

CSP:

  • CSP headers are applied in packaged mode (app.isPackaged)

Preload API (window.katanos)

Snapshot and Backup Payload IO

  • saveSnapshot(payload)

  • exportSnapshot(payload, suggestedName?)

  • exportUserFolder(payload, suggestedName?)

  • importUserFolder()

Window Controls

  • setFullScreen(enabled)

  • isFullScreen()

  • minimize()

  • confirmClose()

OS and Utility

  • openExternal(url)

  • copyText(text)

  • openStorePage()

Diagnostics

  • logError(payload)

  • getAppInfo()

  • openLogFolder()

Secrets

  • encryptSecret(value)

  • decryptSecret(value)

Backup Filesystem API

  • selectBackupFolder()

  • writeBackupFile({ folderPath, fileName, data })

  • listBackupFiles(folderPath)

  • deleteBackupFile(filePath)

  • checkFolderWritable(folderPath)

Lifecycle Hooks

  • signalReady()

  • onRequestClose(callback)

API Contract (Renderer <-> Main)

Source of truth: electron/preload.cjs + electron/renderer.d.ts + electron/main.cjs.

Contract rules:

  • all bridge methods are exposed under window.katanos

  • every method returns a Promise

  • handlers should return structured status payloads (ok/success/canceled) instead of throwing into UI code

  • channel names are namespaced as katanos:*

Key method contracts:

Method
Input
Return

saveSnapshot

payload: unknown

{ path?: string }

exportSnapshot

payload: unknown, suggestedName?: string

{ canceled?: boolean; path?: string }

exportUserFolder

payload: unknown, suggestedName?: string

{ canceled?: boolean; path?: string }

importUserFolder

none

{ canceled?: boolean; payload?: unknown }

setFullScreen

enabled: boolean

boolean

isFullScreen

none

boolean

minimize

none

boolean

confirmClose

none

boolean

deleteUserData

userId: string

boolean

openExternal

url: string

boolean

copyText

text: string

boolean

openStorePage

none

boolean

setRichPresence

{ details?: string; state?: string }

boolean

logError

payload: unknown

boolean

encryptSecret

value: string

{ ok?: boolean; value?: string; error?: string }

decryptSecret

value: string

{ ok?: boolean; value?: string; error?: string }

getAppInfo

none

app/runtime metadata object

openLogFolder

none

{ ok?: boolean; error?: string }

signalReady

none

void

onRequestClose

callback: () => void

() => void (unsubscribe)

selectBackupFolder

none

`{ canceled: boolean; path: string

writeBackupFile

{ folderPath, fileName, data }

{ success: boolean; path?: string; error?: string }

listBackupFiles

folderPath: string

Array<{ name; path; size; mtime }>

deleteBackupFile

filePath: string

boolean

checkFolderWritable

folderPath: string

{ writable: boolean }

Backward-compatibility guidance:

  • if a bridge method signature changes, update both renderer.d.ts and docs in the same commit

  • if payload format changes, keep version markers in backup payloads and preserve old-reader support where feasible

IPC Channels in Main

All channels are namespaced katanos:*.

Main handlers include:

  • saveSnapshot

  • exportSnapshot

  • exportUserFolder

  • importUserFolder

  • setFullScreen

  • isFullScreen

  • minimize

  • confirmClose

  • setRichPresence

  • openExternal

  • encryptSecret

  • decryptSecret

  • copyText

  • deleteUserData

  • openStorePage

  • selectBackupFolder

  • writeBackupFile

  • listBackupFiles

  • deleteBackupFile

  • checkFolderWritable

  • logError

  • getAppInfo

  • openLogFolder

Startup Sequence

  1. Splash window is created and shown.

  2. Main app window is created hidden.

  3. Renderer loads.

  4. Main window is shown after splash duration (or fallback timeout).

Implementation note:

  • katanos:signalReady is handled once but currently does not force early show.

Logging

Log file location:

  • <userData>/katanos/logs/errors.log

Sources captured:

  • renderer console.error

  • renderer uncaught errors and unhandled rejections

  • main process exceptions/rejections

  • renderer process crash/unresponsive events

safeStorage Secret Flow

  • renderer calls preload encrypt/decrypt

  • main uses safeStorage.encryptString/decryptString

  • stored encrypted values use prefix enc$

Fallback behavior:

  • if safeStorage is unavailable, encryption/decryption returns unavailable status

Backup Filesystem Behavior

writeBackupFile performs atomic write strategy:

  1. write *.tmp

  2. rename to final file

  3. cleanup temp on failure

Deletion hardening:

  • only files matching katanos-backup-*.json are deletable via IPC

Data Scope Caveat for Electron Export/Autosave

Main process file-based autosave/export-folder logic uses a fixed list of per-user data files:

  • events

  • todos

  • transactions

  • contacts

  • habits

  • journal

Additional renderer domains are not serialized by this Electron file splitter unless added in USER_DATA_FILES.

Discord Rich Presence

Main process initializes discord-rpc and sets activity from renderer payloads.

Configured metadata includes:

  • app start timestamp

  • large image key

  • website/store buttons

Packaging Entry

Electron entrypoint in package.json:

  • "main": "electron/main.cjs"

Common scripts:

Last updated

Was this helpful?