Electron
KatanOS uses Electron for desktop packaging, OS integration, filesystem access, and secret handling.
Files
electron/main.cjs: main process and IPC handlerselectron/preload.cjs: renderer bridge (window.katanos)electron/renderer.d.ts: TypeScript typings for bridgeelectron/splash.html+electron/splash.js: splash UI
Security Baseline
Main window webPreferences:
contextIsolation: truenodeIntegration: falsesandbox: truedevTools: false
Navigation policy:
external window opens are denied and rerouted to
shell.openExternalonly forhttp/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)
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.katanosevery method returns a Promise
handlers should return structured status payloads (
ok/success/canceled) instead of throwing into UI codechannel names are namespaced as
katanos:*
Key method contracts:
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.tsand docs in the same commitif 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:
saveSnapshotexportSnapshotexportUserFolderimportUserFoldersetFullScreenisFullScreenminimizeconfirmClosesetRichPresenceopenExternalencryptSecretdecryptSecretcopyTextdeleteUserDataopenStorePageselectBackupFolderwriteBackupFilelistBackupFilesdeleteBackupFilecheckFolderWritablelogErrorgetAppInfoopenLogFolder
Startup Sequence
Splash window is created and shown.
Main app window is created hidden.
Renderer loads.
Main window is shown after splash duration (or fallback timeout).
Implementation note:
katanos:signalReadyis handled once but currently does not force early show.
Logging
Log file location:
<userData>/katanos/logs/errors.log
Sources captured:
renderer
console.errorrenderer 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/decryptStringstored encrypted values use prefix
enc$
Fallback behavior:
if safeStorage is unavailable, encryption/decryption returns unavailable status
Backup Filesystem Behavior
writeBackupFile performs atomic write strategy:
write
*.tmprename to final file
cleanup temp on failure
Deletion hardening:
only files matching
katanos-backup-*.jsonare 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?
