You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Introduce a bind-aware authentication posture in WebSocketGateway and the Chainlit UI: permissive on loopback (127.0.0.1 / localhost / ::1) so non-developer onboarding keeps its one-command quickstart, strict on any external interface so a LAN/VPS deploy can't ship with admin/admin or a missing token.
This is the foundation for issues B (magic-link), C (Origin/CSRF), D (UI pairing banner), E (owner-DM pairing buttons). Ship this first; the others build on AuthMode and the bind_host signal.
Add bind_host: str = "127.0.0.1" field to GatewayConfig
+2
Net: +267 / −129 ≈ +138 LOC across 8 touched files. Well within "minimal and focused".
Technical Considerations
No new deps. All stdlib (ipaddress for safe loopback check).
Import-time budget:protocols.py contains only Literal + two pure functions → 0 ms added.
Backward compat:bind_host defaults to 127.0.0.1 (matches current default) → no behaviour change for local users.
Safe default: external bind without token → hard fail at startup with:
GatewayStartupError: Cannot bind to 0.0.0.0 without an auth token.
Fix: praisonai onboard (30 seconds, 3 prompts)
Or: export GATEWAY_AUTH_TOKEN=$(openssl rand -hex 16)
Escape hatch:PRAISONAI_ALLOW_DEFAULT_CREDS=1 — for lab/demo only; documented.
DRY win: 5× UI auth duplication collapses into one helper.
Acceptance Criteria
praisonaiagents.gateway.protocols.is_loopback("127.0.0.1") is True, same for localhost, ::1, 0:0:0:0:0:0:0:1; False for 0.0.0.0, 10.x, 192.168.x, public IPs.
# src/praisonai/tests/integration/gateway/test_bind_aware_e2e.pyimportos, subprocess, time, requestsfrompraisonaiagentsimportAgent# Loopback with no token — must workproc=subprocess.Popen(["praisonai", "gateway", "start", "--host", "127.0.0.1"])
time.sleep(3)
assertrequests.get("http://127.0.0.1:8765/info").status_codein (200, 401)
proc.terminate()
# Real agent still works (unrelated to gateway, sanity)a=Agent(instructions="Reply in exactly 3 words.")
r=a.start("Say hi")
print(r)
assertlen(r) >0
Implementation Notes
Key files to read first
src/praisonai/praisonai/gateway/server.py 191-325 — the init + _check_auth path you will wrap
src/praisonai/praisonai/ui/chat.py 197-401 — the auth bootstrap to consolidate
Overview
Introduce a bind-aware authentication posture in
WebSocketGatewayand the Chainlit UI: permissive on loopback (127.0.0.1/localhost/::1) so non-developer onboarding keeps its one-command quickstart, strict on any external interface so a LAN/VPS deploy can't ship withadmin/adminor a missing token.This is the foundation for issues B (magic-link), C (Origin/CSRF), D (UI pairing banner), E (owner-DM pairing buttons). Ship this first; the others build on
AuthModeand thebind_hostsignal.Parent tracking: #1502.
Background
Current state (evidence):
src/praisonai/praisonai/gateway/server.pyGATEWAY_AUTH_TOKENif missing, logs raw token to WARNINGsrc/praisonai/praisonai/gateway/server.pysrc/praisonai/praisonai/ui/chat.pyCHAINLIT_USERNAME/PASSWORDdefaults toadmin/admin— warn only, accepts requestPeer implementations of the same idea (proven in production):
~/hermes-agent/gateway/platforms/api_server.py:476-477— "If no API key is configured, all requests are allowed (only when API server is local)"~/openclaw/src/gateway/auth.ts:139-160—isLocalDirectRequest()helper drivingGatewayAuthSurfaceArchitecture
Single rule, enforced at two entry points:
Protocol-first (zero heavy imports in core):
Adapters / enforcement live in the wrapper (
src/praisonai/praisonai/gateway/auth.py, new, ~80 lines).Files to Create / Modify
New files
src/praisonai-agents/praisonaiagents/gateway/protocols.pyAuthModeliteral +is_loopback()+resolve_auth_mode()pure helperssrc/praisonai/praisonai/gateway/auth.pyassert_external_bind_safe(config)— raisesGatewayStartupErrorwith one-line fix messagesrc/praisonai/praisonai/ui/_auth.pyregister_password_auth(app, *, bind_host)— shared helper; enforces non-default creds on external bindsrc/praisonai/tests/unit/gateway/test_bind_aware_auth.pysrc/praisonai/tests/unit/ui/test_ui_bind_aware_creds.pyadmin/adminon0.0.0.0unless escape hatch setModified files
src/praisonai/praisonai/gateway/server.pystart(), callassert_external_bind_safe(self.config)before binding; log fingerprint not raw tokensrc/praisonai/praisonai/ui/{chat,bot,agents,realtime,code}.pypassword_auth_callbackbootstrap withregister_password_auth(app, bind_host=…)src/praisonai-agents/praisonaiagents/gateway/config.pybind_host: str = "127.0.0.1"field toGatewayConfigNet: +267 / −129 ≈ +138 LOC across 8 touched files. Well within "minimal and focused".
Technical Considerations
ipaddressfor safe loopback check).protocols.pycontains onlyLiteral+ two pure functions → 0 ms added.bind_hostdefaults to127.0.0.1(matches current default) → no behaviour change for local users.PRAISONAI_ALLOW_DEFAULT_CREDS=1— for lab/demo only; documented.Acceptance Criteria
praisonaiagents.gateway.protocols.is_loopback("127.0.0.1") is True, same forlocalhost,::1,0:0:0:0:0:0:0:1;Falsefor0.0.0.0,10.x,192.168.x, public IPs.resolve_auth_mode("127.0.0.1", None) == "local",resolve_auth_mode("0.0.0.0", None) == "token", explicit config wins.host="127.0.0.1"starts with noauth_token→ permissive mode, warn-level log.host="0.0.0.0"and no token → raisesGatewayStartupErrorwith the fix message.host="0.0.0.0"and token → starts normally.localhostwith unsetCHAINLIT_USERNAME/PASSWORD→admin/adminworks, WARN log.0.0.0.0withadmin/admin→ refuses to start unlessPRAISONAI_ALLOW_DEFAULT_CREDS=1.@cl.password_auth_callbackduplication (grep shows exactly 1 definition).GATEWAY_AUTH_TOKENlogged only as fingerprintgw_****XXXX(last 4 chars); raw token written to~/.praisonai/.env(mode 0600) only.python -c "import time; t=time.time(); import praisonaiagents; print(f'{(time.time()-t)*1000:.0f}ms')"< 200 ms.Real agentic test
Implementation Notes
Key files to read first
src/praisonai/praisonai/gateway/server.py191-325 — the init +_check_authpath you will wrapsrc/praisonai/praisonai/ui/chat.py197-401 — the auth bootstrap to consolidate~/hermes-agent/gateway/platforms/api_server.py454-488 — reference behaviourTesting commands
References
~/hermes-agent/gateway/platforms/api_server.py,~/openclaw/src/gateway/auth.tspraisonaiagents/, heavy impl inpraisonai/