Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,12 @@ This table is checked in CI against [`outbound-hosts.json`](outbound-hosts.json)
| Pricing refresh (daily) | `raw.githubusercontent.com` | server | The public [LiteLLM](https://github.com/BerriAI/litellm) price list. Anonymous — no credentials, nothing sent. Works offline from a bundled snapshot. |
| Quota chips + Limits page | `api.anthropic.com`, `chatgpt.com`, `api.openai.com`, `cursor.com`, `www.cursor.com`, `cloudcode-pa.googleapis.com`, `api.kimi.com`, `api.z.ai`, `api.github.com` | server | Asks *your* provider about *your* plan limits, using credentials already on your machine. Only for providers you actually use. |
| Token refresh | `auth.openai.com`, `oauth2.googleapis.com`, `auth.kimi.com` | server | Renews those same provider credentials when they expire. |
| Skills tab | `skills.sh`, `api.github.com`, `github.com` | server | Searches the public skills directory and reads public repository metadata. Sends your search terms, nothing else. Only when you open that tab. |
| Skills tab — search | `skills.sh` | server | Searches the public skills directory. Sends the terms you typed. Only when you open that tab. |
| Skills tab — install | `api.github.com`, `raw.githubusercontent.com` | server | Reads a public repository's file tree and downloads the skill's files. Carries the repository owner, name, branch and path — no usage data. |
| Currency conversion | `open.er-api.com` | **browser** | Public USD exchange rates. Anonymous. Only when you pick a non-USD currency. |
| Star count in the header | `api.github.com` | **browser** | The star count for this project's own repository — a fixed public URL that says nothing about you. |
| IP check page | `ip.net.coffee`, `1.1.1.1`, `claude.ai`, `www.anthropic.com` | server + **browser** | Only if you open that page, whose entire purpose is showing you your own IP and whether Anthropic is reachable. |
| IP check page | `ip.net.coffee`, `d.ip.net.coffee`, `1.1.1.1`, `claude.ai`, `www.anthropic.com` | server + **browser** | Only if you open that page, whose entire purpose is showing you your own IP, your DNS resolver, and whether Anthropic is reachable. The `d.` subdomain is a per-run DNS probe: your browser resolves a unique name so the page can see which resolver answered. |
| IP check page — VPN leak test | `stun.l.google.com` (Google), `stun.cloudflare.com` | **browser** | WebRTC STUN binding requests, which is how the page detects whether a VPN leaks your real address — and which necessarily discloses that address to the STUN server. **This is the one place the dashboard talks to Google.** Only if you open that page. |
| `npx` startup | `registry.npmjs.org` | server | How `npx` works — it downloads the package. A global install avoids it. |

**What is deliberately absent:** the Projects panel does not fetch repository avatars or star counts. Doing so would have put the name of a repository you have checked out — a private one included — into a URL sent to GitHub from your browser. It did, until [#100](https://github.com/pitimon/TokenTracker/issues/100). Project rows now render a local icon.
Expand Down
8 changes: 7 additions & 1 deletion dashboard/src/ui/components/HeaderGithubStar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import { shouldFetchGithubStars } from "../dashboard/util/should-fetch-github-st
/**
* Dashboard / marketing header: single row — icon + Star + count (matches Shell header).
*/
export function HeaderGithubStar({ repo = "pitimon/TokenTracker" }) {
// This project's own repository, fixed. It was a prop with a default, which is
// an API that invites a caller to pass a user-derived value into a URL — the
// shape of issue 100. Nothing passed one, so this is hardening, not a fix.
const REPO = "pitimon/TokenTracker";

export function HeaderGithubStar() {
const repo = REPO;
const [stars, setStars] = useState(null);

useEffect(() => {
Expand Down
8 changes: 8 additions & 0 deletions dashboard/src/ui/dashboard/components/DataDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ export function DataDetails({
{/* Projects Tab */}
{activeTab === "projects" && (
<div className="space-y-1">
{/* The Daily tab explains itself when empty; this one rendered a blank
box, which reads as "you have no spending" rather than "nothing has
been attributed to a project yet". */}
{projectEntries.length === 0 ? (
<div className="oai-text-body-sm text-oai-gray-500 dark:text-oai-gray-300">
{copy("dashboard.projects.empty")}
</div>
) : null}
{projectEntries.slice(0, projectLimit).map((entry, idx) => {
const ref = typeof entry?.project_ref === "string" ? entry.project_ref : "";
const key = entry?.project_key || ref || `entry-${idx}`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,14 @@ describe("DataDetails", () => {
expect(screen.queryByText("fable-5")).not.toBeInTheDocument();
});
});

describe("DataDetails — Projects empty state", () => {
it("says so when nothing has been attributed to a project", () => {
// It used to render an empty container, which reads as "you spent nothing"
// rather than "no usage carries project attribution yet". The Daily tab has
// always explained itself; this one did not.
renderDetails({ projectEntries: [] });
fireEvent.click(screen.getByRole("tab", { name: "Project Usage" }));
expect(screen.getByText("dashboard.projects.empty")).toBeInTheDocument();
});
});
26 changes: 13 additions & 13 deletions openwiki-facts/source-facts.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,87 +44,87 @@
"methods": [
"POST"
],
"evidence": "src/lib/local-api.js:898",
"evidence": "src/lib/local-api.js:945",
"mutation": true
},
{
"path": "/functions/tokentracker-wrapped",
"methods": [
"GET"
],
"evidence": "src/lib/local-api.js:932",
"evidence": "src/lib/local-api.js:979",
"mutation": false
},
{
"path": "/functions/tokentracker-usage-summary",
"methods": [
"GET"
],
"evidence": "src/lib/local-api.js:943",
"evidence": "src/lib/local-api.js:990",
"mutation": false
},
{
"path": "/functions/tokentracker-usage-daily",
"methods": [
"GET"
],
"evidence": "src/lib/local-api.js:1028",
"evidence": "src/lib/local-api.js:1075",
"mutation": false
},
{
"path": "/functions/tokentracker-usage-heatmap",
"methods": [
"GET"
],
"evidence": "src/lib/local-api.js:1039",
"evidence": "src/lib/local-api.js:1086",
"mutation": false
},
{
"path": "/functions/tokentracker-usage-model-breakdown",
"methods": [
"GET"
],
"evidence": "src/lib/local-api.js:1107",
"evidence": "src/lib/local-api.js:1154",
"mutation": false
},
{
"path": "/functions/tokentracker-usage-category-breakdown",
"methods": [
"GET"
],
"evidence": "src/lib/local-api.js:1186",
"evidence": "src/lib/local-api.js:1233",
"mutation": false
},
{
"path": "/functions/tokentracker-project-usage-summary",
"methods": [
"GET"
],
"evidence": "src/lib/local-api.js:1232",
"evidence": "src/lib/local-api.js:1279",
"mutation": false
},
{
"path": "/functions/tokentracker-user-status",
"methods": [
"GET"
],
"evidence": "src/lib/local-api.js:1309",
"evidence": "src/lib/local-api.js:1356",
"mutation": false
},
{
"path": "/functions/tokentracker-usage-hourly",
"methods": [
"GET"
],
"evidence": "src/lib/local-api.js:1319",
"evidence": "src/lib/local-api.js:1366",
"mutation": false
},
{
"path": "/functions/tokentracker-usage-monthly",
"methods": [
"GET"
],
"evidence": "src/lib/local-api.js:1329",
"evidence": "src/lib/local-api.js:1376",
"mutation": false
},
{
Expand All @@ -133,15 +133,15 @@
"GET",
"POST"
],
"evidence": "src/lib/local-api.js:1363",
"evidence": "src/lib/local-api.js:1410",
"mutation": true
},
{
"path": "/functions/tokentracker-usage-limits",
"methods": [
"GET"
],
"evidence": "src/lib/local-api.js:1513",
"evidence": "src/lib/local-api.js:1560",
"mutation": false
}
]
Expand Down
Loading