Trust in small tools
A cool tool can still ask the wrong question.
SteamPanno turns a Steam library into a visual poster. The fork was not about owning that idea. It was about the moment people looked at a Steam-related executable and said the obvious thing: why is this not a website?
SteamPanno is the kind of small project that exposes a bigger truth: software does not only fail when the code is bad. It fails when the trust ask is absurd.
The surface idea was harmless and fun. Turn a Steam library into a visual poster. Show people their gaming history in a way they can understand at a glance. That is a good little product idea.
The Reddit thread got interesting because the audience immediately found the fault line. Not the layout algorithm. Not the poster. Not the Steam API. The trust model.
A random executable that touches your Steam identity is a wild thing to ask strangers to run for a one-time image.
Even if the project is open source. Even if the developer is honest. Even if Windows Defender is throwing a false positive. None of that fixes the first thirty seconds of user trust. Normal people are not going to audit the source, compile the app, verify the binary, reason through Steamworks API permissions, and then decide whether the poster is worth it.
They are going to say the obvious thing: why is this not a website?
That is why I forked it. Not because I needed to own the idea. I did not. The original idea was already there. I wanted to explore the scarier question underneath it: what would this look like if the product stopped asking the user to trust the wrong thing?
The trust boundary moved into code.
The web path was not just a hosting change. Steam sign-in needs a callback, Steam API keys need custody, and browser sessions need real handling.
export function steamLoginUrl(returnTo: string, realm: string): string {
const p = new URLSearchParams({
"openid.ns": "http://specs.openid.net/auth/2.0",
"openid.mode": "checkid_setup",
"openid.return_to": returnTo,
"openid.realm": realm,
"openid.identity": "http://specs.openid.net/auth/2.0/identifier_select",
"openid.claimed_id": "http://specs.openid.net/auth/2.0/identifier_select"
});
return `https://steamcommunity.com/openid/login?${p.toString()}`;
} const token = await makeSession(steamid);
const isProd = process.env.NODE_ENV === "production";
res.cookie("session", token, {
httpOnly: true,
sameSite: "lax",
secure: isProd,
maxAge: 7 * 24 * 60 * 60 * 1000
}); services:
- type: web
name: steampanno-web
env: docker
dockerfilePath: webapp/Dockerfile
envVars:
- key: SESSION_SECRET
generateValue: true
- key: STEAM_API_KEY
sync: false That does not make the web version magically safe. Websites can be malicious. Hosted software has its own trust boundary. A login-looking flow can become phishing if it is handled badly.
But it changes the shape of the risk. Instead of asking, "Will I run this executable from the internet?" the user can ask, "What account data does this site need, where does the session live, and can I get the result without handing over more than necessary?"
The more interesting version might not need sign-in at all for the common case. If the user has a public profile, maybe a Steam profile URL is enough. Save sign-in for advanced features. Keep the first trust ask as small as the first use case.
That is also where I stopped. Not because the web path was impossible. Publishing it cleanly meant doing Steam developer/API identity setup I did not expect to need. For a serious product, that is just part of the work. For a side experiment whose point was already clear, it was enough friction to shelve it.
The poster was the shiny object. The trust boundary was the product.