Architecture, Security Model & Internals
Architecture, Security Model & System Internals
This chapter documents the internal architecture, multi-process isolation model, active Electron Fuses, and complete IPC contracts of sshs3. The IPC table is autogenerated directly from application source code (src/shared/types/ipc.ts) and is intended for security auditors, penetration testers, and systems engineers.
1. Three-Process Architecture & Sandboxing
sshs3 is built on Electron's multi-process model with strict separation of privilege between the presentation layer and system execution engines:
Architectural Security Guarantees
- Zero Node.js Access in Renderer: The user interface has zero direct access to Node.js primitives (
fs,child_process,net,electron). All operations must navigate the typed IPC contract. - Fail-Closed Security Design: If the user interface unmounts or fails to respond to host key validation (TOFU) or PIN prompts within the timeout window, the operation aborts automatically.
- Atomic Persistence: All internal state stores (
ProfileStore,SettingsStore,SessionStore) employ sequentialqueueMutationpromises to ensure atomic disk writes via temporary files. - App-Wide
AppAgent& Concurrent Agent Lifecycle:AgentLifecycleManagerprobes for a running agent viaensureAgent(). Concurrent startup callers (such as restored local shell tabs racing startup initialization) coalesce onto the same in-flight promise to prevent PTY environment race conditions. In Global PIN caching mode, the app runs a single app-wide agent (AppAgent) with a stable socket ($XDG_RUNTIME_DIR/sshs3/agent.sockor/tmp/sshs3-agent-,/agent.sock 0700), exposed to external terminals via a serialized, injection-safe# BEGIN sshs3-agentblock in~/.ssh/config.
2. Electron Fuses & Binary Hardening
During production packaging, hardware-level Electron Fuses are enforced via electron-builder.json:
| Electron Fuse | Status | Security Rationale |
|---|---|---|
runAsNode | Enabled (Restricted) | Required for internal auxiliary processes (askpass, certWorker.cjs, proxy CLI) to execute through the signed binary via ELECTRON_RUN_AS_NODE=1. |
enableNodeCliInspectArguments | Disabled (Locked) | Prevents an attacker from attaching a debugger (--inspect, --inspect-brk) to the main process to inspect memory. |
enableNodeOptionsEnvironmentVariable | Disabled (Locked) | Blocks arbitrary code injection via the NODE_OPTIONS environment variable. |
onlyLoadAppFromAsar | Enabled (Locked) | Forces Electron to execute exclusively from inside the packaged app.asar archive. |
3. Threat Model & Asset Protection Matrix
| Asset / Secret | Storage Location | In-Memory Lifetime | Protection Mechanism |
|---|---|---|---|
| Private SSH Keys | Hardware Token / Disk | Never in app memory | Zero Private Key Extraction. OpenSSH executes signing on the hardware chip or system PTY. |
| Smartcard PINs | Ephemeral in AskpassServer | Ephemeral (seconds to session) | Purged immediately after handshake in Always Prompt mode. Never written to disk. |
| Saved Profile Passwords | profiles.json on disk | Decrypted only at connect time | Encrypted with AES via Electron safeStorage (libsecret on Linux, DPAPI on Windows, Keychain on macOS). |
| Remote Vault Profiles | Remote S3 / SFTP server | Lifetime of sync session | Client-side encrypted with AES-256-GCM and scrypt. Remote server sees only opaque ciphertext. |
| Forwarded X11 Windows | VcXsrv TCP Port 6000 | Lifetime of session | Access control enforced; requires MIT-MAGIC-COOKIE. Unauthorized LAN devices are rejected. |
4. Complete Autogenerated IPC Channel Reference
The table below is autogenerated directly from the application source code (src/shared/types/ipc.ts) during every documentation build. It enumerates all typed channels between the renderer and main process.
| IPC Constant | Channel String | Subsystem Module |
|---|---|---|
TERMINAL_CREATE | terminal:create | Terminal |
TERMINAL_WRITE | terminal:write | Terminal |
TERMINAL_RESIZE | terminal:resize | Terminal |
TERMINAL_KILL | terminal:kill | Terminal |
TERMINAL_RECONNECT | terminal:reconnect | Terminal |
TERMINAL_DATA | terminal:data | Terminal |
TERMINAL_EXIT | terminal:exit | Terminal |
TERMINAL_RECONNECTING | terminal:reconnecting | Terminal |
SMARTCARD_DETECT | smartcard:detect | Smartcard |
SMARTCARD_VALIDATE | smartcard:validate | Smartcard |
SMARTCARD_AGENT_PATH_STATUS | smartcard:agent-path-status | Smartcard |
SMARTCARD_AGENT_PATH_FIX | smartcard:agent-path-fix | Smartcard |
SMARTCARD_LOCK_ALL | smartcard:lock-all | Smartcard |
SMARTCARD_LIST_CACHED | smartcard:list-cached | Smartcard |
SMARTCARD_UNLOCK_AT_STARTUP | smartcard:unlock-at-startup | Smartcard |
SMARTCARD_UNLOCK_NOW | smartcard:unlock-now | Smartcard |
SMARTCARD_STARTUP_UNLOCK_STATUS | smartcard:startup-unlock-status | Smartcard |
ASKPASS_PROMPT | askpass:prompt | Smartcard |
ASKPASS_SUBMIT_PIN | askpass:submit-pin | Smartcard |
PRESENCE_PROMPT | presence:prompt | Smartcard |
PRESENCE_CLEAR | presence:clear | Smartcard |
FIDO2_GENERATE_KEY | fido2:generate-key | Smartcard |
FIDO2_LIST_RESIDENT_KEYS | fido2:list-resident-keys | Smartcard |
FIDO2_DELETE_RESIDENT_KEY | fido2:delete-resident-key | Smartcard |
HOSTKEY_PROMPT | hostkey:prompt | SFTP host key verification (TOFU) |
HOSTKEY_RESPOND | hostkey:respond | SFTP host key verification (TOFU) |
STORAGE_CONNECT | storage:connect | Storage |
STORAGE_DISCONNECT | storage:disconnect | Storage |
STORAGE_LIST | storage:list | Storage |
STORAGE_STAT | storage:stat | Storage |
STORAGE_CREATE_FOLDER | storage:create-folder | Storage |
STORAGE_DELETE | storage:delete | Storage |
STORAGE_RENAME | storage:rename | Storage |
STORAGE_CHMOD | storage:chmod | Storage |
STORAGE_SET_METADATA | storage:set-metadata | Storage |
STORAGE_GET_TAGS | storage:get-tags | Storage |
STORAGE_SET_TAGS | storage:set-tags | Storage |
STORAGE_GET_BUCKET_POLICY | storage:get-bucket-policy | Storage |
STORAGE_SET_BUCKET_POLICY | storage:set-bucket-policy | Storage |
STORAGE_GET_BUCKET_CORS | storage:get-bucket-cors | Storage |
STORAGE_SET_BUCKET_CORS | storage:set-bucket-cors | Storage |
STORAGE_GET_BUCKET_VERSIONING | storage:get-bucket-versioning | Storage |
STORAGE_SET_BUCKET_VERSIONING | storage:set-bucket-versioning | Storage |
STORAGE_LIST_OBJECT_VERSIONS | storage:list-object-versions | Storage |
STORAGE_DELETE_OBJECT_VERSION | storage:delete-object-version | Storage |
STORAGE_RESTORE_OBJECT_VERSION | storage:restore-object-version | Storage |
STORAGE_GET_PRESIGNED_URL | storage:get-presigned-url | Storage |
STORAGE_GET_HOMEDIR | storage:get-homedir | Storage |
TRANSFER_ADD | transfer:add | Transfer |
TRANSFER_PAUSE | transfer:pause | Transfer |
TRANSFER_RESUME | transfer:resume | Transfer |
TRANSFER_CANCEL | transfer:cancel | Transfer |
TRANSFER_GET_JOBS | transfer:get-jobs | Transfer |
TRANSFER_CLEAR_COMPLETED | transfer:clear-completed | Transfer |
TRANSFER_PROGRESS | transfer:progress | Transfer |
TRANSFER_CONFLICT_PROMPT | transfer:conflict-prompt | Transfer |
TRANSFER_CONFLICT_RESPOND | transfer:conflict-respond | Transfer |
START_DRAG | drag:start | Transfer |
QUIT_CONFIRM_PROMPT | app:quit-confirm-prompt | Quit confirmation (renders the app's own themed dialog instead of a native OS message box) |
QUIT_CONFIRM_RESPOND | app:quit-confirm-respond | Quit confirmation (renders the app's own themed dialog instead of a native OS message box) |
PROFILES_GET | profiles:get | Profiles |
PROFILES_SAVE_SSH | profiles:save-ssh | Profiles |
PROFILES_DELETE_SSH | profiles:delete-ssh | Profiles |
PROFILES_SAVE_S3 | profiles:save-s3 | Profiles |
PROFILES_DELETE_S3 | profiles:delete-s3 | Profiles |
PROFILES_SAVE_FOLDER | profiles:save-folder | Profiles |
PROFILES_DELETE_FOLDER | profiles:delete-folder | Profiles |
PROFILES_RENAME_FOLDER | profiles:rename-folder | Profiles |
PROFILES_IMPORT_SSH_CONFIG | profiles:import-ssh-config | Profiles |
PROFILES_EXPORT_JSON | profiles:export-json | Profiles |
PROFILES_IMPORT_JSON | profiles:import-json | Profiles |
SESSION_GET | session:get | Session & Tabs |
SESSION_SAVE | session:save | Session & Tabs |
CLIPBOARD_HISTORY_LIST | clipboardHistory:list | Session & Tabs |
CLIPBOARD_HISTORY_ADD | clipboardHistory:add | Session & Tabs |
CLIPBOARD_HISTORY_DELETE | clipboardHistory:delete | Session & Tabs |
CLIPBOARD_HISTORY_CLEAR | clipboardHistory:clear | Session & Tabs |
SNIPPETS_LIST | snippets:list | Session & Tabs |
SNIPPETS_SAVE | snippets:save | Session & Tabs |
SNIPPETS_DELETE | snippets:delete | Session & Tabs |
SETTINGS_GET | settings:get | Settings |
SETTINGS_SAVE | settings:save | Settings |
PROFILE_SYNC_SETUP | profile-sync:setup | between their own machines via a Zero-Knowledge-encrypted S3/SFTP target.) |
PROFILE_SYNC_ENABLE | profile-sync:enable | between their own machines via a Zero-Knowledge-encrypted S3/SFTP target.) |
PROFILE_SYNC_PUSH | profile-sync:push | between their own machines via a Zero-Knowledge-encrypted S3/SFTP target.) |
PROFILE_SYNC_PULL | profile-sync:pull | between their own machines via a Zero-Knowledge-encrypted S3/SFTP target.) |
PROFILE_SYNC_STATUS | profile-sync:status | between their own machines via a Zero-Knowledge-encrypted S3/SFTP target.) |
PROFILE_SYNC_COMPARE | profile-sync:compare | between their own machines via a Zero-Knowledge-encrypted S3/SFTP target.) |
PROFILE_SYNC_SET_AUTO_SYNC | profile-sync:set-auto-sync | between their own machines via a Zero-Knowledge-encrypted S3/SFTP target.) |
PROFILE_SYNC_UNLOCK_SMARTCARD | profile-sync:unlock-smartcard | between their own machines via a Zero-Knowledge-encrypted S3/SFTP target.) |
PROFILE_SYNC_LINK_SMARTCARD | profile-sync:link-smartcard | between their own machines via a Zero-Knowledge-encrypted S3/SFTP target.) |
PROFILE_SYNC_UNLINK_SMARTCARD | profile-sync:unlink-smartcard | between their own machines via a Zero-Knowledge-encrypted S3/SFTP target.) |
PROFILE_SYNC_WIPE | profile-sync:wipe | between their own machines via a Zero-Knowledge-encrypted S3/SFTP target.) |
CONNECTION_TEST_SSH | connection:test-ssh | Connection Testing |
SSH_LIST_PUBLIC_KEYS | ssh:list-public-keys | Install public keys in a host's authorized_keys (ssh-copy-id) |
SSH_INSTALL_PUBLIC_KEYS | ssh:install-public-keys | Install public keys in a host's authorized_keys (ssh-copy-id) |
SSH_PROBE_HOST | ssh:probe-host | Install public keys in a host's authorized_keys (ssh-copy-id) |
SSH_TEST_LOGIN | ssh:test-login | Install public keys in a host's authorized_keys (ssh-copy-id) |
SSH_BUILD_INSTALL_COMMAND | ssh:build-install-command | Install public keys in a host's authorized_keys (ssh-copy-id) |
CONNECTION_TEST_S3 | connection:test-s3 | Install public keys in a host's authorized_keys (ssh-copy-id) |
AWS_SSO_LOGIN | aws-sso:login | AWS SSO login (device-authorization flow) |
AWS_SSO_LOGIN_CANCEL | aws-sso:login-cancel | AWS SSO login (device-authorization flow) |
AWS_SSO_PROMPT | aws-sso:prompt | AWS SSO login (device-authorization flow) |
AWS_SSO_LIST_ACCOUNTS | aws-sso:list-accounts | AWS SSO login (device-authorization flow) |
AWS_SSO_LIST_ROLES | aws-sso:list-roles | AWS SSO login (device-authorization flow) |
GIT_FETCH_PUBLIC_KEYS | git:fetch-public-keys | Git & Git Provider Integration |
GIT_GET_SIGNING_CONFIG | git:get-signing-config | Git & Git Provider Integration |
GIT_CONFIGURE_SIGNING | git:configure-signing | Git & Git Provider Integration |
GIT_SET_SIGNING_ENABLED | git:set-signing-enabled | Git & Git Provider Integration |
GIT_GET_STATUS | git:get-status | Git & Git Provider Integration |
GIT_CLONE | git:clone | Git & Git Provider Integration |
GIT_PULL | git:pull | Git & Git Provider Integration |
GIT_TEST_REMOTE_ACCESS | git:test-remote-access | Git & Git Provider Integration |
DOTFILES_IMPORT_FROM_GIT | dotfiles:import-from-git | Git & Git Provider Integration |
SSH_AGENT_STATUS | ssh:agent-status | SSH Agent |
DOTFILES_POOLS_GET | dotfiles:pools-get | Dotfiles pools (opt-in, see AppSettings.dotfilesPoolEnabled) |
DOTFILES_POOLS_SAVE | dotfiles:pools-save | Dotfiles pools (opt-in, see AppSettings.dotfilesPoolEnabled) |
DOTFILES_POOLS_DELETE | dotfiles:pools-delete | Dotfiles pools (opt-in, see AppSettings.dotfilesPoolEnabled) |
DOTFILES_OPEN_FOLDER | dotfiles:open-folder | Dotfiles pools (opt-in, see AppSettings.dotfilesPoolEnabled) |
DOTFILES_SELECT_FILES | dotfiles:select-files | Dotfiles pools (opt-in, see AppSettings.dotfilesPoolEnabled) |
DOTFILES_READ_SOURCES | dotfiles:read-sources | Dotfiles pools (opt-in, see AppSettings.dotfilesPoolEnabled) |
DOTFILES_ADD_FROM_STORAGE | dotfiles:add-from-storage | Dotfiles pools (opt-in, see AppSettings.dotfilesPoolEnabled) |
DOTFILES_SYNC_PROMPT | dotfiles:sync-prompt | Dotfiles pools (opt-in, see AppSettings.dotfilesPoolEnabled) |
DOTFILES_SYNC_RESPOND | dotfiles:sync-respond | Dotfiles pools (opt-in, see AppSettings.dotfilesPoolEnabled) |
DOTFILES_SYNC_STATUS | dotfiles:sync-status | Dotfiles pools (opt-in, see AppSettings.dotfilesPoolEnabled) |
FILE_READ | file:read | File Editor |
FILE_SAVE | file:save | File Editor |
FILE_OPEN_EXTERNAL | file:open-external | File Editor |
FILE_CLOSE_EXTERNAL | file:close-external | File Editor |
FILE_EXTERNAL_STATUS | file:external-status | File Editor |
FILE_TAIL_START | file:tail:start | File Editor |
FILE_TAIL_STOP | file:tail:stop | File Editor |
FILE_TAIL_DATA | file:tail:data | File Editor |
FILE_TAIL_ERROR | file:tail:error | File Editor |
SEARCH_START | search:start | Content search ("search inside files") |
SEARCH_CANCEL | search:cancel | Content search ("search inside files") |
SEARCH_PREVIEW | search:preview | Content search ("search inside files") |
SEARCH_RESULT | search:result | Content search ("search inside files") |
SEARCH_PROGRESS | search:progress | Content search ("search inside files") |
SEARCH_ERROR | search:error | Content search ("search inside files") |
SEARCH_DONE | search:done | Content search ("search inside files") |
APP_OPEN_EXTERNAL | app:open-external | General |
APP_GET_VERSION | app:get-version | General |
UPDATE_GET_STATE | update:get-state | General |
UPDATE_CHECK | update:check | General |
UPDATE_DOWNLOAD | update:download | General |
UPDATE_INSTALL | update:install | General |
UPDATE_STATE | update:state | General |
APP_GET_HOMEDIR | app:get-homedir | General |
APP_GET_PLATFORM | app:get-platform | General |
APP_GET_HOSTNAME | app:get-hostname | General |
APP_GET_SECURITY_STATUS | app:get-security-status | General |
APP_DETECT_LOCAL_SHELLS | app:detect-local-shells | General |
APP_CHECK_X11_SERVER | app:check-x11-server | General |
X11_GET_STATUS | x11:get-status | General |
X11_START_SERVER | x11:start-server | General |
X11_STOP_SERVER | x11:stop-server | General |
DIALOG_OPEN_FILE | dialog:open-file | General |
DIALOG_OPEN_FOLDER | dialog:open-folder | General |
DIALOG_SAVE_FILE | dialog:save-file | General |
DIR_SYNC_COMPUTE_DIFF | dirsync:compute-diff | Directory sync (dual-pane folder → folder diff/copy between any two storage providers) |
DIR_SYNC_SCAN_PROGRESS | dirsync:scan-progress | Directory sync (dual-pane folder → folder diff/copy between any two storage providers) |
DIR_SYNC_APPLY | dirsync:apply | Directory sync (dual-pane folder → folder diff/copy between any two storage providers) |
DIR_SYNC_APPLY_PROGRESS | dirsync:apply-progress | Directory sync (dual-pane folder → folder diff/copy between any two storage providers) |
DIR_SYNC_PROFILE_LIST | dirsync:profile-list | Directory sync (dual-pane folder → folder diff/copy between any two storage providers) |
DIR_SYNC_PROFILE_SAVE | dirsync:profile-save | Directory sync (dual-pane folder → folder diff/copy between any two storage providers) |
DIR_SYNC_PROFILE_DELETE | dirsync:profile-delete | Directory sync (dual-pane folder → folder diff/copy between any two storage providers) |
K8S_LIST_CONTEXTS | k8s:list-contexts | Kubernetes / OpenShift discovery |
K8S_LIST_NAMESPACES | k8s:list-namespaces | Kubernetes / OpenShift discovery |
K8S_LIST_PODS | k8s:list-pods | Kubernetes / OpenShift discovery |
K8S_RELOAD | k8s:reload | Kubernetes / OpenShift discovery |
K8S_LOGIN | k8s:login | Kubernetes / OpenShift discovery |
K8S_CONFIG_CHANGED | k8s:config-changed | Kubernetes / OpenShift discovery |
K8S_TERMINAL_CREATE | k8s-terminal:create | Kubernetes / OpenShift interactive exec terminal |
K8S_TERMINAL_WRITE | k8s-terminal:write | Kubernetes / OpenShift interactive exec terminal |
K8S_TERMINAL_RESIZE | k8s-terminal:resize | Kubernetes / OpenShift interactive exec terminal |
K8S_TERMINAL_KILL | k8s-terminal:kill | Kubernetes / OpenShift interactive exec terminal |
PERF_SSH_SAMPLE | perf:ssh-sample | Kubernetes / OpenShift interactive exec terminal |
PERF_K8S_SAMPLE | perf:k8s-sample | Kubernetes / OpenShift interactive exec terminal |
PERF_LOCAL_SAMPLE | perf:local-sample | Kubernetes / OpenShift interactive exec terminal |
K8S_TERMINAL_DATA | k8s-terminal:data | Kubernetes / OpenShift interactive exec terminal |
K8S_TERMINAL_EXIT | k8s-terminal:exit | Kubernetes / OpenShift interactive exec terminal |
K8S_LOG_START | k8s-log:start | Kubernetes / OpenShift log follow |
K8S_LOG_STOP | k8s-log:stop | Kubernetes / OpenShift log follow |
K8S_LOG_DATA | k8s-log:data | Kubernetes / OpenShift log follow |
K8S_LOG_END | k8s-log:end | Kubernetes / OpenShift log follow |
K8S_POD_DESCRIBE | k8s:pod-describe | Kubernetes / OpenShift pod describe & details |
K8S_PORT_FORWARD_START | k8s-port-forward:start | Kubernetes / OpenShift port forward |
K8S_PORT_FORWARD_STOP | k8s-port-forward:stop | Kubernetes / OpenShift port forward |
K8S_PORT_FORWARD_LIST | k8s-port-forward:list | Kubernetes / OpenShift port forward |
K8S_PORT_FORWARD_EVENT | k8s-port-forward:event | Kubernetes / OpenShift port forward |
K8S_DEBUG_ATTACH | k8s:debug-attach | Kubernetes / OpenShift debug |
SSH_TUNNEL_START | ssh-tunnel:start | SSH tunnels (standalone port forwarding, independent of terminal sessions) |
SSH_TUNNEL_STOP | ssh-tunnel:stop | SSH tunnels (standalone port forwarding, independent of terminal sessions) |
SSH_TUNNEL_LIST | ssh-tunnel:list | SSH tunnels (standalone port forwarding, independent of terminal sessions) |
SSH_TUNNEL_EVENT | ssh-tunnel:event | SSH tunnels (standalone port forwarding, independent of terminal sessions) |
SSH_TUNNEL_CHECK_PORT | ssh-tunnel:check-port | SSH tunnels (standalone port forwarding, independent of terminal sessions) |
5. Troubleshooting & Diagnostics Runbook
| Symptom / Error Message | Probable Root Cause | Corrective Action |
|---|---|---|
IPC channel not registered | Version mismatch between renderer and main | Verify IpcBridge.ts registers a handler matching the channel constant. |
safeStorage is not available | Operating system keyring service is not running | In headless Linux setups, ensure gnome-keyring or a compatible Secret Service daemon is active. |
ELECTRON_RUN_AS_NODE rejected | Binary fuses were modified or corrupted | Validate binary fuses using npx @electron/fuses read --app . |