Files
remmina-mac/docs/ARCHITECTURE.md
T

62 lines
3.5 KiB
Markdown

# 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.