# 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 1. `RemminaMacApp` creates `ProfileStore` as `@StateObject`, injects as `environmentObject`. 2. `ProfileStore.load()` reads `profiles.json` + `sshkeys.json`, else sample data. 3. `ContentView` filters via `searchText`/`selectedGroup`; `selection` drives detail. 4. `connect(_:)` stamps `lastConnected`, opens a tab. The terminal view runs: TCP pre-check → host-key status (popup if unknown/changed) → `PTYSession.start()`. 5. `PTYSession` spawns via `ctty-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). 6. Keyboard flows from SwiftTerm's delegate straight into the pty master (`sendBytes`); window resizes propagate via `TIOCSWINSZ` (`setWinsize`). 7. Secrets only via `KeychainHelper`; JSON holds account references. `TerminalManager` caches 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, `TerminalManager` - `Services/HostKeyService.swift` — fingerprint flow - `Views/SwiftTermView.swift` — emulator wiring + Dracula theme - `Views/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 1. Embedded RDP (FreeRDP bridge) instead of `Process`. 2. Integrated SFTP browser. 3. Export/Import buttons + drag & drop for keys. 4. Tray / favorites / folder badges like Remmina.