bascule-oss/docs/architecture.md
Tyler King 6eb2de5dc0 docs: update all documentation for management API + dashboard
Updated 9 files to reflect:
  Management API (axum, port 9090) — embedded in bascule-server
  Dioxus dashboard components (WASM web target)
  6 crates in workspace (was 4)

README.md:
  Added Management API + Dashboard features section
  Added dashboard row to comparison table

docs/architecture.md:
  Updated diagram showing dual-listener architecture
  Added Management API section explaining Arc<SessionStore> sharing
  Updated crate table (6 crates)

docs/configuration.md:
  Added [dashboard] config section reference

docs/observability.md:
  Added Management API monitoring section with curl examples

docs/quickstart.md:
  Added Management API quick start section

docs/comparison.md:
  Added dashboard and TPM attestation rows

CLAUDE.md + CONTRIBUTING.md:
  Updated crate lists and feature flags

config/bascule.example.toml:
  Added [dashboard] section

All 17 README links verified valid. Build clean.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-05 17:17:18 -04:00

4.7 KiB

Architecture

Overview

Bascule is a single-binary SSH proxy with a built-in management API:

                       ┌─────────────────────────────────────┐
Operator workstation   │  bascule (single binary)            │
┌───────────────┐      │                                     │
│ bascule-shell │ SSH  │  Port 2222: SSH Proxy (russh)       │
│  identity +   │─────▶│    ├── AuthProvider                 │
│  TPM attest   │      │    ├── SessionHandler hooks         │
└───────────────┘      │    └── SessionBackend               │
                       │          ├── Local PTY               │
Browser / curl         │          ├── Remote Proxy            │
┌───────────────┐ HTTP │          └── Container               │
│  Dashboard    │─────▶│                                     │
│  /api/*       │      │  Port 9090: Management API (axum)   │
└───────────────┘      │    ├── /api/sessions                │
                       │    ├── /api/stats                   │
                       │    ├── /api/health                  │
                       │    └── /dashboard (WASM, planned)   │
                       │                                     │
                       │  Arc<SessionStore> shared            │
                       └─────────────────────────────────────┘

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:

#[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:

#[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<String, String>;
    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.

Management API

The management API runs alongside the SSH proxy in the same process. Both share an Arc<SessionStore> — when a session starts via SSH, the store updates; when the dashboard polls /api/sessions, it reads the same store. Zero serialization, zero IPC.

Enable via [dashboard] config section or --features dashboard (default on).

Crate Structure

Crate Purpose
bascule-core Library — server, handler, auth, PTY, proxy, container, hooks, store
bascule-server Binary — CLI, config, tracing, management API
bascule-auth-agent-id Optional — Entra Agent ID authentication
bascule-shell Binary — Identity-aware login shell with TPM
bascule-dashboard Library — Dioxus UI components
bascule-dashboard-web Binary — WASM web dashboard target