Dmitry Beresnev commited on
Commit
c376717
·
1 Parent(s): 2a70ca5

add auth form, fix dockerfile and supervisord conf

Browse files
Files changed (3) hide show
  1. Dockerfile +6 -0
  2. app.py +71 -0
  3. supervisord.conf +1 -1
Dockerfile CHANGED
@@ -44,6 +44,9 @@ ENV OPENCLAW_CUSTOM_BASE_URL=https://researchengineering-agi.hf.space/v1
44
  ENV OPENCLAW_CUSTOM_MODEL_ID=deepseek-chat
45
  ENV OPENCLAW_CUSTOM_PROVIDER_ID=researchengineering-agi-hf-space
46
  ENV OPENCLAW_CUSTOM_COMPATIBILITY=anthropic
 
 
 
47
  ENV OPENCLAW_GATEWAY_BIND=lan
48
  ENV OPENCLAW_BOOTSTRAP_ONBOARD=1
49
  ENV OPENCLAW_CONTROL_UI_BASE_PATH=/openclaw
@@ -51,6 +54,9 @@ ENV OPENCLAW_ALLOWED_ORIGINS=https://researchengineering-agi-assistant.hf.space,
51
  ENV OPENCLAW_TRUSTED_PROXIES=127.0.0.1,::1
52
  ENV OPENCLAW_CONTROL_UI_ALLOW_INSECURE_AUTH=1
53
  ENV OPENCLAW_CONTROL_UI_DISABLE_DEVICE_AUTH=1
 
 
 
54
 
55
  RUN mkdir -p /app/vault /app/.openclaw/state
56
 
 
44
  ENV OPENCLAW_CUSTOM_MODEL_ID=deepseek-chat
45
  ENV OPENCLAW_CUSTOM_PROVIDER_ID=researchengineering-agi-hf-space
46
  ENV OPENCLAW_CUSTOM_COMPATIBILITY=anthropic
47
+ ENV OPENCLAW_CUSTOM_API_KEY=
48
+ ENV OPENCLAW_CUSTOM_API_KEY_OPTIONAL=1
49
+ ENV OPENCLAW_CUSTOM_API_KEY_PLACEHOLDER=no-key
50
  ENV OPENCLAW_GATEWAY_BIND=lan
51
  ENV OPENCLAW_BOOTSTRAP_ONBOARD=1
52
  ENV OPENCLAW_CONTROL_UI_BASE_PATH=/openclaw
 
54
  ENV OPENCLAW_TRUSTED_PROXIES=127.0.0.1,::1
55
  ENV OPENCLAW_CONTROL_UI_ALLOW_INSECURE_AUTH=1
56
  ENV OPENCLAW_CONTROL_UI_DISABLE_DEVICE_AUTH=1
57
+ ENV STREAMLIT_AUTH_ENABLED=1
58
+ ENV STREAMLIT_AUTH_USERNAME=
59
+ ENV STREAMLIT_AUTH_PASSWORD=
60
 
61
  RUN mkdir -p /app/vault /app/.openclaw/state
62
 
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import json
 
2
  import os
3
  import re
4
  import shutil
@@ -46,6 +47,9 @@ STREAMLIT_ERR_LOG_PATH = Path("/tmp/streamlit.err.log")
46
  STREAMLIT_LOG_PATH = Path("/tmp/streamlit.log")
47
  CADDY_ERR_LOG_PATH = Path("/tmp/caddy.err.log")
48
  CADDY_LOG_PATH = Path("/tmp/caddy.log")
 
 
 
49
 
50
 
51
  def resolve_openclaw_bin() -> str | None:
@@ -516,10 +520,77 @@ def render_backtest_visuals(result, data: pd.DataFrame, params: dict) -> None:
516
  st.dataframe(result.trades, use_container_width=True)
517
 
518
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
519
  st.set_page_config(page_title="OpenClaw Control Center", layout="wide")
 
 
 
 
 
 
 
 
 
 
 
 
 
520
  st.title("OpenClaw Control Center")
521
  st.caption("Manage gateway runtime, config, environment, and test calls from one UI.")
522
 
 
 
 
 
 
 
 
523
  init_state()
524
  pull_logs()
525
 
 
1
  import json
2
+ import hmac
3
  import os
4
  import re
5
  import shutil
 
47
  STREAMLIT_LOG_PATH = Path("/tmp/streamlit.log")
48
  CADDY_ERR_LOG_PATH = Path("/tmp/caddy.err.log")
49
  CADDY_LOG_PATH = Path("/tmp/caddy.log")
50
+ STREAMLIT_AUTH_ENABLED = os.getenv("STREAMLIT_AUTH_ENABLED", "1") == "1"
51
+ STREAMLIT_AUTH_USERNAME = os.getenv("STREAMLIT_AUTH_USERNAME", "").strip()
52
+ STREAMLIT_AUTH_PASSWORD = os.getenv("STREAMLIT_AUTH_PASSWORD", "").strip()
53
 
54
 
55
  def resolve_openclaw_bin() -> str | None:
 
520
  st.dataframe(result.trades, use_container_width=True)
521
 
522
 
523
+ def login_enabled() -> bool:
524
+ return STREAMLIT_AUTH_ENABLED
525
+
526
+
527
+ def has_login_credentials() -> bool:
528
+ return bool(STREAMLIT_AUTH_USERNAME and STREAMLIT_AUTH_PASSWORD)
529
+
530
+
531
+ def validate_login(username: str, password: str) -> bool:
532
+ return hmac.compare_digest(username, STREAMLIT_AUTH_USERNAME) and hmac.compare_digest(
533
+ password, STREAMLIT_AUTH_PASSWORD
534
+ )
535
+
536
+
537
+ def render_login_form() -> None:
538
+ st.markdown(
539
+ """
540
+ <style>
541
+ .auth-wrap {display:flex; justify-content:center; margin-top:4vh;}
542
+ .auth-card {
543
+ width: 420px; max-width: 92vw; padding: 1.1rem 1.2rem 1rem 1.2rem;
544
+ border-radius: 14px; border: 1px solid #d8dee9; background: #ffffff;
545
+ box-shadow: 0 8px 24px rgba(16, 24, 40, 0.08);
546
+ }
547
+ .auth-title {font-size:1.2rem; font-weight:700; color:#0f172a; margin-bottom:.25rem;}
548
+ .auth-sub {font-size:.92rem; color:#475467; margin-bottom:.8rem;}
549
+ </style>
550
+ <div class="auth-wrap">
551
+ <div class="auth-card">
552
+ <div class="auth-title">OpenClaw Control Login</div>
553
+ <div class="auth-sub">Sign in to access the control center.</div>
554
+ </div>
555
+ </div>
556
+ """,
557
+ unsafe_allow_html=True,
558
+ )
559
+ with st.form("streamlit_auth_form", clear_on_submit=False):
560
+ username = st.text_input("Username")
561
+ password = st.text_input("Password", type="password")
562
+ submit = st.form_submit_button("Sign in", use_container_width=True)
563
+ if submit:
564
+ if validate_login(username.strip(), password):
565
+ st.session_state["authenticated"] = True
566
+ st.rerun()
567
+ st.error("Invalid username or password.")
568
+
569
+
570
  st.set_page_config(page_title="OpenClaw Control Center", layout="wide")
571
+ st.session_state.setdefault("authenticated", False)
572
+
573
+ if login_enabled():
574
+ if not has_login_credentials():
575
+ st.error(
576
+ "UI auth is enabled, but credentials are missing. "
577
+ "Set `STREAMLIT_AUTH_USERNAME` and `STREAMLIT_AUTH_PASSWORD` in Hugging Face Secrets."
578
+ )
579
+ st.stop()
580
+ if not st.session_state.get("authenticated", False):
581
+ render_login_form()
582
+ st.stop()
583
+
584
  st.title("OpenClaw Control Center")
585
  st.caption("Manage gateway runtime, config, environment, and test calls from one UI.")
586
 
587
+ if login_enabled():
588
+ with st.sidebar:
589
+ st.success("Authenticated")
590
+ if st.button("Sign out", use_container_width=True):
591
+ st.session_state["authenticated"] = False
592
+ st.rerun()
593
+
594
  init_state()
595
  pull_logs()
596
 
supervisord.conf CHANGED
@@ -5,7 +5,7 @@ pidfile=/tmp/supervisord.pid
5
  user=root
6
 
7
  [program:openclaw]
8
- command=/bin/sh -lc "GATEWAY_BIND=${OPENCLAW_GATEWAY_BIND:-lan}; AUTH_FILE_MAIN=/app/.openclaw/state/agents/main/agent/auth-profiles.json; ONBOARD_MARKER=/app/.openclaw/state/.onboard_custom_done; if [ \"${OPENCLAW_BOOTSTRAP_ONBOARD:-1}\" = \"1\" ] && [ ! -s \"$AUTH_FILE_MAIN\" ] && [ ! -f \"$ONBOARD_MARKER\" ]; then echo '[onboard] bootstrapping custom provider'; openclaw onboard --non-interactive --mode local --auth-choice custom-api-key --custom-base-url \"${OPENCLAW_CUSTOM_BASE_URL}\" --custom-model-id \"${OPENCLAW_CUSTOM_MODEL_ID}\" --custom-provider-id \"${OPENCLAW_CUSTOM_PROVIDER_ID:-researchengineering-agi-hf-space}\" --custom-compatibility \"${OPENCLAW_CUSTOM_COMPATIBILITY:-anthropic}\" --gateway-port 18789 --gateway-bind \"$GATEWAY_BIND\" --skip-skills --accept-risk --secret-input-mode plaintext || true; touch \"$ONBOARD_MARKER\"; fi; python /app/scripts/bootstrap_gateway_token.py; exec openclaw gateway run --port 18789 --bind \"$GATEWAY_BIND\" --allow-unconfigured --dev"
9
  autorestart=true
10
  startsecs=5
11
  startretries=20
 
5
  user=root
6
 
7
  [program:openclaw]
8
+ command=/bin/sh -lc "GATEWAY_BIND=${OPENCLAW_GATEWAY_BIND:-lan}; AUTH_FILE_MAIN=/app/.openclaw/state/agents/main/agent/auth-profiles.json; API_KEY_RAW=${OPENCLAW_CUSTOM_API_KEY:-${CUSTOM_API_KEY:-}}; API_KEY=\"$API_KEY_RAW\"; if [ -z \"$API_KEY\" ] && [ \"${OPENCLAW_CUSTOM_API_KEY_OPTIONAL:-1}\" = \"1\" ]; then API_KEY=\"${OPENCLAW_CUSTOM_API_KEY_PLACEHOLDER:-no-key}\"; echo '[onboard] using placeholder custom API key'; fi; if [ \"${OPENCLAW_BOOTSTRAP_ONBOARD:-1}\" = \"1\" ] && [ ! -s \"$AUTH_FILE_MAIN\" ]; then if [ -n \"$API_KEY\" ]; then echo '[onboard] bootstrapping custom provider with API key'; openclaw onboard --non-interactive --mode local --auth-choice custom-api-key --custom-base-url \"${OPENCLAW_CUSTOM_BASE_URL}\" --custom-model-id \"${OPENCLAW_CUSTOM_MODEL_ID}\" --custom-provider-id \"${OPENCLAW_CUSTOM_PROVIDER_ID:-researchengineering-agi-hf-space}\" --custom-compatibility \"${OPENCLAW_CUSTOM_COMPATIBILITY:-anthropic}\" --custom-api-key \"$API_KEY\" --secret-input-mode plaintext --gateway-port 18789 --gateway-bind \"$GATEWAY_BIND\" --skip-skills --accept-risk || true; else echo '[onboard] skipped: no API key or placeholder'; fi; fi; python /app/scripts/bootstrap_gateway_token.py; exec openclaw gateway run --port 18789 --bind \"$GATEWAY_BIND\" --allow-unconfigured --dev"
9
  autorestart=true
10
  startsecs=5
11
  startretries=20