Files
clawmates/crates/cm-auth/tests/bootstrap.rs
T
Omar SobhandClaude Fable 5 447f7039d8 Compose: production README, env knobs, first-owner bootstrap — deployed & live
Made the Docker Compose route turn-key for a real self-host, then stood
the whole stack up and drove a live chat through it.

- First-owner bootstrap (cm-auth::bootstrap_owner): a fresh local-auth
  install has no users and no signup route, so the initial Owner +
  workspace are provisioned ONCE from CLAWMATES_BOOTSTRAP_* env on first
  boot — idempotent, never clobbers an existing install (keys on 'any
  workspace exists'). Two real-Postgres tests (creates + signs in;
  second call is a no-op). Wired into server boot, guarded on a
  non-empty password
- deploy/compose/README.md: full production bring-up — services, the
  security topology, every config knob, Anthropic vs local-LLM, the
  broker-key backup, ops, and TLS/SSE proxy notes
- .env.example fleshed out (bootstrap, LLM, auth mode, OTLP); compose
  uses optional env_file so only the knobs you set are injected (unset
  options never override clawmates.toml with empty strings)
- volume-init one-shot chowns the broker's named volumes so the non-root
  scratch broker can write its socket + generated master key

Deployed locally and verified end to end: all 5 containers healthy,
broker generated its key, server bootstrapped owner@…, login + /api/user/me
work, and a real message streamed a live Anthropic response through the
gateway. Captured screenshots of login, workspace home, chat, and the
Computer panel.

166 Rust tests (+2 bootstrap).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-10 14:06:15 -05:00

71 lines
2.3 KiB
Rust

//! Production first-owner bootstrap: a fresh local-auth deployment has no
//! users and no signup route, so the very first owner is provisioned once
//! from configuration. Idempotent — never clobbers an existing install.
use cm_auth::{bootstrap_owner, AuthService, AuthedUser};
use cm_domain::Role;
#[tokio::test]
async fn bootstrap_creates_the_first_owner_and_workspace_once() {
let pool = cm_testkit::test_pool().await;
// First boot: provisions the workspace + owner with a usable password.
let created = bootstrap_owner(&pool, "Acme", "[email protected]", "s3cret-pw", 1250)
.await
.unwrap();
assert!(created, "first call provisions");
let auth = AuthService::new(pool.clone());
let token = auth
.login_local("[email protected]", "s3cret-pw")
.await
.expect("the bootstrapped owner can sign in");
let AuthedUser { role, .. } = auth.authenticate(token.secret()).await.unwrap();
assert_eq!(role, Role::Owner);
// The starter credit grant landed.
let user = cm_db::repo::users::find_by_email(&pool, "[email protected]")
.await
.unwrap();
assert_eq!(
cm_db::repo::credits::balance(&pool, user.workspace_id)
.await
.unwrap(),
1250
);
}
#[tokio::test]
async fn bootstrap_is_idempotent_and_never_clobbers_an_existing_install() {
let pool = cm_testkit::test_pool().await;
assert!(
bootstrap_owner(&pool, "Acme", "[email protected]", "pw-one", 100)
.await
.unwrap()
);
// Re-running (e.g. every container restart) is a no-op: no second
// workspace, no password reset, no duplicate owner.
let created = bootstrap_owner(&pool, "Acme", "[email protected]", "pw-two", 100)
.await
.unwrap();
assert!(!created, "second call is a no-op");
let auth = AuthService::new(pool.clone());
assert!(
auth.login_local("[email protected]", "pw-two")
.await
.is_err(),
"a second owner is never created"
);
assert!(
auth.login_local("[email protected]", "pw-one").await.is_ok(),
"the original owner is untouched"
);
let workspaces: i64 = sqlx::query_scalar("SELECT count(*) FROM workspaces")
.fetch_one(&pool)
.await
.unwrap();
assert_eq!(workspaces, 1);
}