Status + answers to your three questions

Status + answers to your three questions

1. Is the app healthy now?

No — the session-restore issue is still reproducing 100% of the time. We just re-tested live (fresh Incognito window, login with real credentials) and got the same result: the server mints a valid session row on login (verified via direct read-only query against our production Postgres — opaque random token, correct role, +24h expiry), but on page reload the user is bounced back to the login screen every time. The server-side write path works; the read-back on reload does not.

2. Python 3.14 vs runtime.txt

Confirmed on our side: runtime.txt contains exactly python-3.10 (single line, no whitespace, unchanged in git for ~3 months). Our local environment and our whole dependency set are built and tested against CPython 3.10.

Our deploy log reports Using Python 3.14.7 environment at /home/adminuser/venv — so after the post-apt-get-incident rebuild, runtime.txt appears not to be honored; the container is on 3.14.7, not 3.10.

Two questions:

  • Is python-3.10 still a supported value for runtime.txt on Community Cloud, or has the supported range been narrowed?
  • When runtime.txt specifies a version you no longer support, does the platform now silently fall back to the current default (3.14.x) instead of failing the build?

This is material to the cookie problem: our entire wheel set (including tornado’s C extensions and anything touching http.cookies / asyncio) was resolved and tested on 3.10, and the container is now on 3.14 — a far larger environment drift than the one dependency we pinned.

3. The _ar_session cookie mechanism (“does the hashing happen in the browser?”)

No hashing happens in the browser. The browser only ever holds a raw, opaque random token (secrets.token_urlsafe(32), 43 chars). The SHA-256 hashing is done server-side, in Python, on read: we hash the incoming token and look up sha256(token) in a sessions table. The raw token is never stored server-side; the browser never sees a hash.

How the cookie is written (this is the key detail):

  • It is set client-side, in JavaScript — document.cookie = '_ar_session=' + encodeURIComponent(token) + '; path=/; SameSite=Lax; max-age=86400' — executed inside a st.components.v1.html(...) component, i.e. from within its iframe writing to window.parent.document.cookie.
  • It is not set via an HTTP Set-Cookie response header from the server.
  • Confirmed in browser DevTools: after login the _ar_session cookie is present on the app’s origin, with the expected Domain, Path=/, SameSite=Lax and expiry.

How it is read back:

  • Server-side, via st.context.cookies.get("_ar_session").
  • Per Streamlit 1.56.0’s own source (web/server/browser_websocket_handler.py → TornadoClientContext), st.context.cookies is populated from tornado_request.cookies of the WebSocket upgrade handshake (GET /_stcore/stream, Upgrade: websocket). The docstring: “Captures headers, cookies, and client info from the initial WebSocket handshake.”
  • Observed failure: on reload, st.context.cookies returns no _ar_session, even though the cookie is in the browser and — being SameSite=Lax on the same origin — should be included on the same-origin WebSocket upgrade request.

Additional finding since our last check: we pinned tornado==6.4.2 in requirements.txt (Streamlit’s own metadata excludes tornado==6.5.0 explicitly, suggesting a known WS/cookie regression in that release line). Confirmed installed in the rebuilt container’s deploy log. The symptom is unchanged — 100% reproduction rate, identical behavior. This rules out our own dependency pin as the cause.

We also run an independent external WebSocket client (a GitHub Actions job with zero cookies, zero app logic — a bare websockets.connect() to wss://archirapid.streamlit.app/~/+/_stcore/stream every hour) that has been getting HTTP 502 on that same endpoint intermittently since the rebuild, with occasional green runs (partial recovery, not resolved). This points at the edge/WebSocket layer itself, not at our code or dependencies.

Explicit question: Does the cookie-stripping you suspect apply specifically to the Cookie: header of the WebSocket upgrade request (/_stcore/stream), or to any HTTP request as well? If it is the WS upgrade specifically, that would mean st.context.cookies currently cannot see any app-set cookie on Community Cloud in the platform’s present state.

Hey there, thanks for the detailed status update and for testing so thoroughly! Let’s break down your questions and findings:

1. Is the app healthy now?
No, the session-restore issue persists. Your server-side session write is working, but on reload, the cookie isn’t being read back by Streamlit (via st.context.cookies). This matches known issues where cookies set via JavaScript (not via HTTP Set-Cookie) may not be visible to the Streamlit backend, especially on Community Cloud, due to how the platform proxies and handles WebSocket upgrade requests. There are multiple community reports confirming that cookies set client-side (via JS) are not reliably sent on the WebSocket handshake, so st.context.cookies often returns empty or incomplete results in this scenario. This is a platform limitation, not a bug in your code or dependency set (discussed here, here, and here).

2. Python 3.14 vs runtime.txt
Community Cloud currently supports Python 3.10, 3.11, 3.12, 3.13, and 3.14 (see docs). However, if you specify a version in runtime.txt that is not supported or if the platform changes its default, it may silently fall back to the latest supported version (e.g., 3.14.x) instead of failing the build. This can cause environment drift, especially if your dependencies were built for 3.10. This fallback behavior is not always clearly documented, but it is observed in practice (see docs).

3. _ar_session cookie mechanism
You’re correct: setting cookies via JS (document.cookie) inside a Streamlit component iframe does not guarantee that the cookie will be sent on the WebSocket handshake (/_stcore/stream). Streamlit’s backend (st.context.cookies) only sees cookies present in the initial WebSocket upgrade request, and platform-level proxies or security layers may strip or block these cookies, especially if they are not set via HTTP Set-Cookie headers. This is a known limitation on Community Cloud (see discussion, here, and here). The stripping appears to apply specifically to the WebSocket upgrade request, not to all HTTP requests, but since Streamlit’s session context is tied to the WebSocket, this effectively blocks your use case.

Summary:

  • Cookies set via JS are not reliably visible to Streamlit’s backend on Community Cloud due to platform-level handling of WebSocket upgrade requests.
  • Python version drift can occur if runtime.txt is not honored; Community Cloud may silently use a newer Python version.
  • The cookie-stripping issue is specific to the WebSocket upgrade request, which is what st.context.cookies relies on.

Workarounds:

  • Set cookies via HTTP Set-Cookie headers from the backend if possible (not always feasible in Streamlit).
  • Consider using Streamlit’s built-in authentication or session state mechanisms.
  • Monitor this feature request for native, robust cookie support.

Sources: