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
+1 -1
View File
@@ -24,7 +24,7 @@ def create_app(password: str = "", name: str = "cmd-chat-server") -> Sanic:
app.ctx.ws_secret = os.urandom(32)
app.ctx.admin_token = secrets.token_hex(16)
app.ctx.rate_limiter = RateLimiter(max_requests=10, window_seconds=60)
# Coven capacity. 4 by default; raise via CMD_CHAT_MAX_USERS — infra-for-more,
# Clergy capacity. 4 by default; raise via CMD_CHAT_MAX_USERS — infra-for-more,
# the cap is data not architecture (broadcast fan-out is O(N)).
app.ctx.max_users = int(os.environ.get("CMD_CHAT_MAX_USERS", "4"))
app.ctx.cleanup_task = None
+4 -4
View File
@@ -16,7 +16,7 @@ def generate_ws_token(user_id: str, secret: bytes) -> str:
def _roster_frame(app: Sanic) -> str:
"""Authoritative presence snapshot — all coven members converge on this."""
"""Authoritative presence snapshot — all clergy members converge on this."""
users = app.ctx.session_store.get_all()
return json.dumps(
{
@@ -46,7 +46,7 @@ async def srp_init(request: Request, app: Sanic) -> HTTPResponse:
return response.json({"error": "Username taken"}, status=409)
if app.ctx.session_store.count() >= app.ctx.max_users:
return response.json({"error": "Coven full"}, status=409)
return response.json({"error": "Clergy full"}, status=409)
user_id, B, salt = app.ctx.srp_manager.init_auth(username, client_public)
@@ -82,7 +82,7 @@ async def srp_verify(request: Request, app: Sanic) -> HTTPResponse:
# Authoritative capacity gate — the slot is only consumed once a session
# is actually added here (init is best-effort / racy).
if app.ctx.session_store.count() >= app.ctx.max_users:
return response.json({"error": "Coven full"}, status=409)
return response.json({"error": "Clergy full"}, status=409)
H_AMK, session_key = app.ctx.srp_manager.verify_auth(user_id, client_proof)
@@ -173,7 +173,7 @@ async def chat_ws(request: Request, ws: Websocket, app: Sanic) -> None:
pass
finally:
await manager.disconnect(user_id)
# Free the slot + username so the coven can be rejoined (was previously
# Free the slot + username so the clergy can be rejoined (was previously
# held until the 1h stale sweep, which also blocked the name).
app.ctx.session_store.remove(user_id)
await manager.broadcast(