factcircuit
FactCircuit: an auditable claim-and-evidence verification loop with immutable provenance, staged checks, bounded retrieval, and reproducible evaluation.
A live overview of the free-to-play games on Steam: who plays them, what they are, and what they ask of your machine.
DataTypeScript · PythonGitHub @poli0981OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
A curated list of free-to-play games on Steam– no quality guarantees, just pure listings. Quality? Playability? Fun? Who knows...
Translation pending
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
A curated list of free-to-play games on Steam — now ~3,600 of them — no quality guarantees, no endorsements, just pure listings by a broke, unemployed Vietnamese dev with too much free time and a Steam library full of sale games (80% under $10).
v4.0.0 is the version that got rebuilt from the ground up. It is still a list. It is also:Read the full README ↗A curated list of free-to-play games on Steam — now ~3,600 of them — no quality guarantees, no endorsements, just pure listings by a broke, unemployed Vietnamese dev with too much free time and a Steam library full of sale games (80% under $10).
v4.0.0 is the version that got rebuilt from the ground up. It is still a list. It is also:
/admin, behind Cloudflare Access: a review queue for the games the daily discovery sweep finds (approved by the maintainer, or in capped daily batches by a rule the maintainer turns on), corrections to published games, and removals;Powered ~70 % by AI (Grok for v1, Claude for v2 + the entire web/desktop layer) — code is reviewed and tested, but mistakes happen. See the Disclaimer for the specific accuracy caveats (genre best-effort, English-only assumptions, test-data leakage, unsigned-commit dev artefacts).
Wallet safe (for now). Play at your own risk.
| Surface | URL |
|---|---|
| 🌐 Web app | https://free-steam-games.win/ |
| 🖥️ Desktop releases | GitHub Releases (Win .msi, Mac .dmg, Linux .AppImage) |
| 📱 Android APK | GitHub Releases (sideload .apk, Android 11+ — see docs/android-support.md) |
| 🏆 Top online (md) | games/top-online.md |
| 🎯 Top offline (md) | games/top-offline.md |
| 📦 Raw data | data/data_001.jsonl … data_005.jsonl (5 sharded JSONL files) |
| 🛠️ About | https://free-steam-games.win/about (in-app) |
| 👤 Authors | AUTHORS.md — maintainer + contact channels + AI-assistant disclosure |
A SvelteKit 2 / Svelte 5 app, prerendered and served by a Cloudflare Worker that also proxies the dataset (/api/data/*) and Steam artwork (/img/*), so your browser talks to this site only — apart from Cloudflare's analytics beacon and its Turnstile human check, both loaded only after you accept the terms. Features:
/games/:appid is prerendered for search engines and filled with live player counts and reviews in the browser.data/index.json carries a SHA-256 per shard; the app only caches a generation it could verify, keeps the last good one offline, and re-checks when you come back to the tab.Pushing to main deploys (Cloudflare Workers Builds). Pipeline commits to data/** do not need a rebuild: the data is proxied, not bundled. See docs/DEPLOYMENT.md.
A native shell around the same web app, distributed via GitHub Releases:
.msi, macOS .dmg (universal Apple Silicon + Intel), Linux .AppImage and .deb, and an Android .apk.steam:// links open the Steam client./api/data/* on free-steam-games.win), same offline cache.Built by .github/workflows/release-desktop.yml (matrix Win/Mac/Linux via tauri-apps/tauri-action) and release-android.yml, triggered by a desktop-v* / android-v* tag or a manual run. Each release is created as a draft.
# from web/, locally:
npm run tauri:dev # dev build with hot reload
npm run tauri:build # release binary into web/src-tauri/target/release/bundle
Discover New Games sweeps the Steam store daily and proposes candidates into a Cloudflare D1 queue. The maintainer reviews them in a small Svelte app embedded in the Worker (never shipped to visitors): approving commits the store link to scripts/temp_info.jsonl, and the pipeline does the rest, checking every game again (free, a game, reachable) before it is published. Approval can also run on its own, in batches with a daily cap, once the maintainer turns it on. Decided and already-published rows are read-only. Corrections to published games are saved as override files that survive every pipeline run, and a removed game goes into data/blocklist.jsonl, which keeps it out of the catalogue until it is restored. An overview, the pipeline's runs and a data-quality report complete it. See docs/ADMIN.md.
The Python pipeline that powered v2 is untouched and runs daily as before:
data/data_*.jsonl (cap 800 records/shard) plus a data/index.json manifest with a SHA-256 per shard..
├── data/ # Sharded data (the source of truth)
│ ├── data_001.jsonl # ≤ 800 records
│ ├── data_002.jsonl … 005 # the rest
│ └── index.json # last_updated, totals, shard manifest
│
├── games/ # The two generated leaderboards
│ ├── top-online.md # live leaderboard
│ └── top-offline.md # most-played offline-capable
│
├── scripts/ # Python data pipeline
│ ├── core/
│ │ ├── constants.py # rate limits, anti-cheat patterns, schema
│ │ ├── steam_client.py # rate-limited HTTP client
│ │ ├── data_store.py # JSONL I/O, dedup, schema merge
│ │ ├── fetcher.py # batch processor
│ │ ├── scraper.py # HTML page parser
│ │ └── health_checker.py # status detection
│ ├── ingest_new.py / update_data.py / discover_new.py / …
│ └── temp_info.jsonl # ingest queue
│
├── web/ # Everything that is not the pipeline
│ ├── src/ # SvelteKit app: routes, components, charts, i18n
│ ├── admin/ # /admin review app (embedded in the Worker, never in dist/)
│ ├── worker/ # Cloudflare Worker: data + image proxy, admin API, D1 migrations
│ ├── shared/ # rules shared by the Worker and the admin app
│ ├── src-tauri/ # Tauri 2 desktop + Android shell (Rust)
│ └── static/ # icons, social image, robots.txt, _headers
│
├── docs/ # Legal + acknowledgements + contact
├── .github/workflows/ # data pipeline, discovery, CI, desktop/Android release
├── LICENSE # MIT — code
└── LICENSE-DATA # CC BY 4.0 — dataset
scripts/
├── core/
│ ├── constants.py # Config, rate limits, anti-cheat patterns
│ ├── steam_client.py # HTTP client, backoff, retry, session pooling
│ ├── data_store.py # JSONL I/O, validation, dedup, save_main() + index.json
│ ├── overrides.py # Human corrections re-applied on every write
│ ├── blocklist.py # Removed games, left out of every write
│ ├── fetcher.py # Batch processor, apply_details/reviews/players/scraped
│ ├── scraper.py # Store page HTML parser (languages, DLC, tags, anti-cheat)
│ ├── anticheat.py # Steam's anti-cheat disclosure and the rule that applies it
│ ├── steamcharts.py # SteamCharts all-time peak reader (polite, stops on refusal)
│ ├── peaks.py # all_time_peak: only ever raised
│ ├── rotation.py # crc32 daily slices shared by the refresh jobs, growing with the catalogue
│ ├── budget.py # A job's time budget: stop in time to save, warn before it runs out
│ └── health_checker.py # Game status detection with cached API data
├── discover_new.py # Propose new games into the /admin review queue
├── ingest_new.py # Add queued games (health check + fetch from Steam)
├── remove_games.py # Apply data/blocklist.jsonl now (runs before every ingest)
├── update_data.py # Daily: fill in incomplete records
├── refresh_store_data.py # Daily rotation: tags/languages weekly, store details monthly
├── refresh_anticheat.py # Daily: every game's anti-cheat, as its developer declares it on Steam
├── refresh_peaks.py # Daily rotation: all-time peak from SteamCharts, monthly per game
├── update_reviews.py # Daily: every game's review summary from Steam, ~2 minutes
├── mark_dead_games.py # Flag online games with no players
├── purge_unhealthy.py # Daily: remove delisted/not-free/unreleased (store API screen + appdetails)
├── top_online.py / top_offline.py # Leaderboards
├── edit_game.py # Overrides from the command line
├── refetch_all.py # Force re-fetch, in parts (manual only)
└── delete_game.py # Remove games (blocklist) from the command line
| Workflow | Trigger | What it does |
|---|---|---|
| Auto Update JSON | Daily 00:00 UTC | Fills in incomplete records (API + HTML scrape) |
| Discover New Games | Daily 05:00 UTC | Propose new free games into the /admin review queue |
| Ingest New | On scripts/temp_info.jsonl or data/blocklist.jsonl push, and every 3 h | Apply removals, then add approved games |
| Top Online | Sun/Wed/Fri 03:00 UTC | Live player leaderboard |
| Top Offline | 1st + 15th of month 05:00 UTC | Leaderboard for single-player F2P concurrents |
| Update Reviews | Daily 06:00 UTC | Every game's review summary from Steam's store API |
| Refresh Store Data | Daily 16:00 UTC | Rotating re-fetch: tags, languages, DLC flag weekly; name, image, description, developer/publisher, release date, platforms, Metacritic, DRM monthly. Then every game's anti-cheat from Steam's disclosure (committed separately; manual run with only: anticheat does just that) |
| Refresh All-Time Peaks | Daily 19:30 UTC | all_time_peak from SteamCharts, every game once a month, plus our own samples; manual run with backfill asks about every game |
| Purge Unhealthy | Daily 07:00 UTC | Store API screen of every game; removal once appdetails agrees |
| Mark Dead Games | Mon + Thu 04:30 UTC | Online + >1y + 0 players ≥14d → is_dead=true + 💀 note |
| Daily Snapshot | Daily 23:00 UTC | Per-game players and reviews into data/snapshots/ |
| Force Re-fetch | Manual only | Re-fetch games from scratch, one part (1/8 … 8/8) a run |
| Web CI / Python Tests | On web/** / scripts/** change | Type check, tests, build and output checks |
| Release Desktop / Android | On desktop-v* / android-v* tag | Build the apps into a draft release |
| Announce Discussion | On release published |
The site itself is deployed by Cloudflare Workers Builds on every push to main, not by a workflow.
New games arrive on their own: Discover New Games sweeps the Steam store
daily into a review queue, and the maintainer approves them at /admin — by
hand, or with an auto-approve rule that has a daily cap and is off until turned
on. Discovery only proposes, and every approved game is checked again by the
pipeline (free, a game, reachable) before it is published. Missing a free game
a few days after it went free? Post its store link in
Discussions › Ideas.
The companion browser extension, steam-f2p-extension, is archived and no longer a way in.
Issues are for reports, not submissions: Bug report, Feature request, Delete game and Feedback. All reviewed by hand.
The manual way. Fork, append store links to scripts/temp_info.jsonl, and open a pull request:
{"link": "730"}
{"link": "https://store.steampowered.com/app/570/"}
Accepts a full URL, short URL, or bare appid. A line is a link and nothing else: any other key is ignored, and the game is fetched from Steam. A correction to a published game (genre, anti-cheat, notes…) goes in a Discussion; the maintainer records it as an override.
See CONTRIBUTING.md for the full flow.
Unemployed, introvert max level, dropped out uni year 3, mooching off family. Needed a clean F2P list without digging Steam every time "hôm nay chơi free cái gì đây". So this repo was born — hobby project of a "barely competent" dev pumped by AI.
[!WARNING] Disclaimer. Quality / fun / safety: who knows. The maintainer only vouches for games personally played (
safecolumn:y= yes,?= dunno,n= nope). Problems with a game are between you, Valve, and the developer of that game — not the maintainer. Seedocs/DISCLAIMER.mdfor the specific accuracy caveats.
Code is MIT; the dataset is CC BY 4.0 with carve-outs for fields the project does not own (description, header_image). Keep the right licence with you if you fork. Star if you find a hidden gem ✨
Last auto-update: daily via GitHub Actions. Manual runs from the Actions tab.
Auto-create a GitHub Discussion in Announcements |
/admin.web/README.md — stack details, per-phase changelog, build instructions.