# DodoSSH A self-hosted, team-oriented SSH client with an end-to-end encrypted vault. Manage hosts, credentials and keys in a desktop app; sync them across your devices and share them with teammates through a server you run yourself. **The server stores ciphertext and never holds a key** — the operator cannot read the credentials it stores. > Status: early development. See [the milestone plan](#milestones) for what exists today. ## Why Teams either scatter SSH credentials across individual `~/.ssh` directories with no sharing story, or pay per-seat for a hosted product that holds their infrastructure credentials. DodoSSH keeps the convenience of a synced, shareable vault while remaining self-hostable and zero-knowledge. ## Architecture | Component | Choice | | --- | --- | | Backend | ASP.NET Core on .NET 10, PostgreSQL + EF Core | | Client | Avalonia (C#) for Windows/Linux/macOS; terminal pane is a WebView running xterm.js | | Auth | OIDC, provider-agnostic (Entra ID, Keycloak, Auth0, Authentik) | | Vault | End-to-end encrypted; X25519 + Ed25519 + XChaCha20-Poly1305, Argon2id unlock | | Connections | Client-direct SSH by default, with an optional raw-TCP server relay | Two consequences worth knowing before you read further: - **Revocation is not retroactive.** A removed member keeps what they already downloaded. The real remediation is rotating the SSH credential, so offboarding is built around a rotation checklist rather than a button that implies more than it delivers. - **No session recording in relay mode.** The relay forwards SSH ciphertext, so it cannot see commands. That is the cost of the relay not being able to read your traffic. The reasoning behind each major decision is recorded in [`docs/adr/`](docs/adr/), starting with [the E2EE trust model](docs/adr/0001-e2ee-trust-model.md). ## Repository layout ``` src/ DodoSSH.Contracts DTOs shared with the client — the real API contract DodoSSH.Crypto DSH1 envelope, AAD derivation, key wrapping DodoSSH.Domain entities and invariants, no EF DodoSSH.Infrastructure DbContext, configurations, migrations DodoSSH.Api the host tests/ one test project per source project docs/adr/ architecture decision records ``` ## Building Requires the .NET SDK pinned in [`global.json`](global.json) (10.0.x). ```bash dotnet build DodoSSH.slnx ``` ```bash dotnet test DodoSSH.slnx ``` Run the API locally: ```bash dotnet run --project src/DodoSSH.Api ``` It listens on `http://localhost:5233`, serving `/healthz/live`, `/healthz/ready` and — in Development — `/openapi/v1.json`. ### Conventions the build enforces - Warnings are errors. `dotnet format --verify-no-changes` gates CI. - Package versions are centralised in `Directory.Packages.props`; `packages.lock.json` is committed and CI restores in locked mode. - [`BannedSymbols.txt`](BannedSymbols.txt) bans `DateTime.UtcNow` (use `TimeProvider`), `Guid.NewGuid` (use `CreateVersion7`), sync-over-async, MD5/SHA1 and PBKDF2. - Public members of `DodoSSH.Contracts` must be declared in `PublicAPI.Unshipped.txt`, so a contract change is a build error rather than a client-side surprise. ## Milestones - **M0 — foundation.** Repo structure, build conventions, CI, ADRs. *Done.* - **M1 — vertical slice.** OIDC login → enroll → create a host → open a shell. *Backend done:* the DSH1 crypto core, the data model, sync push/pull for hosts, `/me`, and enrollment with the identity-provider key binding. *Remaining:* the desktop client, and the two spikes that gate it — the Linux WebView and SSH.NET's `window-change`. Both need a Linux and a macOS machine, so neither has run yet. - **M2 — full personal vault**, robust sync, relay. - **M3 — teams**, sharing, ACLs. - **M4 — hardening and ops**, packaging, self-hosting guide. - **M5 — multi-provider OIDC**, key rotation, per-item content keys. ## Licence Not yet chosen.