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>
|
||
|---|---|---|
| .github/workflows | ||
| charts/bascule | ||
| config | ||
| crates | ||
| docs | ||
| images | ||
| .editorconfig | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| CONTRIBUTING.md | ||
| DCO | ||
| docker-compose.yml | ||
| Dockerfile | ||
| GOVERNANCE.md | ||
| LICENSE | ||
| Makefile | ||
| NOTICE | ||
| README.md | ||
| rustfmt.toml | ||
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 statusGET /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... ║
╚═══════════════════════════════════════════════════════╝
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
- Quick Start
- Configuration
- Authentication
- Architecture
- Observability
- Kubernetes
- bascule-shell
- Comparison
- Container Images
- Contributing
License
Apache 2.0