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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
sudo apt-get install -y --no-install-recommends \
libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxi-dev libxfixes-dev \
libxss-dev libxtst-dev libxrender-dev libxinerama-dev \
libcurl4-openssl-dev \
libcurl4-openssl-dev zlib1g-dev \
libxkbcommon-dev libwayland-dev wayland-protocols libgl1-mesa-dev libegl1-mesa-dev \
libasound2-dev libpulse-dev libdbus-1-dev libudev-dev
- name: Configure
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ jobs:
sudo apt-get install -y --no-install-recommends \
libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxi-dev libxfixes-dev \
libxss-dev libxtst-dev libxrender-dev libxinerama-dev \
libcurl4-openssl-dev \
libcurl4-openssl-dev zlib1g-dev \
libxkbcommon-dev libwayland-dev wayland-protocols libgl1-mesa-dev libegl1-mesa-dev \
libasound2-dev libpulse-dev libdbus-1-dev libudev-dev
- name: Configure
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,9 @@ assets/fonts/Fontin-LICENSE.txt

# built website
_site/

# Report relay: deploy credentials are local-only, and so is anything wrangler leaves behind.
worker/.env
worker/.dev.vars
worker/node_modules/
worker/.wrangler/
13 changes: 10 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ Pipeline: **hotkey → auto-copy → clipboard → parse → identify → price

The overlay, Settings, the league list, the static game-data layer, the item layer (parse →
resolve → price-relevant numbers → search plan, plus the game-styled tooltip), the trade search,
poe.ninja reference pricing, the in-game currency exchange feed, QuickPaste and the binary updater
(with the Windows installer it depends on) are all **built and tested**.
poe.ninja reference pricing, the in-game currency exchange feed, QuickPaste, the bug reporter (with
the relay it posts to) and the binary updater (with the Windows installer it depends on) are all
**built and tested**.
What is not built is [docs/roadmap.md](docs/roadmap.md) — including the fact that a language other
than English cannot yet be selected, because the data build emits only English.

Expand All @@ -41,6 +42,8 @@ read whole; each is one layer.
| [docs/item-layer.md](docs/item-layer.md) | `src/item/` — parse, resolve, derive, range matching, and the plan rules every strategy shares. Where most pricing judgement lives. |
| [docs/strategy-unique.md](docs/strategy-unique.md), [strategy-map.md](docs/strategy-map.md), [strategy-gem.md](docs/strategy-gem.md), [strategy-logbook.md](docs/strategy-logbook.md) | One per search strategy that has more to say than the shared rules: uniques (including unidentified), maps (with charts and Valdo maps), gems, expedition logbooks (the one item that is up to three items at once). |
| [docs/quickpaste.md](docs/quickpaste.md) | The paste list — the popup at the cursor, the nine number-key slots, and the clipboard *write*, which is a seam of its own. |
| [docs/reporting.md](docs/reporting.md) | `src/report/`, the **Report a bug** button and its dialog, the screenshot read-back and the PNG encoder — the app side of a bug report, up to the request. |
| [worker/README.md](worker/README.md) | `worker/` — the Cloudflare Worker that relays that report to Discord, what has to be set up by hand, and the injection rules every payload field is held to. |
| [docs/trade-layer.md](docs/trade-layer.md) | `src/trade/` — query building, the two-step client, the rate limiter, and how results and the filter list are drawn. |
| [docs/ninja.md](docs/ninja.md) | `src/ninja/` — the poe.ninja reference price. |
| [docs/exchange.md](docs/exchange.md) | `src/exchange/` — GGG's hourly in-game currency exchange digests. |
Expand Down Expand Up @@ -72,7 +75,8 @@ rather than reconstructing the procedure.
- **HTTP:** libcurl behind `src/net/http.hpp` (static Schannel build on Windows, so the release is a
single `.exe`; gzip required, not `AUTO`). Do not re-add a `CURL::libcurl` alias — curl declares
that name itself. **JSON:** nlohmann/json. **Tests:** doctest. **Clipboard:** our own platform
seam, never SDL's.
seam, never SDL's. **zlib** is `ppc_core`'s one other link dependency, and only for
`util/png`.
- **Game data:** never baked into the binary. Built and published by the separate public repo
**[JIRPOS/PathOfPriceCheck-Data](https://github.com/JIRPOS/PathOfPriceCheck-Data)** and downloaded
at runtime, so a new league needs a data build rather than a new release.
Expand All @@ -91,6 +95,9 @@ violate one of these on the strength of not having read it.
- **Never issue a GGG request outside `trade::request`.** The shared rate limiter is a hard
requirement, not a courtesy. poe.ninja and the currency-exchange CDN are *different hosts with
different rules* and deliberately do not go through it. → trade-layer, external-apis
- **A bug report is sent only by a press, and the dialog shows the whole of it first.** Nothing may
reach the relay that the preview does not draw, and nothing about a report is gathered in the
background. → reporting, PRIVACY.md
- **The clipboard is ours to read *and* to write.** `clipboard_set_text` owns the X selection from
a thread of its own, because a write on X11 is a promise to answer for the text later. →
quickpaste, platform
Expand Down
12 changes: 11 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ add_library(ppc_core STATIC
src/leagues.cpp
src/util/sha256.cpp
src/util/base64.cpp
src/util/png.cpp
src/report/report.cpp
src/util/debug_log.cpp
src/data/index.cpp
src/data/lexicon.cpp
Expand Down Expand Up @@ -155,7 +157,12 @@ add_library(ppc_core STATIC
src/platform/input.cpp)
target_include_directories(ppc_core PUBLIC src)
target_compile_definitions(ppc_core PUBLIC APP_VERSION="${APP_VERSION}")
target_link_libraries(ppc_core PUBLIC nlohmann_json::nlohmann_json)
# zlib: the PNG encoder's deflate, and nothing else. Already in the tree on the Windows path
# above, where curl fetches it; everywhere else it is a system package (libcurl needs it too).
if(NOT TARGET ZLIB::ZLIB)
find_package(ZLIB REQUIRED)
endif()
target_link_libraries(ppc_core PUBLIC nlohmann_json::nlohmann_json ZLIB::ZLIB)

set(APP_SOURCES
src/main.cpp
Expand All @@ -169,6 +176,7 @@ set(APP_SOURCES
src/ninja_service.cpp
src/ninja/client.cpp
src/exchange_service.cpp
src/report_service.cpp
src/exchange/client.cpp
src/icon_cache.cpp
src/data/updater.cpp
Expand All @@ -180,6 +188,7 @@ set(APP_SOURCES
src/screens/settings_screen.cpp
src/screens/quickpaste_screen.cpp
src/screens/pricecheck_screen.cpp
src/screens/report_screen.cpp
src/screens/item_view.cpp)
if(WIN32)
list(APPEND APP_SOURCES
Expand Down Expand Up @@ -283,3 +292,4 @@ ppc_add_test(exchange_test)
ppc_add_test(ratelimit_test)
ppc_add_test(track_test)
ppc_add_test(quickpaste_test)
ppc_add_test(report_test)
87 changes: 81 additions & 6 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# Privacy

**This project collects nothing.** There is no account, no telemetry, no analytics, no crash
reporting and no usage counter — and, more to the point, **there is no server on the other end to
collect anything with.** The project operates no backend of any kind. Everything the application
does happens on the machine it runs on, against third-party APIs that are the same ones a browser
would talk to.
reporting and no usage counter. Nothing is gathered in the background, on a timer, or as a side
effect of anything you do. Everything the application does happens on the machine it runs on,
against third-party APIs that are the same ones a browser would talk to.

**There is exactly one thing you can send us, and only by pressing a button that says so.**
**Report a bug** on the price-check panel opens a dialog that shows you the entire payload — the
item text, what the tool made of it, four version strings, whatever you type, and a screenshot
only if you tick the box — and sends it nowhere until you press Send. It goes to a small relay of
ours, described in full [below](#reporting-a-bug). That relay is the project's only backend, it
exists for that one button, and nothing else in the application ever talks to it.

The one thing that might sound like a phone-home is the update check, so it is worth being exact:
it downloads a small **static file** from the GitHub release page — the same bytes served to
Expand All @@ -31,6 +37,7 @@ Exhaustively — this is every outbound request the binary can make.
| `web.poecdn.com/api/currency-exchange/<hour>` | when an item is priced and the newest published hour is not already on disk; **one download covers every item and every league** | nothing but the request |
| `poe.ninja/poe1/api/economy/...` | when a reference price is needed and the 30-minute cache has expired; **once per category**, not per price check | nothing but the request and the league name |
| `poe.ninja/favicons/favicon-32x32.png` | once, for the reference row's source mark | nothing but the request |
| `ppc-reports.jirpos.workers.dev` | **only when you press Send in the bug reporter**, never otherwise | the report you were shown before you pressed it — see [below](#reporting-a-bug) |

**Opening a search in your browser** builds the same query into a `pathofexile.com/trade/search`
URL and hands it to your browser. That costs no API call, and what happens after it is between you
Expand Down Expand Up @@ -74,6 +81,9 @@ The whole tool works by reading the clipboard, so this is worth being precise ab
- If you press the hotkey while something other than an item is on your clipboard, that text is
what gets parsed. It fails to parse, nothing opens, and it is discarded. But it *was* read — so
the ordinary caution applies: this is a global hotkey and the clipboard is a global thing.
- Item text you priced is held in memory until the next check, and is the one thing a **bug
report** can carry off the machine — only the check you are looking at, and only if you press
Send. See [Reporting a bug](#reporting-a-bug).
- **Two things write to your clipboard, both because you asked**: picking an entry from QuickPaste,
which puts that entry's own text there and nothing else, and clicking the diagnostic check id in
the panel footer, which copies that four-character id. On Linux the text is then served from a
Expand Down Expand Up @@ -113,12 +123,77 @@ its shortcuts, one registry value at `HKCU\Software\PathOfPriceCheck` recording
installed, and the usual Add/Remove Programs entry; uninstalling removes them.

One optional setting is personal information you may type in yourself: **Account** in Settings
(`Name#1234`). It is stored in `config.json` and, as of today, is **not sent anywhere** — nothing
in the request path reads it.
(`Name#1234`). It is stored in `config.json` and is **never sent anywhere** — nothing in the
request path reads it, the bug reporter included. It is used for one thing: marking a listing in
the results as yours, and even a screenshot you choose to attach has it replaced along with every
other handle on the table.

Search results contain other players' account names and the whisper text for contacting them.
Those live in memory for as long as the panel is open and are dropped when the next check runs.

## Reporting a bug

The **Report a bug** button on the price-check panel. Nothing here happens unless you press it, and
then press **Send** in the dialog it opens.

**The dialog is the disclosure.** It shows the payload in full, in the same text that goes on the
wire, before anything is sent — there is no summary standing in for the real thing and no field it
does not display. Read it, and if you would rather not send some part of it, close the dialog.

What a report contains, exhaustively:

| | |
|---|---|
| the item | the clipboard text the game wrote, verbatim and unedited |
| the parse | what this tool made of that text: the fields it read, what they resolved to in the data bundle, which modifier matched which stat record and which matched none, and what a search would have asked for |
| your comment | the box you typed in, or nothing if you left it empty |
| four version strings | the application's version, the operating system's name (`Linux`, `Windows`), the league you have selected, and the data bundle's version. Nothing else, and none of them is per-machine |
| a screenshot | **only if you tick the box.** See below |

What a report does **not** contain: your account name, your character, any identifier of your
machine or install, any path from your disk, any cookie, and anything at all from a previous check.
There is no id tying two reports to one person, because there is no id.

### The screenshot

The checkbox starts unticked. The picture beside it is the exact image that would be attached, at
the moment you pressed the button, so the decision is one you can make by looking.

It is a **read-back of this application's own window**, not a capture of your screen: the pixels
this program drew, and only those. The game behind the transparent parts of the overlay is not in
it and cannot be — nothing here has the ability to photograph another window. Everything else on
your desktop is likewise absent.

**Nobody's account name is in it.** On an item that ran a search the panel shows a results table,
and before the picture is taken the panel is redrawn with every seller's handle replaced by its
position — `seller 1`, `seller 2` — so what is photographed never had a name on it. Yours is
covered by the same rule, on the row marked as yours. Prices, ages and everything else about the
market are left exactly as they were, because those are the thing a mispricing is read against.

What is in it, then, is the panel as you were looking at it with the names taken out — which is
still worth checking before you tick the box, and is why the preview is the size it is.

### Where it goes

To `ppc-reports.jirpos.workers.dev`, a Cloudflare Worker operated by the maintainer, which forwards
it to a private channel the maintainer reads and does nothing else with it. The Worker keeps no
database, writes no log of requests, and stores nothing: the report is relayed and the request is
over. Its source is in [`worker/`](worker/) in this repository, so what it does is readable rather
than promised.

Cloudflare sits in front of it and sees your IP address, as any host you make a request to does;
their [privacy policy](https://www.cloudflare.com/privacypolicy/) applies. The Worker uses that
address for one thing — an hourly cap, so the endpoint cannot be flooded — and it is never part of
what reaches the channel.

A report stays in that channel until it is dealt with. If you want one removed, quote its id: the
dialog shows it after a successful send and it is the only handle either of us has on it.

### Turning it off

There is nothing to turn off, because nothing runs. The button sends when you press it; if you
never press it, the application never contacts the relay and never has.

## The debug log

**Off by default, for everyone.** It exists because the clipboard handover between the game, Wine
Expand Down
4 changes: 3 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,9 @@ dismiss-on-blur for local dev; add `PPC_DEV_ITEM=<file>` to open the price-check
clipboard instead, `PPC_DEV_PASTE=1` to open the paste popup at the pointer, or `PPC_DEV_IDLE=1` to keep the idle status marker up (it otherwise only ever
appears while the game is the window in front). `PPC_DEV_UPDATE_URL=<url>` points the update check
at a `latest.json` of your own, which is the only way to see its three notice surfaces before a
release publishes one — see [updater.md](updater.md).
release publishes one — see [updater.md](updater.md). `PPC_REPORT_URL=<url>` points the bug
reporter at a relay of your own, which is how its two outcomes are seen without posting into the
real channel — see [reporting.md](reporting.md).

The Windows binary is **GUI-subsystem** (`WIN32_EXECUTABLE`, entered at `WinMain` in `src/main.cpp`):
a console-subsystem build pops a console window beside an application whose whole UI is an overlay
Expand Down
Loading