fix: run_local_safe.sh leaves the Deep Research service reachable
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 85/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- bash, python
- Domain
- documentation, tooling
Research direction
Edit dashboard/scripts/run_local_safe.sh to add RESEARCH_SHELL_SERVICE_URL= RESEARCH_DD_SERVICE_URL= RESEARCH_SERVICE_TOKEN= to the env list (line ~37-51). Update docs/local-run.md section 8 and script header line 4, and add the URLs to docs/architecture/configuration.md. Done when the script blanks those vars and docs reflect that no external research call can leave a safe local instance.
Written by the indexing model from the issue text.
Description
Checked at origin/main 7f9ce0f3 by reading only. Nothing was run, so every behavioural claim below is from source.
Summary
dashboard/scripts/run_local_safe.sh says it blanks "every broker/LLM key, so nothing can trade or bill" (header, line 4), and docs/local-run.md section 8 says "no LLM call can be billed". That holds for every LLM key the backend reads. It does not hold for the research-agent path: a dashboard/.env configured like prod can still submit a Deep Research run to a real external service from a "safe" local instance. That is the exact case the script's own header (lines 5-8) says it defends against.
One path can spend. The other two vars named in the report cannot.
How the script blanks (this part works)
run_local_safe.sh:37-51isexec env VAR= ... python -m uvicorn. That sets each variable to the empty string; it does not unset it.app.py:70-74then callsload_dotenv(dashboard/.env)with the defaultoverride=False. python-dotenv 1.2.2 (pinned,requirements.txt:175) skips any key already inos.environ(dotenv/main.py:105,if k in os.environ and not self.override: continue), and an empty string counts as present. So.envcannot refill a blanked key.domain/chat/service.py:24(load_dotenv()) behaves the same.- Blanked:
ALPACA_API_KEY,ALPACA_SECRET_KEY,ANTHROPIC_API_KEY,OPENAI_API_KEY,DEEPSEEK_API_KEY,COMMONSTACK_API_KEY,OPENROUTER_API_KEY, the three*_DATABASE_URL,BREVO_API_KEY,ATL_STRIPE_TEST_BILLING_ENABLED,STRIPE_SECRET_KEY,STRIPE_WEBHOOK_SECRET,IFIND_REFRESH_TOKEN,IFIND_ACCESS_TOKEN. Forced:ROBINHOOD_EXECUTE=false,ATL_BAR_CACHE_WARM=0,LEADERBOARD_DAILY_AUTO_DEPLOY=0,DATABASE_PATH,ATL_BAR_CACHE_DIR. - Every LLM client site gates on a non-empty key before building a client (
providers/anthropic_native.py:30,domain/chat/service.py:79-88,algo_service.py:149), so the SDKs' own env fallbacks are never reached with the key blanked.
What the script does not blank
| Var | Read at | Reachable locally? | Class |
|---|---|---|---|
RESEARCH_SERVICE_TOKEN (+ RESEARCH_SHELL_SERVICE_URL, RESEARCH_DD_SERVICE_URL) |
api/routers/research.py:92; URLs domain/agents/marketplace.py:57-59 |
Yes, if .env carries a real URL |
Can spend: submits an LLM research job on an external service |
FINGPT_API_KEY |
integrations/news_sentiment.py:71 |
Yes | External, free |
FINANCIAL_DATASETS_API_KEY |
infrastructure/ai_hedge_fund/adapter.py:75 |
No | Unreachable in practice |
DISCORD_CLIENT_SECRET, DISCORD_BOT_API_SECRET, DISCORD_OAUTH_STATE_SECRET |
api/discord_oauth.py:28,44,56 |
Yes, interactive OAuth only | External, free |
ROBINHOOD_MCP_URL, ROBINHOOD_OAUTH_STATE_SECRET |
infrastructure/brokers/robinhood_oauth.py:21,52 |
No | Unreachable in practice |
SESSION_HASH_SECRET, ADMIN_BOOTSTRAP_SECRET, LEADERBOARD_DAILY_REFRESH_SECRET, LLM_EXECUTION_HANDOFF_SECRET, ANALYTICS_PSEUDONYMIZATION_KEY, BROKER_TOKEN_ENCRYPTION_KEY |
various | n/a | Inbound or local crypto; no outbound spend |
1. Research service: can spend (conditional)
- The router is always mounted (
api/router.py:24,36) and its sweeper always started (app.py:369-376). POST /api/v1/research/agents/{id}/runs(research.py:262-355) needs a signed-in user, reserves $1 of local Credits (research.py:303), thenhttpx.post(f"{base}/runs", headers={"X-Service-Token": ...})atresearch.py:322-327. The agent service runs the Deep Research job, an LLM run on that service's keys (docs/integrations/research-agents.md). The reservation only bills the local SQLite copy.- A fresh local signup gets the welcome Credits grant (
api/auth.py:610,credits_service.grant_default_signup_credits; 8 Credits per CLAUDE.md), enough for the $1 reservation. I did not run the grant. - Blanking
RESEARCH_SERVICE_TOKENis not the lever._service_token()(research.py:82-98) refuses only whenRENDER/ATL_ENV=production. Locally an empty token becomes the literaldev-tokenand the call still goes out. Whether a real service rejectsdev-tokendepends on that service; I did not check it. - The URL is the lever.
marketplace.json:429,456namesRESEARCH_SHELL_SERVICE_URL/RESEARCH_DD_SERVICE_URL, defaulting tohttp://127.0.0.1:9100. Blank them and(base or default)falls back to the local default, so nothing leaves the machine. - Precondition: the URL vars are not in
.env.exampleand not indocs/architecture/configuration.md, so only a prod-deriveddashboard/.envsets them. That is the scenario the script exists for. - Even viewing the workbench calls
GET {base}/manifestwith the token (research.py:209-214).
2. FINGPT_API_KEY: external, free
news_sentiment._headers()(:70-72) adds a Bearer token to GETs againstagenticfinsearch.org(:22-23). These read precomputed artifacts. No LLM is called per request, and the FinSearch timers are paused (CLAUDE.md)./api/news/signalsis frozen by default (news.py:44-52,112, unset means frozen), so the panel makes no call unless.envsetsNEWS_FEED_FROZEN=0.- The live caller is every
/api/v2run step:execution/backtest_backend.py:194->get_news_sentiment->fetch_signals, regardless of the freeze flag. Without the key the same GET goes out anonymously (_headersreturns{}), so blanking the key changes only whether the prod bearer leaves the machine, not whether the call happens.
3. FINANCIAL_DATASETS_API_KEY: unreachable
backtests.py:2066pops it from the child env for the hosted runtime and substitutes the agent owner's own stored credential. The route also 503s whenOPENROUTER_API_KEYis empty (backtests.py:3953), and the script blanks that.HourlyBacktesteris built with a hosted runtime only in the scrubbed child (dashboard/scripts/backtest_hourly_agent.py:538;engine.py:353). The adapter's allowlist entry atadapter.py:75never sees an ambient value.
The wrong claims
docs/local-run.mdsection 8, line 213-214: "Covered by this setup: no broker order can be placed, no LLM call can be billed, and no persistent data outside~/.cache/atl-localis written."dashboard/scripts/run_local_safe.sh:4: "blanks every broker/LLM key, so nothing can trade or bill".- Not wrong as written: README.md:72 says "blanks broker, LLM, DB, mail, Stripe and iFinD keys", which matches the script.
- Imprecise:
docs/local-run.md:223-225says "the news panel may call the FinSearch endpoint". The panel is frozen by default; the call that happens is the per-step/api/v2one.
Suggested fix
run_local_safe.sh: add to theenvlist
RESEARCH_SHELL_SERVICE_URL= RESEARCH_DD_SERVICE_URL= RESEARCH_SERVICE_TOKEN=
(the two URLs do the work; the token stops a prod token being sent to127.0.0.1:9100). OptionallyFINGPT_API_KEY=so a prod bearer is not sent off-machine.FINANCIAL_DATASETS_API_KEY=is a no-op, so skip it.docs/local-run.md: add a bullet to the "The script:" list (line 78-86) for the research URLs and token. Reword the section 8 sentence to: "no broker order can be placed, and no LLM call can be billed to a key from the environment (a provider key you add yourself under BYOK is your own)". Change the news bullet to say the panel is frozen by default and the live FinSearch call comes from/api/v2run steps.- Script header line 4: say "blanks the broker, LLM, research-service, ..." rather than "every".
- Add
RESEARCH_SHELL_SERVICE_URL/RESEARCH_DD_SERVICE_URLtodocs/architecture/configuration.md(onlyRESEARCH_SERVICE_TOKENis listed, at line 297).
Not verified
Nothing was executed. Not checked: that a real research service rejects dev-token; that the welcome grant lands on a fresh local SQLite copy; who pays for the external service's LLM calls (docs/integrations/research-agents.md says the agent author, docs/architecture/backend-internals.md:37 says the run bills the user's Credits).
Found while fact-checking #695.
- Dominant language
- Python
- Stars
- 767
- Forks
- 156
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 71
Getting set up
- Ships a Dockerfile or Docker Compose file
- No pull request template
- No contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Open-Finance-Lab/AgenticTrading
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Open-Finance-Lab/AgenticTrading#699 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 Under an hour Newbie friendliness 85/100
Open-Finance-Lab/AgenticTrading#698 ·
Maintainers usually reply within 1 day
-
fix(llm): Gemini adapter drops thinking tokens from the output countPossibly taken @subidhkhanal claimed this 8 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
Open-Finance-Lab/AgenticTrading#657 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
Open-Finance-Lab/AgenticTrading#591 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Open-Finance-Lab/AgenticTrading#586 ·
Maintainers usually reply within 1 day
All issues in Open-Finance-Lab/AgenticTrading
Similar issues
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
NousResearch/hermes-agent#147104 ·
Maintainers usually reply within 1 day
-
[oblt-aw][autodoc] Document new required `shared-org-key` input for `obs-aw-dependency-review`Possibly taken @github-actions claimed this today. Open
Difficulty 1/5 Under an hour Newbie friendliness 90/100
Maintainers usually reply within 1 day
-
bot-triaged module: inductor oncall: pt2 topic: fuzzer triaged
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
getchaintips leaves out the active tip once a header is staged on it, where Core always lists itOpen
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
btclib-org/btclib-node#2158 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
patrick-hudson/put.gd-desktop#979 · 1 comment ·
Maintainers usually reply within 1 day