This tool holds OAuth credentials for a brokerage account and can place orders against it. Treat every part of it accordingly.
| thing | where | protection |
|---|---|---|
| Schwab app key and secret | .env |
gitignored; never logged |
| Your contact address for EDGAR | .env |
gitignored; sent only to sec.gov in the User-Agent |
| OAuth token | ~/.investbot/token.json |
gitignored pattern *token*.json; 7-day expiry |
| Account numbers and hashes | API responses | redacted to last-four at every output boundary |
| Your holdings and targets | portfolio.toml |
gitignored; committed example only |
| Your trading allowlist and limits | risk.toml |
gitignored; committed example only |
| Orders you intend to place | orders.toml |
gitignored; committed example only |
| Tax lots and cost basis | data/ledger.db |
gitignored |
| Every placement and refusal | ~/.investbot/audit.jsonl |
outside the repo; contains order ids, not account numbers |
If you find a way to make this tool place an order it should not, leak an
account identifier, or reach /orders from a path other than
investbot place --live, please report it privately rather than in a
public issue. Open a GitHub security advisory on this repository, or
contact the maintainer directly.
Include what you did, what happened, and what you expected. A failing test that demonstrates it is the best possible report.
- Any path to
/ordersthat does not pass throughexecute_orderswith bothexecution_enabledandlive=True. - Any way for the MCP server to arm execution.
- Any retry around
post_once. Schwab has no idempotency key; a retry is a double-execution. - A full account number or hash reaching stdout, a log line, an MCP response, or an exception message.
- A committed file containing real holdings, balances, lots, or orders.
- A test in the default suite that reaches the network.
- The 7-day token expiry. It is Schwab's policy and the reason this tool cannot run unattended.
- The absence of paper trading. Also Schwab's.
- That a user with a correctly configured
risk.tomland a deliberately writtenorders.tomlcan lose money. The tool bounds the blast radius of a mistake; it does not evaluate the trade.
schwab-py handles OAuth. httpx handles everything else. yfinance
scrapes Yahoo and is used for backtests and fund metadata only -- it is
never in the trading path. Keep them current; run the contract tests after
a bump.