feat(hh): /pw command, RAM-only direnv autostart, robust lets-hack; coven→clergy

- add /pw (alias /password): reveal this room's password locally (never
  broadcast); surfaced in the F1 help overlay and the join hint
- direnv-autostart/: cd-to-launch a single real-user session via direnv;
  password is minted in memory at launch (never written to disk, matching the
  RAM-only model) and scoped to the child process. setup.sh installs direnv,
  hooks bash/zsh, and `direnv allow`s the dir
- lets-hack.sh: boot a FRESH server by default (replacing any live one) with a
  --reuse opt-out; add -h/--help/-help; guard against killing the tmux session
  you're attached to; switch-client into the coven when run inside tmux
- rename coven→clergy across rust/python/scripts; tests/test_coven.py→test_clergy.py
- snapshots in-progress hack-house client work (sandbox, themes, net, ui)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-05-31 22:29:17 -07:00
parent 8e6365a649
commit 5de493e895
21 changed files with 1058 additions and 91 deletions
+24
View File
@@ -24,6 +24,21 @@ pub struct Session {
pub insecure: bool,
}
/// The credentials needed to (re)authenticate a Session — kept so the UI can
/// re-run the SRP handshake and rejoin after a disconnect (AFK / server blip).
#[derive(Clone)]
pub struct ConnParams {
pub ip: String,
pub port: u16,
pub user: String,
pub password: String,
pub no_tls: bool,
pub insecure: bool,
}
/// The write half of a split websocket; outgoing frames are sent here.
pub type WsSink = futures_util::stream::SplitSink<Ws, WsMsg>;
/// Full SRP handshake against the Sanic server. Returns a ready Session
/// (room key derived, ws url built) but does not open the websocket.
pub fn authenticate(
@@ -99,6 +114,15 @@ pub async fn connect(session: &Session) -> Result<Ws> {
Ok(ws)
}
/// Open the websocket for a session, spawn the reader task feeding `tx`, and
/// hand back the write half. Used for the initial connect and every reconnect.
pub async fn open(session: &Session, tx: UnboundedSender<Net>) -> Result<WsSink> {
let ws = connect(session).await?;
let (write, read) = ws.split();
tokio::spawn(reader(read, session.room.clone(), tx));
Ok(write)
}
fn parse_users(v: &Value) -> Vec<User> {
v.as_array()
.into_iter()