Three path deps in this workspace's Cargo.toml (substrate-proto, hfl-types, substrate-hfl) pointed at `../substrate-project/substrate/ crates/...`, which resolves correctly only when bascule-oss sits as a sibling to a substrate-project-named directory at the parent level. That's not the current layout: bascule-oss is now nested INSIDE the substrate-project umbrella, as a working tree alongside substrate/ and other Guildhouse repos. The paths don't resolve in the current nested layout; bascule-oss has been locally unbuildable since the reorg (existing Cargo.lock survived only because nobody ran `cargo update` or `cargo generate-lockfile` to force re-resolution). Fix: update the three paths to `../substrate/crates/...`, matching the sibling-checkout layout. Comment updated to name the current convention explicitly rather than referencing an old side-by-side CI claim that no longer matches CI's actual layout either (see below on CI state). What this fix does NOT change - **The architectural rule in CLAUDE.md still stands** — "zero substrate/chronicle/gsap dependencies." This fix is a path correction, not a policy change. - **The rule is already broken in both spirit and letter.** bascule- core depends on substrate-proto unconditionally; substrate-proto depends on substrate-common; both names are substrate-prefixed. The CI contamination check at .github/workflows/ci.yml:81-88 greps Cargo.lock for "substrate|chronicle|gsap|hfl|metakernel" and fails on any match — current Cargo.lock contains substrate- common, substrate-hfl, substrate-proto, hfl-types, so CI would fail on a fresh run regardless of this patch. - **This fix is a LOCAL buildability unblock only.** It makes `cargo check -p bascule-core` pass in the current layout. It does NOT fix CI, which is independently broken for the reasons above. CI state The bascule-oss CI workflow uses `actions/checkout@v4` with no coordinating checkout of substrate-project or its substrate/ subrepo. Regardless of whether bascule-oss's paths use `../substrate/...` or `../substrate-project/substrate/...`, CI currently has nothing to resolve them against. And the contamination check would flag the resulting Cargo.lock even if the paths did resolve. CI is already red (or will be, on next trigger). Re-enabling CI against bascule-oss requires separate coordination work: either adding a substrate-project checkout step to the workflow, or renaming substrate-proto (see below), or amending the contamination check to allow specific neutral-protocol crates by name. That work is out of scope for this patch. Architectural open item: substrate-proto naming violation Documented in the new NOTES-SUBSTRATE-PROTO-NAMING-VIOLATION.md. Summary: substrate-proto's CONTENT is a legitimate neutral protocol contract (proto-generated SAT types bascule-core needs to compose session attestations). The NAMING is substrate- prefixed, which both violates CLAUDE.md's string-match rule and creates an optical problem for CNCF sandbox positioning — sandbox committees read dependency names more than content when evaluating whether a project is a generic open-source substrate or a specific company's product with open-source framing. Proposed resolution (post-v1): rename substrate-proto → sat-proto (or attestation-proto), move the consumed subset of substrate- common's public surface out of substrate-common into the neutrally- named crate, update bascule-core and any other consumers, regenerate Cargo.lock, confirm the contamination check passes. This is NOT v1-blocking. This IS CNCF-sandbox-positioning work — worth doing in coordination with sandbox application prep so the naming story and the institutional positioning story land together. Why we're shipping this narrower fix now Phase 5.5c-c of the substrate foundation work (the adapter crate bridging bascule-core session lifecycle to bascule-revocation- ingress SessionLookup) requires depending on bascule-core as a cross-workspace path dep. Without this path fix, substrate-side crates can't pull bascule-core locally, blocking 5.5c-c implementation. The path fix costs one line per dep and introduces no new coupling; the deeper rename is several-repo coordination that would delay 5.5c-c unnecessarily. Fix-what's-blocking, document-what's-underlying, keep moving. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Tyler J King <tking@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 | ||
| NOTES-SUBSTRATE-PROTO-NAMING-VIOLATION.md | ||
| 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