Commit graph

12 commits

Author SHA256 Message Date
eab96ef3d4 bascule-gateway: fix env-prefix separator + embedded accord parse
Two bugs surfaced when bascule-gateway pods first reached Running and
attempted config load (F.4 deployment):

1. Env-var override didn't take effect for any BASCULE_* variable.
   config::Environment::with_prefix("BASCULE") without an explicit
   prefix_separator strips the literal "BASCULE" with no separator,
   so BASCULE_ACCORD_PATH became "_ACCORD_PATH" (leading underscore)
   which doesn't match the field "accord_path". Result: every env
   override silently fell back to the default in config.rs, and the
   pod read /accord/accord.yaml instead of /etc/bascule/accord.yaml
   from the configured volume. Adds .prefix_separator("_") to match
   QM's pattern in services/quartermaster/src/config.rs:150.

2. Embedded fallback accord YAML had `sampled: []` and a stray
   `sampleRate: 1`, but the schema has
   `sampled: Option<SampledConfig {events, sample_rate}>` — empty
   list mis-parses as struct. Result: when accord file lookup failed,
   the .expect("empty accord must parse") panicked, crashing the
   bascule-gateway container. Now omitted (Option default None).

Both fixes verified against accord-core's schema in
services/accord-core/src/schema.rs.

Signed-off-by: Tyler J King <tking@guildhouse.dev>
2026-04-25 05:00:10 -04:00
2cfc0b4d5e packaging(bascule-gateway): production Dockerfile
Multi-stage rust:bookworm → debian:bookworm-slim build, modeled on
guildhouse/services/Dockerfile (F.2). Build context is the
substrate-project repo root because bascule-gateway's Cargo.toml has
cross-workspace path deps reaching:

  - ../../substrate/crates/governance-types (and substrate-rt
    transitively, which inherits edition from substrate's workspace
    root — substrate must be COPYed as a whole for the inheritance
    chain to load)
  - ../../guildhouse/services/{accord-core, accord-opa, qm-core,
    guildhouse-proto}
  - ../../guildhouse/sdk/{guildhouse-mq, guildhouse-tower} via
    transitive deps

Image output: git.guildhouse.dev/tking/bascule-gateway:v0.1.0.

Signed-off-by: Tyler J King <tking@guildhouse.dev>
2026-04-25 04:57:05 -04:00
3526b6975f bascule-gateway: implement CreateAnchor submission to Quartermaster
Wires the AuditPipeline's flush() path to QM's QuartermasterNotary
gRPC service. Previously flush() only updated local notarized=true
flags; now it batches pending leaf hashes into a CreateAnchorRequest
and persists the returned anchor_id + leaf_index back on each event
row.

Lazy-retry semantics match guildhouse-spire-plugins pkg/governance
(F.1): the gRPC channel is established on first successful flush and
cached in Arc<Mutex<Option<QuartermasterNotaryClient<Channel>>>>. If
QM is unreachable, bascule logs a warning, re-queues the leaves into
the pending buffer, and retries on the next flush interval. Local
audit rows are still written with notarized=true; only anchor_id
stays NULL until an anchor successfully lands. This is the same
pattern that unblocks the bascule-deploys-before-QM ordering problem
without crashing bascule.

Schema: bascule.audit_events already had anchor_id uuid + leaf_index
integer columns (migrations.rs, pre-existing). This commit populates
them for the first time.

Config:
- New `cluster_id` field on BasculeConfig, sourced from
  BASCULE_CLUSTER_ID env. Empty string disables QM submission (local
  storage only). In F.4, bascule gets the UUID from QM's clusters
  table (generated at QM genesis).
- Existing `qm_endpoint` field now actually used (was scaffolded in
  pre-F.4 code but never read).

Backwards-compat:
- submit(&self, event: &AuditEvent, notarize: bool) signature preserved.
- should_notarize(classification, fidelity) public fn preserved.
- Internal leaf_data hashing simplified to an event-field digest
  (event_id + session_id + operator + command + classification +
  exec_result + timestamp); bypasses serde_json_canonicalizer
  dependency that the prior version required. Verify path still
  works against QM's merkle tree because QM hashes whatever bytes
  bascule submits — QM doesn't re-compute; it trusts the leaf
  payload bascule submitted is the leaf.

Signed-off-by: Tyler J King <tking@guildhouse.dev>
2026-04-24 15:40:30 -04:00
Tyler J King
9c492d739a docs: add ARCHITECTURE.md, CHANGELOG, fix Cargo metadata
ARCHITECTURE.md explains the governed shell stack, Keylime integration
model, ShellClass derivation, and implementation status for reviewer
orientation.

CHANGELOG documents v0.1.0-rc.1 deliverables.

Cargo.toml metadata (license, repository) added to bascule-core,
bascule-agent, bascule-gateway.

Signed-off-by: Tyler King <tking@guildhouse.dev>
Signed-off-by: Tyler J King <tking727@gmail.com>
2026-04-15 15:37:27 -04:00
Tyler J King
aa447f151e feat(bascule-core): add DelegationScope for Infrastructure shell pattern
DelegationScope is orthogonal to ShellClass — an Application session
can have delegation authority to orchestrate System operations on
remote targets (the Infrastructure shell pattern for Ansible/Terraform).

TargetSelector supports: None, Hosts (explicit list), LabelSelector
(deferred to K8s API), TrustDomain (all hosts). Default: denied
(fail-closed).

DelegationDecision: Permitted, Denied (with reason), Deferred (for
async label resolution).

Added delegation field to SessionScope with #[serde(default)] for
backward-compatible deserialization.

7 unit tests for delegation scope checking.

Signed-off-by: Tyler King <tking@guildhouse.dev>
Signed-off-by: Tyler J King <tking727@gmail.com>
2026-04-15 15:16:24 -04:00
Tyler J King
ece4e2349f feat(gateway): session downgrade on posture breach
Add breach evaluator that compares posture changes against active
sessions and applies BreachResponse policy:
- LogOnly/AlertDelegates: log, no session enforcement
- ReducePosture: downgrade System -> Application, session continues
- SuspendTrust: terminate session immediately
- RevokeAccord: terminate session, Accord dead

Posture change detection via 30s polling loop on posture-current
ConfigMap (matching existing reaper interval pattern).
No mid-session upgrade — downgrade only, upgrade requires new ceremony.

9 unit tests for breach evaluation covering all BreachResponse variants.

Signed-off-by: Tyler King <tking@guildhouse.dev>
Signed-off-by: Tyler J King <tking727@gmail.com>
2026-04-15 15:16:11 -04:00
Tyler J King
1a54cc3877 feat(bascule-gateway): derive ShellClass at ceremony grant from posture
Read the cluster's operational posture level from the posture-current
ConfigMap at ceremony grant time. Derive ShellClass via
derive_shell_class() and stamp into the granted SessionScope.

- Normal posture (5) -> ShellClass::System
- Any DEFCON escalation -> ShellClass::Application
- Fail-closed: missing ConfigMap -> Lockdown -> Application
- posture_level_at_establishment stored for audit/breach comparison

Signed-off-by: Tyler King <tking@guildhouse.dev>
Signed-off-by: Tyler J King <tking727@gmail.com>
2026-04-15 10:37:30 -04:00
Tyler J King
e28be3335d feat(bascule-core): add ShellClass enum with posture-based derivation
Introduce ShellClass (Application | System) as a session-scoped
classification derived from PostureLevel at ceremony grant time.

- ShellClass::Application: default, software operations only
- ShellClass::System: host operations, requires Normal (5) posture
- derive_shell_class(): pure function, configurable threshold
- satisfies(): hierarchical check (System satisfies Application)
- No mid-session upgrade by design (immutable in SessionScope)

Added shell_class and posture_level_at_establishment to SessionScope
with #[serde(default)] for backward-compatible deserialization.

Signed-off-by: Tyler King <tking@guildhouse.dev>
Signed-off-by: Tyler J King <tking727@gmail.com>
2026-04-15 10:36:45 -04:00
Tyler J King
47a5484614 feat(bascule-agent): replace soft-mode attestation with ConfigMap posture reader
Replace hardcoded posture return in AttestationHandler (Shellstream
namespace 0x0005) with PostureReader that reads the posture-current
ConfigMap written by the substrate-operator's posture evaluator.

Data pipeline is now end-to-end:
  Keylime verifier -> posture evaluator -> ConfigMap -> bascule-agent

Behavior:
- posture_source='config': reads posture-current ConfigMap, maps
  level to PostureLevel, caches with configurable TTL (default 30s)
- posture_source='static' or dev_mode: returns configured static
  level and wire value (replaces hardcoded string for clarity)
- Graceful fallback: missing ConfigMap -> PostureLevel::Lockdown
  (fail-closed) + warning log

New dependencies: kube, k8s-openapi, governance-types (via path).
Does NOT add keylime-client — reads ConfigMap JSON directly.

Signed-off-by: Tyler King <tking@guildhouse.dev>
Signed-off-by: Tyler J King <tking727@gmail.com>
2026-04-15 10:17:00 -04:00
Tyler J King
e3fb2a9a58 refactor(ceremony-engine): use GovernanceEnvelope for merkle leaves
PipelineMerge, SchematicPublish, and GitOpsSync ceremony merkle
leaves are now the canonical_hash() of a GovernanceEnvelope,
binding git ref + governance metadata into a single auditable
32-byte hash.

Uses the resolution's resolved_at timestamp for deterministic
envelope construction.

Non-git ceremony types (MutationIntent, Custom) unchanged.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-12 12:13:53 -04:00
Tyler J King
3d5e5485ec refactor(ceremony-engine): bind git commit hash into canonical_bytes
PipelineMerge ceremony resolutions now include the git commit
SHA in their canonical form, binding the Quartermaster merkle
leaf to git's merkle tree. SchematicPublish includes tree_hash,
GitOpsSync includes target_revision.

Non-git ceremony types (MutationIntent, Custom) unchanged —
canonical_bytes still returns proof_hash alone.

See cid-reconciliation-audit.md Site 8.

Signed-off-by: Tyler King <tking@guildhouse.dev>
2026-04-12 07:54:01 -04:00
b1865a0627 initial: bascule v0.1.0
Bascule shell runtime workspace — governed shell access layer
for Substrate/Guildhouse FFC deployments.

Crates:
- bascule-agent: node agent with SSH server + command filtering
- bascule-core: audit, grant engine, ceremony types, session
- bascule-filter-core: log line filtering (stdio protocol)
- bascule-gateway: OIDC auth, session management, SAT validation
- bascule-node-agent: k8s DaemonSet agent (pod watcher, BPF manager)
- bascule-proto: protobuf definitions
- bascule-shell: governed SSH shell (commands, elevation, REPL)
- bascule-tail: chronicle log tail + fanout
- ceremony-engine: ceremony lifecycle (6 types + request/resolution)

172 tests passing.
Implements SBS-SPEC-0001 shell model.
Reference impl for SPEC-SHELLOPS-0001 Layer 1 (root shell).
2026-03-18 16:40:48 -04:00