Version 2.0.0.6 Released
Version 2.0.0.6 Released
Date: 27th Apr 2026
Critical Security Update: APP_KEY / API_KEY_PEPPER Hardening, Encrypted Credentials at Rest, Reverse-Proxy Trust Gate, Docker Permission Fix & Broker Updates
This is a critical security update covering 10 commits since v2.0.0.5. It closes the highest-impact remote attack surface in OpenAlgo's web tier (forge-able session cookies signed with the publicly-known sample APP_KEY), encrypts every remaining plaintext credential in the database, gates client-supplied forwarded-IP headers behind an explicit TRUST_PROXY_HEADERS flag (so attackers can no longer spoof source IP to dodge IP bans / rate limits), invalidates all sessions on password change, fixes the Docker .env permission bug behind issue #960, and patches a transitive postcss CVE pulled in by the React build.
Every running deployment should pull and apply this update. Most installs only need the standard four-step upgrade (stop → pull → migrate → restart) — the code changes are backward-compatible and your data, authenticator codes, broker session, and external API keys are all preserved. See the Required Upgrade Steps at the bottom.
Highlights
- Per-instance APP_KEY auto-rotation —
.sample.envno longer ships real-looking hex values forAPP_KEY/API_KEY_PEPPER. Instead it carries placeholder strings; the app detects them on first run and replaces them in.envwith cryptographically random values viasecrets.token_hex(32). This closes the entire class of "forge a session cookie offline using the public sample key" attacks against any deployment that previously copied.sample.envto.envwithout manually rotating. - Credentials encrypted at rest — TOTP secrets, the Telegram bot token, the Samco 2FA secret, and Flow workflow API keys are now Fernet-encrypted in the database, matching the existing pattern used for broker tokens and TradingView API keys. The Flask session cookie also no longer carries the user's TOTP secret (it was signed but not encrypted, exposing the seed via cookie reads). All read paths fall back to plaintext for pre-update rows so existing data continues to work without a forced migration.
- Forwarded-IP header trust gate (TRUST_PROXY_HEADERS) —
utils/ip_helper.pypreviously honouredCF-Connecting-IP/X-Forwarded-For/X-Real-IP/True-Client-IP/X-Client-IPfrom any client unconditionally. Direct-bind deployments could be fooled by a client setting these headers to fake their source IP, silently bypassing the IP ban list, the per-IP login rate-limiter, the 404 auto-ban tracker, and the login-attempt audit log. Headers are now ignored by default and only trusted whenTRUST_PROXY_HEADERS=TRUEis set; the install scripts that configure nginx set this automatically. - Password change kicks every browser session —
change_password,change_password_api, and the/auth/reset-passwordflow now clear all active sessions and emitforce_logoutso other devices stop accepting the old cookie immediately. Closes the gap where an attacker holding a stolen cookie kept their session even after the legitimate user changed the password. - Docker
.envpermission fix (closes #960) — fresh Docker installs no longer crash-loop withError: .env file not found.because the install scripts now use a permission mode that the container'sappusercan read via the bind mount. The Profile → System health check is Docker-aware and recommends the correct mode for each deployment type. - postcss CVE-2026-41305 — pinned to
>=8.5.10(resolves to 8.5.12) via npmoverrides. Closes Dependabot alert #153. - Two earlier credential-leak vectors — fixes carried in
2c53573f(closes session and log leaks identified in the security audit). - Broker fixes — Paytm and Groww decimal-strike option symbols (#908), Kotak native MPP via the
mpparameter (#1293), sandboxFundManager._lockself-deadlock when the funds row is missing (#1274).
Security
APP_KEY / API_KEY_PEPPER
.sample.envnow uses placeholder strings instead of real-looking hex values. New users runningcp .sample.env .envfollowed byuv run app.pysee a one-time green[OpenAlgo first-run setup]message; the app generates fresh secrets and writes them back to.envatomically.- Existing users who installed via
install.sh(custom keys generated at install time) see no change — the check is a singlefrozensetlookup that returns immediately for any non-placeholder value. - The same detection covers
.envfiles copied from a pre-fix commit (the historical leaked literals are also recognised and rotated). update.shandupdate.batno longer fall through to acp .sample.env .envthat leaves placeholder values in place; they now generate fresh keys in that recovery branch..envis tightened to mode0o600on bare-metal installs and0o644on Docker installs (UID 1000 must read it via the bind mount).update.shretroactively tightens existing-install.envperms when run.
Encrypted-at-rest credentials
Four columns previously stored in plaintext are now Fernet-encrypted on write and decrypted on read:
users.totp_secretbot_config.token(Telegram bot token)auth.secret_api_key(Samco 2FA secret)flow_workflows.api_key(Flow workflow OpenAlgo API key)
A backward-compatible read path means existing rows continue to work between the code update and any operator-driven re-encryption pass — there is no in-between broken state.
The Flask session cookie no longer carries session["totp_secret"] (Flask sessions are signed, not encrypted; the cookie was leaking the TOTP seed to anyone who could read the cookie value).
Reverse-proxy trust (TRUST_PROXY_HEADERS)
- New
.envflag, defaultFALSE. When false,get_real_ip()returns the immediate peer address only; client-supplied forwarded-IP headers are ignored. install.sh,install-multi.sh,install-docker.sh, andinstall-docker-multi-custom-ssl.shset this toTRUEautomatically because they configure an nginx reverse proxy as part of the install (gunicorn binds on a Unix socket / container-gateway-only port that cannot be reached directly from the internet, so the proxy headers are trustworthy).update.shmigrates existing installs by detecting nginx in/etc/nginx/sites-enabledor/etc/nginx/conf.dand adding the appropriate value to.env—TRUEif a proxy is found,FALSEotherwise. No manual edit needed for the common reverse-proxied bare-metal install.- Local dev (
cp .sample.env .env+uv run app.py) anddocker-run.shdesktop installs leave itFALSE, which is the correct default for direct-bind setups.
Password change session invalidation
blueprints/auth.py:change_password,change_password_api, and the/auth/reset-passwordstep="password"branch all now callclear_user_sessions(username)and emitsocketio.emit("force_logout", …)after a successful password change. Every device — including the one that just changed the password — gets logged out and prompted to log in again with the new credentials.- This matches the convention used by banking and high-security web apps: a password change is a hard reset of the auth state, on the assumption that the change was triggered by suspected compromise (or routine rotation that wants the same effect).
postcss XSS (CVE-2026-41305)
- Bumped via
frontend/package.jsonoverridestopostcss >= 8.5.10. Resolves to 8.5.12 in the lockfile. npm installnow reports 0 vulnerabilities.
Pip CVE-2026-3219
- Dependabot alert #152 dismissed as
tolerable_risk.pipis transitive inuv.lock, only invoked at install time, never at runtime. The advisory has nofirst_patched_versionpublished yet; will revisit whenuvpicks up the upstream fix.
Optional operator tool: upgrade/rotate_pepper.py
A standalone, opt-in tool is included for operators who want to additionally rotate API_KEY_PEPPER to a fresh value and re-encrypt every credential in the database. It is intentionally not wired into migrate_all.py because rotating the pepper invalidates Argon2 password hashes (Argon2 is one-way; hashes cannot be migrated) — running it requires a one-time password reset via the TOTP flow afterwards. Use this only if your .env ever held the publicly-known sample API_KEY_PEPPER literal and you want to make absolutely sure no attacker has a copy of decryptable ciphertext from your database. Most users will never need it; the regular four-step upgrade below is sufficient.
Bug Fixes
Docker / Install
.envpermission bug (#960) — host.envat mode0o600was unreadable to the container'sappuser(UID 1000) when bind-mounted, causingstart.shto exit withError: .env file not found.and crash-loop the container. Fixed acrossinstall/install-docker.sh,install/install-docker-multi-custom-ssl.sh, andinstall/docker-run.sh(now use mode0o644). The Profile → System health check at/api/systemis Docker-aware and expects the correct mode per deployment.update.shrecovery branch — when.envwas missing, the script previously copied.sample.envverbatim and exited; the running app would have hit the public sample keys. Now generates fresh keys viasecrets.token_hex(32)in that path.update.shexisting-install hardening — automatically addsTRUST_PROXY_HEADERS(auto-detected) and tightens.envperms to0o600on the next update if not already set.
Brokers
- Paytm and Groww decimal strikes (#908) — option symbols like
VEDL25APR24292.5CEnow retain the.5instead of being silently stripped during symbol normalisation. - Kotak — native MPP via
mpparameter (#1293) — replace the localMKT → LMTrewrite with the broker's native market-protection-price flag.
Sandbox
FundManager._lockself-deadlock (#1274) — when the sandbox funds row was missing, the_lock-holding code path tried to acquire the same lock again. Fix unwinds the call so the missing-row branch releases the lock first.
Auth / Logging
- Two credential-leak vectors closed in
2c53573f— session and log paths that could surface API keys / tokens in error contexts.
Frontend / Tooling
frontend/package.jsonoverrides extended withpostcss >= 8.5.10.db/backups/added to.gitignore(created automatically by the optional pepper-rotation tool).
Dependencies
postcss: pinned>= 8.5.10(resolved 8.5.12) — closes CVE-2026-41305 / GHSA-qx2v-qp2m-jg93.
Required Upgrade Steps
The standard four-step upgrade. Your data, broker session, authenticator codes, and TradingView/Chartink API keys are all preserved automatically. No new password reset is required for the regular upgrade path.
1. Stop OpenAlgo
| Deployment | Command |
|---|---|
| Bare-metal (systemd) | sudo systemctl stop openalgo-<your-deployment>
|
| Docker | cd /opt/openalgo && sudo docker compose down
|
| Docker desktop | ./install/docker-run.sh stop
|
| Local dev | Ctrl+C in the terminal running uv run app.py
|
2. Pull the latest code and dependencies
cd /path/to/openalgo
git pull origin main
uv syncIf you're on Docker and your .env was at mode 0o600, run this once to unbreak the container before the next start:
sudo chmod 644 .envIf you installed via install.sh and want the new TRUST_PROXY_HEADERS flag + tightened .env perms applied automatically, run:
sudo bash install/update.shIt detects nginx, sets the right value in .env, and tightens perms to 0o600. (Skipping this is fine — IP-based features still work; they just see the proxy IP instead of the real client IP until you set TRUST_PROXY_HEADERS=TRUE manually.)
3. Apply standard database migrations
This is the same step you've always run on every release.
cd upgrade
uv run migrate_all.py
cd ..4. Restart OpenAlgo
| Deployment | Command |
|---|---|
| Bare-metal (systemd) | sudo systemctl start openalgo-<your-deployment>
|
| Docker | cd /opt/openalgo && sudo docker compose up -d
|
| Docker desktop | ./install/docker-run.sh start
|
| Local dev | uv run app.py
|
What's preserved (no action needed)
- Authenticator app codes — your TOTP enrolment is unchanged. The same QR you scanned at setup keeps producing valid codes.
- Login password — the regular four-step upgrade does not change your password. You keep using the same credentials.
- Broker session — your saved broker auth token still works. No need to re-OAuth with your broker.
- TradingView / Chartink / Excel / Python API keys — your OpenAlgo API key value is unchanged. External integrations continue to work without any configuration change.
- All trades, positions, strategies, watchlists, settings, logs.
What changes
- If your
.envever held the publicly-known sampleAPP_KEYvalue, the app rotates it to a fresh random value on first start and prints a one-time[OpenAlgo first-run setup]line in the console. Your existing browser session ends and you log in again with the same password. Most users who installed viainstall.shalready have custom keys and see no message. TRUST_PROXY_HEADERSis added to your.envon the nextupdate.shrun (auto-detected, no manual edit required).- New TOTP secrets, Telegram bot tokens, Samco 2FA secrets, and Flow workflow API keys created from this release onward are stored encrypted at rest. Existing rows continue to work transparently via the backward-compatible read path.
Optional: full credential refresh (operator-driven)
If your installation ever ran with the publicly-known sample API_KEY_PEPPER value in .env, you can additionally run the standalone pepper-rotation tool to generate a fresh pepper and re-encrypt every credential in the database. This is a one-time operator decision and requires a one-time password reset via the TOTP flow afterwards. The tool prompts for confirmation, creates a database backup before any change, and is documented inline:
cd upgrade
uv run rotate_pepper.py
# Type 'yes' when prompted. Then restart and use /auth/reset-password (TOTP)
# to set a new password.Most users do not need this — install.sh deployments have always generated fresh per-instance keys, so their database has never been encrypted under a publicly-known value.
Contributors
- @marketcalls (Rajandran) — release management, broker fixes (Paytm/Groww strike preservation, Kotak native MPP), sandbox
FundManagerfix, websocket polish, and the v2.0.0.6 critical security update including APP_KEY/PEPPER hardening, encrypted-at-rest credential overhaul, the optional pepper-rotation tool, the reverse-proxy trust gate, password-change session invalidation, the Docker.envpermission fix, and the postcss CVE bump.
Upgrade Quick Reference
# 1. Stop OpenAlgo (see table above for your deployment type)
# 2. Pull and sync
cd /path/to/openalgo
git pull origin main
uv sync
# 3. (Optional, server-mode only) Apply install-script hardening
sudo bash install/update.sh
# 4. Run standard migrations
cd upgrade && uv run migrate_all.py && cd ..
# 5. Restart OpenAlgo (see table above for your deployment type)
uv run app.py
# 6. Open the web UI -> Login (same password as before)Links
- Repository: https://github.com/marketcalls/openalgo
- Documentation: https://docs.openalgo.in
- Discord: https://www.openalgo.in/discord
- YouTube: https://www.youtube.com/@openalgo
- Issue tracker: https://github.com/marketcalls/openalgo/issues