A single-file, config-driven rating/labeling server — tap-to-score from your phone, results to JSON.
Project description
siftrate
Rate a pile of things from your phone. One Python file, zero dependencies.
Point siftrate at a JSON config describing your items and a scoring scheme. It serves a mobile-first tap-to-score UI on your own network and writes every rating to a JSON file you own. No database, no Docker, no account, no build step — just the Python standard library.
python3 siftrate.py --config examples/blog-post-drafts.json
# open the printed URL — tap, score, done
Or, once published to PyPI:
pipx run siftrate --config myreview.json
Why this exists
I built a throwaway script one evening to rate a batch of Substack posts from my phone — and kept reaching for it. Rating job applicants' take-homes. Triaging a photo dump. Scoring blog drafts. Every "look at N things and record a judgment on each" task turned out to be the same tool. So I cleaned it up.
The insight worth keeping: most rating jobs don't need an annotation platform. They need a list, a scale, your thumb, and a results file.
What it does
- Config in, JSON out. Describe items + a labeling scheme in one JSON file; get one merged results file back. Both are yours, both are plain JSON.
- Phone-first UI. Big tap targets, one item per screen, progress bar, swipe-free navigation. Built to be used with a thumb on a couch, not a mouse in a lab.
- Scale + flags + notes. A numeric scale (with optional captions like "poor"/"great"), multi-select flag toggles, and a free-text note — any combination, per config.
- Resumable + crash-safe. Results are merged and rewritten atomically on every tap. Kill it mid-review, restart, and you're exactly where you left off.
- Private by default. Binds
127.0.0.1unless you say otherwise. No telemetry, no outbound calls, ever — verify it yourself, it's one file.
Quickstart
- Copy
siftrate.pyanywhere (orpip install -e ./pipx run siftrateonce published). - Write a config (30 seconds):
{
"title": "Rate the blog post drafts",
"items": [
{"id": "draft-01", "title": "The Hidden Cost of Cheap Hosting",
"meta": "1,450 words · draft v2", "body": "Opens with a downtime horror story…"}
],
"labels": {
"scale": {"min": 1, "max": 5, "captions": {"1": "kill it", "5": "publish as-is"}},
"flags": ["needs edit", "off-brand"],
"note": true
},
"output": "results.json"
}
- Run it, open the URL, tap through the pile.
- Read
results.json:
{
"draft-01": {"score": 4, "flags": ["needs edit"], "note": "tighten the middle",
"updated_at": "2026-07-02T01:00:00Z"}
}
Three ready-to-run examples live in examples/: blog-draft review,
photo-dump triage, and take-home grading.
Config reference
| Key | Required | What it does |
|---|---|---|
title |
no | Page heading |
items / items_file |
yes (one of) | Inline array, or a path to a JSON array |
items[].id |
yes | Stable key in the results file |
items[].title / body / url / meta |
no | What renders on the card (body takes text or HTML; url links the title) |
labels.scale |
no | {min, max, captions?} numeric scale |
labels.flags |
no | Array of toggle labels |
labels.note |
no | true for a free-text field per item |
output |
yes | Results JSON path (merged, atomic writes) |
host / port |
no | Default 127.0.0.1:8091 |
CLI flags override config: --config (required), --host, --port, --token.
Rating from your phone (the security model)
siftrate binds localhost by default, which is safe and useless for thumbs. To rate from your phone, widen the bind to a network you trust — and gate it:
- Best: a tailnet. Run it on any machine on your Tailscale
network,
--host <tailscale-ip>, open the URL on your phone. Encrypted, invite-only, zero exposure. - Trusted LAN:
--host 0.0.0.0 --token some-secret— the token gates every route (link becomeshttp://…/?token=some-secret, which the UI persists). - Never expose it to the open internet. It's a review tool, not a web service.
Rule of thumb the tool enforces in its help text: widen the bind → bring --token.
Why not Label Studio / Argilla / Prodigy?
Those are annotation platforms — servers, databases, users, project models, ML tooling. Genuinely great when you're labeling training data at scale with a team.
| siftrate | Label Studio | Argilla | Prodigy | |
|---|---|---|---|---|
| Install | copy 1 file | Docker + Postgres | server + client | paid license |
| Time to first rating | ~30 seconds | an afternoon | an hour | an hour |
| Phone-first UI | yes | no | no | no |
| Output | one JSON file | export pipeline | dataset API | dataset |
| Dependencies | zero | many | many | many |
| Team workflows / ML loops | no | yes | yes | yes |
If you need inter-annotator agreement, active learning, or ten reviewers — use a platform. If you need your own judgment recorded over a pile of items before dinner — that's this.
Development
python3 siftrate.py --config examples/photo-triage.json # run from source
pip install -e . && siftrate --help # entry point
Zero runtime dependencies is a feature, not an accident — contributions that add one need a very good story.
License
MIT © Toby Rosen
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file siftrate-0.1.0.tar.gz.
File metadata
- Download URL: siftrate-0.1.0.tar.gz
- Upload date:
- Size: 12.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a21eda7ebba62e4369c137d86e62d1f03edb27d901b27f981ca709478e67d795
|
|
| MD5 |
44a3a9a9de82a6c997222eded6ed81f6
|
|
| BLAKE2b-256 |
59570d193a064261f6bcb0ffeef41756a91fbfa8ae7749dad4fd1472b881621b
|
File details
Details for the file siftrate-0.1.0-py3-none-any.whl.
File metadata
- Download URL: siftrate-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2818dd55b8ec03ff4f5e8dabccfa86b5a8d450040a815884fc6cfa69c0ffa3c
|
|
| MD5 |
e2ae6434fef3b755e83df7ffeb2f24c6
|
|
| BLAKE2b-256 |
008f7fb5a0a8fab190ec000bae0a254e41e734f71d9d61d8023e3e5029f05dea
|