fix(webui): persist chat history and WebUI transcripts across deploys#9
Merged
Conversation
Chat history did not fully survive redeploys: the sidebar listed past
conversations but opening one showed an empty thread. Root cause was
nanobot's data_dir resolving to /app (baked into the image, wiped every
deploy), so the WebUI display transcripts under data_dir/webui were lost
while session files on the persistent disk survived.
- entrypoint.sh: copy the selected config template onto the mounted disk
and run with --config on the disk, so data_dir resolves under the mount
(webui/, cron, media, logs now persist). Overwrite on every boot; secrets
stay as ${VAR} placeholders resolved in memory.
- transcript.py: build_webui_thread_response reconstructs the thread from
the durable session file when the display transcript is missing, instead
of 404ing. Recovers orphaned chats and self-heals future transcript loss.
- render.yaml: correct the misleading disk comment; stays Starter / 1 GB.
- README: HeyGen offload recipe for the hyperframes CLI App, and a note
that history persistence needs the disk, not a bigger plan.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eep them
mistune's HTMLRenderer treats any non-http(s) scheme as harmful and rewrites
it to "#harmful-link" before the nh3 cleaner runs. That stripped mxc:// image
sources and matrix: links even though MATRIX_HTML_CLEANER is explicitly built
to preserve them (via _filter_matrix_html_attribute and MATRIX_ALLOWED_URL_SCHEMES),
breaking test_send_keeps_only_mxc_image_sources.
Pass an HTMLRenderer with allow_harmful_protocols=("mxc:", "matrix:") so mistune
emits the URLs unchanged and nh3 remains the single security gate. Non-mxc image
srcs, javascript:, and data: URLs are still stripped by nh3 as before.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
After a redeploy, the WebUI sidebar listed past conversations, but opening one showed an empty thread ("What should we work on?") instead of the previous messages. The conversation content was never lost — it lives in the persisted session files — but the WebUI display transcript was gone.
Root cause: nanobot has two storage roots.
workspace(session files, sidebar index) is on the mounted disk and survives. Butdata_dirresolved to/app— the committed config baked into the image — which is ephemeral and rebuilt every deploy. The WebUI display transcripts live underdata_dir/webui, so they were wiped on each deploy → the thread endpoint 404'd → empty thread.Changes
entrypoint.sh— copy the selected config template (render-config.json, orrender-demo-config.jsonwhenDEMO) onto the mounted disk and run with--configpointing there, sodata_dir(=config_path.parent) resolves under the mount.webui/, cron, media, and logs now persist alongside session files. Copied as root before thesetprivdrop, then covered by the existingchown. Overwrite on every boot; secrets stay as${VAR}placeholders resolved in memory, so nothing secret is written to disk.nanobot/webui/transcript.py—build_webui_thread_responsenow reconstructs the thread from the durable session file when the display transcript is missing, instead of returningNone(→ 404). Recovers already-orphaned chats and self-heals any future transcript loss.render.yaml— correct the misleading disk comment. Stays Starter / 1 GB (the lean default; history persistence needs the disk, not a bigger plan).README.md— HeyGen offload recipe for the hyperframes CLI App (setHEYGEN_API_KEYin the dashboard rather than the interactivehyperframes auth login), plus a note that history persistence needs the disk, not a plan upgrade.Testing
tests/utils/test_webui_transcript.py: reconstruction from session messages (plain + paginated, matching the livelimit=160&direction=latestcall) and a guard that both-empty still returnsNone.pytest tests/cli_apps/ tests/webui/ tests/utils/test_webui_transcript.pygreen;ruff check nanobot/clean;shellcheck entrypoint.shclean.🤖 Generated with Claude Code