Render deploy template + opt-in public demo mode - #1
Merged
Conversation
Blueprint (render.yaml) provisioning the Odysseus web app plus bundled private SearXNG and ChromaDB services, with a slim hosted Dockerfile, generated admin password, secrets as sync:false, and same-origin CORS. - Dockerfile.render: slim image (drops GPU/local-model/host-Docker tooling) - searxng.Dockerfile + entrypoint: bake settings.yml (json format) + secret - entrypoint.render.sh: bind $PORT, run first-time setup - app.py: default ALLOWED_ORIGINS to RENDER_EXTERNAL_URL when unset - README: hermes-style Render template README + Deploy button - favicon: white Render logo as base favicon Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit 0bf3cc2af3d51cf90fa62d6e9a78c771f0008405)
…ration details Clarified the process for obtaining the automatically generated admin password during deployment and emphasized that it should be copied from the Render Dashboard. Added a section on configuring `ALLOWED_ORIGINS` for CORS, specifying when to set it for custom domains or additional origins. (cherry picked from commit 572f7385a92fe0a6b70c0a836c5586d45c5e7979)
setup.py catches its own admin-creation errors and exits 0, so a silent seeding failure would boot a healthy-looking service (the "/" health check still passes) that nobody can log into. Assert the auth invariant in the Render entrypoint after setup: when auth is enabled, an admin account must exist, otherwise exit non-zero so the deploy surfaces as failed. Reuses src.constants.AUTH_FILE so the path tracks ODYSSEUS_DATA_DIR like the app. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit 4d3bfb1c2cdffcf1a96ef4a9959d7324ff905b39)
Added a note specifying that a key with only the **Chat completions** permission is sufficient for the `OPENAI_API_KEY`, as embeddings run locally and no other OpenAI endpoints are utilized. This enhances the documentation for better understanding of API key requirements. (cherry picked from commit 457d6a572495faf1b87c78dcd30d7fd86c172b06)
The base SearXNG image treats /tmp as a volume, so the baked-in settings.yml.template was wiped at runtime and the entrypoint's sed failed with "No such file or directory", crashing the deploy. Stage the template in /usr/local/share instead, which persists in the image layer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit 9b89e9d772e25c21a550132da5629c8d85b9f6d7)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit dc0c150ce749a5c4834258124acccc9151341f49)
…sseus-dev#2) * feat(render): add one-click Render deploy template Blueprint (render.yaml) provisioning the Odysseus web app plus bundled private SearXNG and ChromaDB services, with a slim hosted Dockerfile, generated admin password, secrets as sync:false, and same-origin CORS. - Dockerfile.render: slim image (drops GPU/local-model/host-Docker tooling) - searxng.Dockerfile + entrypoint: bake settings.yml (json format) + secret - entrypoint.render.sh: bind $PORT, run first-time setup - app.py: default ALLOWED_ORIGINS to RENDER_EXTERNAL_URL when unset - README: hermes-style Render template README + Deploy button - favicon: white Render logo as base favicon Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(README): update admin password instructions and add CORS configuration details Clarified the process for obtaining the automatically generated admin password during deployment and emphasized that it should be copied from the Render Dashboard. Added a section on configuring `ALLOWED_ORIGINS` for CORS, specifying when to set it for custom domains or additional origins. * fix(render): fail hosted deploy when admin auth isn't seeded setup.py catches its own admin-creation errors and exits 0, so a silent seeding failure would boot a healthy-looking service (the "/" health check still passes) that nobody can log into. Assert the auth invariant in the Render entrypoint after setup: when auth is enabled, an admin account must exist, otherwise exit non-zero so the deploy surfaces as failed. Reuses src.constants.AUTH_FILE so the path tracks ODYSSEUS_DATA_DIR like the app. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(README): clarify OpenAI API key restrictions for chat completions Added a note specifying that a key with only the **Chat completions** permission is sufficient for the `OPENAI_API_KEY`, as embeddings run locally and no other OpenAI endpoints are utilized. This enhances the documentation for better understanding of API key requirements. * fix(render): stage SearXNG settings template outside /tmp The base SearXNG image treats /tmp as a volume, so the baked-in settings.yml.template was wiped at runtime and the entrypoint's sed failed with "No such file or directory", crashing the deploy. Stage the template in /usr/local/share instead, which persists in the image layer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(render): group services under an odysseus project Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(render): add OpenAI model seeding on first boot and update README Introduced a new script to seed an OpenAI model endpoint during the initial deployment, ensuring chat functionality works out of the box. Updated the README to include the new `OPENAI_DEFAULT_MODEL` variable and clarified the auto-configuration process for the OpenAI endpoint. Enhanced the render.yaml with demo mode settings for public showcase options. * feat: update OpenAI model references from gpt-4o to gpt-5.6-sol Replaced all instances of the model identifier `gpt-4o` with `gpt-5.6-sol` across various files, including configuration, documentation, and tests. This change ensures consistency in model usage and reflects the latest updates in the OpenAI model offerings. Updated the README to reflect the new default model and adjusted related code and tests accordingly. * feat(demo): add opt-in public, locked-down chat demo mode DEMO=true opens a public, no-signup chat surface on the deployer's OpenAI key; DEMO=false (default) leaves the full authenticated app unchanged so forks are unaffected. Backend choke points (no scattered `if DEMO`): - src/demo.py: flag, per-visitor cookie/owner, DEMO_PRIVILEGES, rate + per-session message caps, pinned model/endpoint/env-key config, output token clamp, startup log. - app.py: demo path in AuthMiddleware (per-visitor synthetic owner on the route whitelist only, after the is_configured check); startup mode log; boot purge of ephemeral demo sessions. - core/auth.py: get_privileges returns the locked profile for demo owners. - core/session_manager.py: skip _persist_message for demo owners (ephemeral history); pin model/endpoint/env-key on read. - routes/chat_routes.py: demo limit check before spend (friendly SSE, no 500), pre-context lockdown, token clamp, pinned config that bypasses the endpoint-row orphan checks. - src/task_scheduler.py, routes/assistant_routes.py: seeding guards. Frontend: inject a synchronous window.__ODYSSEUS_DEMO flag, make /api/auth/status demo-aware, suppress the 401->/login redirect in demo mode, and hide non-chat sidebar rails. Tests: tests/test_demo_mode.py (identity, whitelist, cookie minting, limit trips, pinned config, privileges, ephemeral persist skip). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: improve error logging and update README for demo mode Updated error handling in the startup event to log exceptions more clearly. Enhanced the README with additional notes on the limitations of demo mode, emphasizing that rate limits are not hard guarantees and should be managed through OpenAI project settings. Adjusted comments in the render.yaml to reflect these changes. Cleaned up code in various files for consistency and clarity. * feat: update demo model to gpt-5.6-luna and adjust related configurations Changed the default demo model from `gpt-4.1-nano` to `gpt-5.6-luna` across various files, including environment variables, model discovery, and session handling. Updated comments and documentation to reflect these changes, ensuring consistency in model usage and enhancing the demo experience for users. * refactor(demo): enhance error handling and logging for demo privileges Improved error handling in the AuthManager and SessionManager classes to log exceptions when demo privilege checks fail. Updated the demo session configuration to conditionally apply rate limiting based on the DEMO_MODE flag. Adjusted comments for clarity and consistency across the codebase, ensuring better maintainability and understanding of demo-related functionality. * feat(demo): implement trusted client IP handling for rate limiting Enhanced the demo mode by implementing a trusted client IP mechanism for rate limiting, ensuring that rate limits are keyed on the real client IP rather than the potentially spoofable leftmost entry in the X-Forwarded-For header. This change improves security and prevents users from bypassing rate limits by rotating IPs. Updated related documentation in the README and configuration files to reflect these changes, including new environment variables for daily message ceilings per IP. Added tests to verify the correct behavior of the new rate limiting logic. * refactor(demo): streamline demo privilege handling and request validation Refactored the demo privilege management by deriving fallback privileges from DEFAULT_PRIVILEGES, ensuring new keys default to off. Introduced a centralized function, is_demo_request, to validate demo requests consistently across routes. This change enhances code maintainability and clarity while improving the handling of demo session configurations. * fix(render): update health check path in configuration Changed the health check path in render.yaml from '/' to '/api/health' to ensure proper monitoring of the service's health status. This update aligns with the new API structure and enhances the reliability of health checks. * fix(demo): honor TRUSTED_PROXY_HOPS=0 for directly-exposed deploys _trusted_proxy_hops() forced hops >= 1, so a proxy-less fork (no edge appending X-Forwarded-For) still trusted the wholly attacker-supplied XFF as the rate-limit / IP-daily-cap key — a full bypass. Follow the conventional trusted-hop semantics (Werkzeug ProxyFix, uvicorn --forwarded-allow-ips): 0 = no trusted proxy, ignore XFF and key on the real TCP peer. Unset/invalid/negative still fall back to 1 (Render default unchanged). Docstrings + README updated; regression tests added. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(demo): polish logging, typing, and entrypoint robustness - Log (not silently pass) on demo status resolution and demo session-config pin failures, matching sibling except-branches. - Rate-limit the src.demo import-failure warning in get_privileges (hot path) to one-shot so a broken import can't spam the log. - DEMO_PRIVILEGES: Dict[str, object] -> Dict[str, Any] for consistency. - entrypoint.render.sh: set -e -> set -eu (all expansions already defaulted). - searxng-render-entrypoint.sh: substitute the secret via python/env instead of sed string interpolation so a sed metachar can't corrupt settings.yml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(auth): enhance demo privilege import logging and session handling - Implemented a locking mechanism to prevent spamming of demo privilege import failure logs in concurrent scenarios. - Updated the auth_status route to ensure a stable demo owner cookie is set on the response, improving the user experience for demo users. - Refined comments in the demo limits function to clarify the order of rate limit checks and their implications on user experience. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit ebac06cd57b92749398eb35d586dd2e5b43a01c8)
Browsers auto-request the domain-root /favicon.ico for tabs and bookmarks, independent of the page's <link rel="icon">. That path was hitting the auth gate and 302-ing to /login, so browsers received HTML instead of an icon and fell back to a stale cached favicon. Add a root /favicon.ico route serving the static icon and exempt it from AuthMiddleware. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit 79e3a96fdfc1be8f3dfbb60cd463274f697b0928)
Clarified the process for setting the `OPENAI_API_KEY` in the environment variables before the first boot to ensure chat functionality works immediately. This change enhances the user experience by providing clearer guidance on the necessary steps for deploying the application successfully. (cherry picked from commit 98bd0257069ce67de5db5bc4870aa21456b9cd22)
…criptions Added new environment variables for OpenAI model seeding, cloud web-search providers, trusted proxy hops, and demo mode configurations in the .env.example file. This update enhances clarity and provides guidance for users on configuring their environment for optimal application performance. (cherry picked from commit 90c4391f320a9f63150e0a26844cd44de7e7cff7)
Changed the value of the `generation` key under `previews` in render.yaml from off to "off" to ensure proper parsing and compatibility with the configuration format. This minor adjustment enhances clarity and consistency in the configuration file. (cherry picked from commit 42539b29a936c336861528e4fdddfa05f40585d2)
(cherry picked from commit 1f6b4f21ce2af6fcd3c2ebe4ffddf2c3964d64cf)
(cherry picked from commit db5432b6e390abc48b1108437e79635945728880)
Removed image tag from README and added a link. (cherry picked from commit 1459eb1d21a9cc534cc3e72cf4fb1afb16664dd0)
Expanded the README to include a new architecture section detailing the service layout and interactions. Added a diagram illustrating the public and private services, enhancing clarity on how the application components communicate over Render's private network. (cherry picked from commit 7d8fbd4f84bdd785b14712ad5643254b01576def)
(cherry picked from commit 9eec59ff68b102b323e6525ddf565df1809d20ee)
…dev#4) * chore(deps): move test tooling out of the production image pytest, pytest-asyncio, and httpx2 were in requirements.txt, so the hosted Render image (Dockerfile.render installs requirements.txt) shipped test-only tooling even though tests/ is excluded from the build context — pure bloat. Move them to a new requirements-dev.txt and install it where tests actually run (CI + the CONTRIBUTING manual-dev path). Production requirements.txt and both Dockerfiles now install runtime deps only; pins are unchanged and the full suite still collects (4655 tests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: run tests on Python 3.14 to match the production runtime CI pinned Python 3.11, but the pinned deps target 3.14 (both Dockerfiles use python:3.14-slim) — numpy==2.5.1 requires >=3.12, so `pip install -r requirements.txt` failed at install time on 3.11 and the pytest job never ran. Bump both the syntax and test jobs to 3.14 so CI exercises the versions that actually ship. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(README): reference the orphaned workspace screenshot docs/odysseus-browser.jpg was committed as a README screenshot ("Refresh README screenshot") but never wired into any doc, so test_no_orphan_images_in_docs flagged it as an orphan. Add it to the README as a product screenshot below the demo clip — fulfilling its original intent instead of deleting it — which greens the docs-hygiene test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> (cherry picked from commit 6696373557296370fb5bce91c1118ef9313d7adb)
The Render template port replaced gpt-4o entries across the model catalog instead of adding the newer tier alongside them, which silently regressed three things for anyone still on gpt-4o: it stopped being detected as vision-capable (src/chat_helpers.py), lost its context window so prompts truncated on the wrong boundary (src/model_context.py), and lost its pricing so the cost readout went blank (static/js/chatRenderer.js). The curated picker list, discovery list, VL auto-detect candidates, and the gpt-4o-mini-tts option had shrunk the same way. Every one of those edits is now additive — the new IDs are added, the existing entries stay. src.demo was imported three inconsistent ways: unconditionally at module top in app.py and chat_routes, lazily-with-fallback in core/auth.py and core/session_manager.py, and lazily-unguarded in assistant_routes and task_scheduler. The module-top imports meant the fallbacks could never fire in a running app — src.demo failing to import takes the boot down first — so they were dead code carrying real complexity, including two double-checked one-shot log guards. src.demo is a leaf module (stdlib plus src.rate_limiter), so there was never a cycle to dodge. Every caller now imports it at module top, and the fallbacks are gone. Callers reference it as a module rather than importing names, so values like DEMO_MODE, DEMO_MODEL, and DEMO_PRIVILEGES are read through it: a `from src.demo import DEMO_MODE` binds the value and goes stale whenever the module is reloaded, which the test suite does to toggle the flag. Also: serve_html_with_nonce matched a literal "<head>", so a template with attributes on the tag would fall through to prepending the demo flag ahead of <html> — after the fetch wrapper it exists to gate. It now matches the tag with a regex and 500s if there is no <head> at all, rather than serving a demo page that bounces visitors to /login. And the README deploy button still pointed at render-examples/odysseus-render, the mis-forked repo this one replaces. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The following required sections are missing or incomplete. Please update the PR description to address them:
This comment is deleted automatically once all sections are complete. |
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.
Adds one-click Render deployment to Odysseus, plus a demo mode that makes a public instance safe to leave running.
Deploy template
render.yamlprovisions the web service, a SearXNG search service, Postgres, and Key Value under one project.Dockerfile.renderandsearxng.Dockerfilebuild the two images; entrypoints indocker/seed admin auth, generate the SearXNG secret on first boot, and stage settings outside/tmp.docker/seed_openai_endpoint.pyregisters the OpenAI endpoint at startup so chat works on first load.Demo mode
src/demo.pygates behavior behind aDEMOflag, off by default:src/rate_limiter.pycap spend on the shared OpenAI key.Other
.env.exampledocuments the new variables.requirements-dev.txtand out of the production image.