Security
Tokens for Good asks you to point an AI agent, running on your own subscription, at a queue of nonprofits. That is a real thing to hand over, so here is exactly what it does, what it cannot do, and what runs on our side.
What your agent does on a run
Before its first search, your agent prints a run plan and then gets to work. It shows the plan and proceeds; it does not stop and ask for approval, because you already asked for the run. The plan looks like this:
- Researching: the organization it just claimed, by name and website.
- Reads: that organization's public website plus public sources about it (web search results, annual reports, ProPublica 990 filings, independent evaluations, charity raters). Public pages only.
- Sends back: one research report and an honest token estimate, to the Tokens for Good API. Requests also carry the package version, platform name, and a random install id so we can spot a broken routine. Nothing goes anywhere else.
- Never touches: your files, your repos, your other MCP servers or connected accounts, your browser session, your credentials, or anything that spends money.
- Takes: about 5 minutes.
A run needs three tools and no more: web search, web fetch, and (for scheduled routines) curl to our API. It reads pages with fetch rather than driving your browser, it never signs in to anything, and it has no reason to read your code. If you want extra isolation, run it in a context with no other sensitive accounts connected.
Why a standing agent is safe to leave running
The scary version of this product would be a routine that phones home for its instructions every night. That is what prompt-injection warnings exist to catch, and early Tokens for Good routines did it, so the warnings people saw were fair.
Current routines are self-contained. The full research methodology is embedded in the routine prompt when you install it, so its instructions are frozen at install time and nothing remote can change what your standing agent does. At runtime it only exchanges JSON data with our API: an org to research, a submission receipt, and a version handshake at /api/research/parameters that returns numbers and row keys, never prose. Upgrade an older routine in one command with /tfg-schedule; it replaces the routine in place, keeping your cadence and current key.
Report bodies never go through a shell either. The routine writes the JSON body to a file and posts it with curl -d @file, so report text full of quotes and shell metacharacters cannot break out of a command on an unattended run.
Your key and your data
- Your API key lives in your MCP config and, for a scheduled routine, in the routine prompt. It is sent only as the
X-TFG-Api-Keyheader, never in a URL. - Keys are SHA-256 hashed at rest and compared with a constant-time check. We cannot read your key back to you; rotate or revoke it any time from your dashboard, and the old one dies immediately.
- Each agent (worker) gets its own key and its own research slot, so revoking one does not disturb the others.
- We hold what you would expect a contributor account to hold: your GitHub handle and avatar, your email if GitHub shared one, your claims and reports, and token estimates. We also log each API call (endpoint, status, platform, package version, install id, and a hashed IP) so we can spot broken routines and count active installs. Reports are public research about nonprofits, not anything about you.
The model never grades its own homework
- Every organization is researched twice, independently, by two contributors who cannot see each other's work.
- Every EVIDENCE TABLE quote is checked deterministically against the page it cites. If the quote is not on the page, the row is dropped and flagged.
- An independent validator prunes unsupported rows against the server's cached copy of each cited page, and can only subtract or correct, never add.
- A consolidator merges the two validated tables, and the final score is computed by code from that merged table. No model scores anything.
- A human reviewer finalizes each report before it is published as the org's directory report.
- An org with no published outcome evidence is submitted with
no_evidence: truerather than invented rows. Honesty is an accepted outcome here.
Server-side hardening
The parts of the platform that touch contributor input, in plain terms:
- Server-side fetching (SSRF): every fetch of a contributor-supplied URL goes through one hardened fetcher. It allows http and https only, blocks private, reserved, loopback and link-local targets, pins DNS for the connection so a name cannot resolve to something else between the check and the fetch, and revalidates every redirect hop against the same rules.
- Authentication: the research API authenticates on a hashed API key with a constant-time compare; contributor pages sit behind GitHub sign-in; reviewer and admin areas behind role checks on top of that; and admin endpoints require a separate admin key. Repeated failed logins lock out, both per account and per IP.
- Rate limits: the authenticated API is limited per key, so two agents on one machine get independent budgets and contributors behind one network do not share a bucket, with a per-IP backstop so rotating keys from one client cannot slip the limit. Public pages, the remote MCP endpoint, and form submissions are throttled too, and the app trusts the platform proxy headers so those limits key on the real client.
- Transport: HTTPS everywhere, with HSTS set to a one-year max-age on the live site.
- Webhooks: inbound webhooks are signature-verified before anything is processed.
We run a full security sweep over both repos periodically, fix what it finds, and log the result. What we have not done is buy a third-party penetration test; the project is young and that money currently buys more by going into research. We would rather say that plainly than imply an audit we do not have.
Read the source
The client half is open source and small enough to read in a sitting: the npm package and MCP server at github.com/Tokens-for-Good/tokens-for-good. The security model in this page is also kept in the repo as SECURITY.md. Start with src/mcp-server.js (every tool your agent can call), src/run-plan.js (the run plan above), and src/platform.js (the scheduled-routine prompt, exactly as installed).
Reporting a vulnerability
Please do not open a public GitHub issue for a vulnerability.
- Preferred: open a private ticket from Help & support on your dashboard. It goes straight to us and stays private.
- No account, or the site itself is the problem: email hank@tokensforgood.ai, a monitored two-way address.
Tell us what you found, how to reproduce it, and what an attacker gets out of it. We read these quickly and will keep you posted until it is fixed. There is no bug bounty; we will credit you in the fix if you want the credit.
Design ideas and hardening suggestions that expose nobody are welcome in the open: open an issue.
Still deciding?
The setup guide walks through installing the MCP server, and the docs cover the same trust questions in troubleshooting form.
Read the docs