3.5 KiB
3.5 KiB
Architecture
Sources/RemminaMac/
RemminaMacApp.swift # @main, WindowGroup, loads ProfileStore
Models/ConnectionProfile.swift # ProtocolType + ConnectionProfile + StoredSSHKey (Codable)
Models/ProfileValidation.swift # input checks + trimming (pure, tested)
Stores/ProfileStore.swift # ObservableObject, JSON persistence, filter/search/groups
Security/KeychainHelper.swift # SecItemAdd/Copy/Delete wrapper
Services/ConnectionServices.swift # SSHService (ssh args, secrets, sshpass, agent) + RDPService
Services/PTYSession.swift # real PTY session (posix_openpt), stderr pipe, TerminalManager
Services/AnsiParser.swift # ANSI/SGR parser for diagnostics buffer (pure, tested)
Services/TCPCheck.swift # TCP pre-check via Network.framework
Services/HostKeyService.swift # keyscan/fingerprint/accept/revoke via system ssh tools
Services/TerminalTheme.swift # Dracula palette (SwiftUI + NSColor + SwiftTerm)
Views/ContentView.swift # NavigationSplitView: sidebar + tabs + status bar
Views/SessionView.swift # per-session detail
Views/SwiftTermView.swift # full terminal emulator view (SwiftTerm, Dracula)
Views/ProfileEditorView.swift # new/edit form + Keychain wiring + validation
Views/SSHKeysView.swift # key list, import, ssh-keygen
Views/HostKeySheet.swift # fingerprint popup (accept/decline, MITM warning)
Views/AboutView.swift # about sheet (contributor, repo link)
Sources/ctty-run/
ctty-run.c # gives the ssh child a controlling terminal
# (double-fork: setsid + slave open + exec)
Data flow
RemminaMacAppcreatesProfileStoreas@StateObject, injects asenvironmentObject.ProfileStore.load()readsprofiles.json+sshkeys.json, else sample data.ContentViewfilters viasearchText/selectedGroup;selectiondrives detail.connect(_:)stampslastConnected, opens a tab. The terminal view runs: TCP pre-check → host-key status (popup if unknown/changed) →PTYSession.start().PTYSessionspawns viactty-run(controlling terminal): stdin/stdout on the pty slave, stderr on a loss-free pipe. Output feeds SwiftTerm (onData) and the diagnostics buffer (lines, used by the error banner + report).- Keyboard flows from SwiftTerm's delegate straight into the pty master
(
sendBytes); window resizes propagate viaTIOCSWINSZ(setWinsize). - Secrets only via
KeychainHelper; JSON holds account references.TerminalManagercaches one session per profile and reconfigures on any config change (fingerprint incl. secret + sshpass presence).
Key files
Stores/ProfileStore.swift— paths +load()/save()Models/ConnectionProfile.swift— protocol defaults (ports, icons)Services/PTYSession.swift— spawn, lifecycle,TerminalManagerServices/HostKeyService.swift— fingerprint flowViews/SwiftTermView.swift— emulator wiring + Dracula themeViews/ContentView.swift— Remmina layout
Verification strategy
Logic is verified with throwaway swiftc harnesses in /tmp (never committed):
ANSI parser, PTY roundtrips, real ssh runs, Keychain roundtrips, TCP checks,
host-key flow against a local paramiko test server, SwiftTerm core headless
(cursor addressing, alt-screen, clear), view-layer stress. See docs/ERRORS.md.
Next steps
- Embedded RDP (FreeRDP bridge) instead of
Process. - Integrated SFTP browser.
- Export/Import buttons + drag & drop for keys.
- Tray / favorites / folder badges like Remmina.