Open-source SSH proxy with identity-aware shell (Rust)
Find a file
Claude Code 56529626f6 feat(m2): bascule-shell exports BASCULE_ROLES for gsh's role check
bascule-shell::set_env now populates BASCULE_ROLES so gsh's
M2 role-aware classifier has something to match against.

Precedence:
  1. Caller-set BASCULE_ROLES wins (env var preserved as-is).
  2. Otherwise derive a default from auth_method:
       oidc-entra | oidc-cached | kerberos -> operator
       ssh-key                              -> apprentice
       _                                    -> apprentice

The auth-method fallback is intentionally minimal — bascule-oss
Identity has no real roles field, and proper role provisioning
(Entra group claims, SPIFFE workload roles) lands in M5. This
default at least populates the env var so M2's role-deny path
is exercised end-to-end on existing dev shells instead of
silently empty.

Stacked on feat/m1-session-sat.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude Code <claude@guildhouse.dev>
2026-04-07 17:52:50 -04:00
.github/workflows docs: DCO, NOTICE, and governance framework 2026-04-05 11:13:20 -04:00
charts/bascule docs: comprehensive documentation + developer experience polish 2026-04-05 10:53:08 -04:00
config docs: update all documentation for management API + dashboard 2026-04-05 17:17:18 -04:00
crates feat(m2): bascule-shell exports BASCULE_ROLES for gsh's role check 2026-04-07 17:52:50 -04:00
docs docs: update all documentation for management API + dashboard 2026-04-05 17:17:18 -04:00
images feat: structured logging, tracing spans, comprehensive documentation 2026-04-04 23:45:03 -04:00
.editorconfig docs: comprehensive documentation + developer experience polish 2026-04-05 10:53:08 -04:00
.gitignore feat: Bascule — identity-aware SSH proxy 2026-04-04 22:25:33 -04:00
Cargo.lock feat(m1): bascule-shell composes a real SAT anchored on session_leaf 2026-04-07 14:38:20 -04:00
Cargo.toml feat(m1): bascule-shell composes a real SAT anchored on session_leaf 2026-04-07 14:38:20 -04:00
CLAUDE.md docs: update all documentation for management API + dashboard 2026-04-05 17:17:18 -04:00
CONTRIBUTING.md docs: update all documentation for management API + dashboard 2026-04-05 17:17:18 -04:00
DCO docs: DCO, NOTICE, and governance framework 2026-04-05 11:13:20 -04:00
docker-compose.yml feat: bascule-shell — identity-aware shell with TPM attestation 2026-04-05 09:47:46 -04:00
Dockerfile feat: bascule-shell — identity-aware shell with TPM attestation 2026-04-05 09:47:46 -04:00
GOVERNANCE.md docs: DCO, NOTICE, and governance framework 2026-04-05 11:13:20 -04:00
LICENSE feat: bascule-shell — identity-aware shell with TPM attestation 2026-04-05 09:47:46 -04:00
Makefile docs: comprehensive documentation + developer experience polish 2026-04-05 10:53:08 -04:00
NOTICE docs: DCO, NOTICE, and governance framework 2026-04-05 11:13:20 -04:00
README.md docs: update all documentation for management API + dashboard 2026-04-05 17:17:18 -04:00
rustfmt.toml docs: comprehensive documentation + developer experience polish 2026-04-05 10:53:08 -04:00

Bascule

Identity-aware SSH proxy for modern infrastructure.

Bascule authenticates operators via SSH keys or AI agent tokens, then connects them to a local shell, remote host, or ephemeral container. No agents to install. No control plane. One binary.

Quick Start

cargo build --release -p bascule-server
./target/release/bascule --config config/bascule.example.toml
# In another terminal:
ssh -p 2222 localhost

See docs/quickstart.md for Docker, Helm, and container mode.

Features

Session Backends

Mode Config Description
Local PTY (default) Spawn a local shell process
Remote Proxy [proxy] Forward to a remote SSH host
Container [container] Ephemeral container per session (Docker/Podman/nerdctl)
Kubernetes [k8s] Shared jumphost with shell sidecar (config ready, runtime coming)

Authentication

  • SSH Keys — standard OpenSSH authorized_keys files
  • Accept All — development only, accepts any key
  • Entra Agent ID — Microsoft AI agent identity (--features agent-id)
  • SPIFFE/SPIRE — workload identity (config ready, runtime coming)

Security

  • Session limiting (semaphore-based max_sessions)
  • Container hardening (--cap-drop ALL, --security-opt no-new-privileges)
  • Container config validation (injection prevention)
  • Read-only rootfs option
  • NetworkPolicy for Kubernetes deployments

Management API + Dashboard

Built-in HTTP management API (port 9090, --features dashboard):

  • GET /api/sessions — active sessions with auth/backend/TPM status
  • GET /api/stats — aggregate analytics (auth breakdown, peak concurrent, TPM %)
  • GET /api/health — server health and version
  • WASM dashboard at /dashboard/ (coming soon)

Observability

  • Structured JSON logging (BASCULE_LOG_FORMAT=json)
  • Tracing spans on auth, session lifecycle, exec requests
  • Management API for real-time session monitoring

Client: bascule-shell

Identity-aware login shell with TPM attestation:

./target/release/bascule-shell --info
╔═══════════════════════════════════════════════════════╗
║  Bascule Shell v0.1.0                                 ║
║  Principal:  tking                                     ║
║  Method:     ssh-key                                   ║
║  TPM:        available (6 PCRs verified)               ║
║  Platform:   sha256:e9b95f002f54222d...                ║
╚═══════════════════════════════════════════════════════╝

See docs/bascule-shell.md.

Comparison

Bascule Teleport Boundary
License Apache 2.0 AGPL / Commercial MPL / Commercial
Agents required No Yes Yes
Control plane No Required Required
Container sessions Yes No No
AI Agent Identity Yes (Entra Agent ID) No No
Binary size ~7MB ~150MB ~100MB
Built-in dashboard Yes (port 9090) Yes No

See docs/comparison.md.

Deployment

  • Standalone: cargo build --release -p bascule-server
  • Docker: docker build -t bascule .
  • Kubernetes: helm install bascule charts/bascule/ — see docs/kubernetes.md

Extending Bascule

Implement SessionHandler to add custom policy:

use bascule_core::hooks::{SessionHandler, SessionInfo};

struct AuditHandler;

#[async_trait]
impl SessionHandler for AuditHandler {
    async fn on_session_start(&self, s: &SessionInfo) -> anyhow::Result<()> {
        println!("{} connected from {}", s.principal, s.source_ip);
        Ok(())
    }
}

See docs/architecture.md.

Governance

Bascule is maintained by Guildhouse LLC. Contributions are accepted under the DCO — you retain copyright to your contributions.

The SessionHandler and AuthProvider traits are public APIs. Implementations are the intellectual property of their authors. See GOVERNANCE.md.

Roadmap

Not yet implemented:

  • OIDC authentication (Keycloak, Entra, Okta)
  • K8s API exec backend runtime
  • SPIFFE/SPIRE auth runtime
  • OpenTelemetry OTLP exporter
  • Prometheus metrics endpoint
  • Session recording
  • Per-session Pod isolation

Documentation

License

Apache 2.0