# Architecture ## Overview Bascule is a single-binary SSH proxy with three pluggable layers: ``` Client SSH → Bascule Server │ ├── AuthProvider (SSH keys / OIDC / Agent ID) │ ├── SessionHandler hooks │ on_session_start → build_session_env → on_exec → on_session_end │ └── SessionBackend ├── LocalPty (portable-pty → /bin/bash) ├── RemoteProxy (upstream SSH → target host) └── Container (docker/podman → ephemeral image) ``` ## Session Backends ### Local PTY (default) Spawns a process on this machine via `portable-pty`. The process gets a real PTY with terminal emulation, resize support, and environment variables. ### Remote Proxy Opens a second SSH connection to a target host via `russh` client. Bridges I/O between the client and upstream channels. The client sees a transparent connection to the target host, but Bascule mediates authentication and applies SessionHandler hooks. ### Container Spawns an ephemeral container per session using docker, podman, or nerdctl. The container is destroyed on disconnect. The container image defines what tools are available — if it's not in the image, the operator can't use it. ## Extension Model ### AuthProvider trait Implement to add custom authentication: ```rust #[async_trait] pub trait AuthProvider: Send + Sync + 'static { async fn check_public_key(&self, user: &str, key: &PublicKey) -> bool; async fn check_password(&self, user: &str, password: &str) -> bool; fn principal_for_user(&self, user: &str) -> String; } ``` ### SessionHandler trait Implement to add policy, audit, or custom behavior: ```rust #[async_trait] pub trait SessionHandler: Send + Sync + 'static { async fn on_session_start(&self, session: &SessionInfo) -> anyhow::Result<()>; async fn build_session_env(&self, session: &SessionInfo) -> HashMap; async fn on_exec(&self, session: &SessionInfo, command: &str) -> anyhow::Result<()>; async fn on_session_end(&self, session: &SessionInfo) -> anyhow::Result<()>; fn display_name(&self, session: &SessionInfo) -> String; } ``` All methods have default implementations (accept/passthrough). Override only what you need. ## Security Model ### Container hardening (default) When `hardened = true`: - `--security-opt no-new-privileges` - `--cap-drop ALL` - `--cap-add SETUID --cap-add SETGID` (minimal for shell) ### Ephemeral sessions When `ephemeral = true` (default), the container is `--rm` and destroyed on disconnect. Nothing persists between sessions. ### Network isolation Set `network = "none"` to completely isolate the container from the network. The operator can run local tools but can't reach external services. ## Crate Structure | Crate | Purpose | |-------|---------| | `bascule-core` | Library — server, handler, auth, PTY, proxy, container, hooks | | `bascule-server` | Binary — CLI, config loading, tracing setup | | `bascule-auth-agent-id` | Optional — Entra Agent ID authentication |