I spent an evening pulling apart what makes Vibestead tick, and the thing that stuck with me wasn't the lumber mill or the auto-dispatching managers. It's a single design decision buried in the feature list: when you connect the CLI, it syncs your AI coding usage and gives you in-game diamonds, but the uploaded payload is a daily usage summary only. No code. No chat transcripts. The pixel village is the fun part. The restraint in that one sentence is the durable part.
That restraint is worth copying on its own, detached from any game. If you've ever wanted a private streak tracker for your own coding habit, or a small shared leaderboard for a team that nobody's legal department will panic about, the hard problem isn't the UI. It's proving that you never touched anything sensitive. So let me walk through rebuilding that half on VicroCode: an ingest endpoint that physically cannot accept anything but counts, a ledger you can open and correct by hand, and a board you load from a URL.
The honest boundary: the counts come from the client, not from us
Before any code, the part most people get wrong. VicroCode cannot reach into Codex or Claude Code and read how much you used them. Nothing hosted on a server can, unless those tools expose something to it. The daily total has to be computed on your own machine, by your own script, and then sent in. That's not a limitation to apologize for. It's the whole privacy story. The client decides what a "usage total" even means, strips everything else, and posts a single number.
Vibestead's model lines up with this exactly: it says it supports connecting tools like Codex and Claude Code and uploads the aggregate, keeping code and chat bodies out of the transfer. I can't verify the internals of their CLI (unverified), but the contract they describe is the one I'd build against. Whatever local script you use to tally your day is yours to write and inspect. The server's job starts only when a clean number shows up at the door.
So the architecture splits cleanly:
- On your machine: a small script tallies a daily number and POSTs it. You own this. You can read every line.
- On VicroCode: an endpoint that refuses anything that isn't a date plus integer counts, writes them to a ledger, and renders a board.
Everything below is the second half. The first half stays with you by design.
An ingest endpoint that refuses everything but counts
The trick to a privacy-safe reporter is to make bad input impossible to store, not just discouraged. The endpoint parses a tiny fixed schema and drops the rest on the floor before it ever reaches the database. If someone accidentally pipes a file or a prompt into it, the request fails validation and nothing lands.
Here's the shape of it. You can run Python online for this and expose it as a hosted endpoint so your local script has somewhere to send the daily total.
import json
import sqlite3
import datetime
# Only these keys are ever read. Anything else is ignored.
ALLOWED = {"day", "sessions", "accepted_suggestions", "total_requests"}
def handle(request_body: str) -> dict:
try:
raw = json.loads(request_body)
except json.JSONDecodeError:
return {"ok": False, "error": "body must be JSON"}
if not isinstance(raw, dict):
return {"ok": False, "error": "body must be an object"}
# Whitelist: pull only the keys we recognise, discard the rest.
clean = {k: raw.get(k) for k in ALLOWED}
# day must be a real ISO date, not free text.
try:
day = datetime.date.fromisoformat(str(clean["day"]))
except (TypeError, ValueError):
return {"ok": False, "error": "day must be YYYY-MM-DD"}
# Every count must be a non-negative integer. No strings, no floats.
counts = {}
for key in ("sessions", "accepted_suggestions", "total_requests"):
val = clean[key]
if not isinstance(val, int) or isinstance(val, bool) or val < 0:
return {"ok": False, "error": f"{key} must be a non-negative integer"}
counts[key] = val
store(day.isoformat(), counts)
return {"ok": True, "day": day.isoformat()}Notice what this does and doesn't do. There's no free-text field anywhere. No `notes`, no `snippet`, no `repo`. If your local script tries to attach a code sample, there's simply no key to put it in, and `clean` never carries it forward. The schema is the privacy policy. That's the part worth stealing from Vibestead, expressed in code you can read in thirty seconds.
One thing I won't gloss over: an endpoint on the open internet needs an auth check, even for something this small. Without one, anyone who finds the URL can spray fake totals into your ledger. Add a shared secret header and reject requests that don't carry it. VicroCode gives you in-platform tool calls and API endpoint hosting, so the check lives right next to the handler:
def authorized(headers: dict, expected_token: str) -> bool:
return headers.get("x-report-token") == expected_tokenKeep that token out of the client code you share and out of any committed file. It's the difference between a private ledger and a public dumping ground.
A SQLite ledger you can open and fix by hand
The storage should be boring on purpose. One table, one row per day, counts as integers. No audit of what you were working on, because there's nothing to audit.
def store(day: str, counts: dict) -> None:
conn = sqlite3.connect("usage.db")
conn.execute(
"""
CREATE TABLE IF NOT EXISTS daily_usage (
day TEXT PRIMARY KEY,
sessions INTEGER NOT NULL DEFAULT 0,
accepted_suggestions INTEGER NOT NULL DEFAULT 0,
total_requests INTEGER NOT NULL DEFAULT 0
)
"""
)
conn.execute(
"""
INSERT INTO daily_usage (day, sessions, accepted_suggestions, total_requests)
VALUES (:day, :sessions, :accepted_suggestions, :total_requests)
ON CONFLICT(day) DO UPDATE SET
sessions = excluded.sessions,
accepted_suggestions = excluded.accepted_suggestions,
total_requests = excluded.total_requests
""",
{"day": day, **counts},
)
conn.commit()
conn.close()The `ON CONFLICT` means a re-sent day overwrites rather than duplicates, which matters because real clients retry. And because this is plain SQLite, you get something a hosted black box never gives you: the ability to correct the record yourself. If a laptop was offline for two days and your script double-counted on the third, you open the SQLite editor, find the row, and fix the number. A streak tracker you can't repair is a streak tracker you'll stop trusting the first time it's wrong.
That hand-editability is also a quiet privacy feature. You can see the entire contents of what's stored about you at any moment, in one table, in one place. Nothing hidden.
A streak board you open from a URL
The reading side is a single HTML page. It fetches from a small read endpoint and draws the streak. No build step, no framework ceremony. You get web app hosting for the HTML, publish it, and bookmark the URL.
def recent(days: int = 30) -> list:
conn = sqlite3.connect("usage.db")
conn.row_factory = sqlite3.Row
rows = conn.execute(
"SELECT day, sessions, accepted_suggestions, total_requests "
"FROM daily_usage ORDER BY day DESC LIMIT ?",
(days,),
).fetchall()
conn.close()
return [dict(r) for r in rows]The front end stays deliberately thin:
<!doctype html>
<html>
<head><meta charset="utf-8"><title>Coding streak</title></head>
<body>
<h1>Daily coding streak</h1>
<div id="board">loading...</div>
<script>
fetch("/api/recent")
.then(r => r.json())
.then(days => {
const board = document.getElementById("board");
if (!days.length) { board.textContent = "No days logged yet."; return; }
board.innerHTML = days.map(d =>
`<div><strong>${d.day}</strong> — ${d.sessions} sessions, `
+ `${d.total_requests} requests</div>`
).join("");
});
</script>
</body>
</html>Computing the current streak is a few lines over the returned dates: walk backward from today, count consecutive days that have a row, stop at the first gap. I left it out of the markup so the privacy-relevant part stays obvious: the page only ever sees counts and dates, because that's all the ledger holds.
Why this pattern travels well right now
Look across what independent builders are actually shipping and a theme shows up: small, sharply scoped tools where the author is upfront about exactly what the thing does and doesn't do. One-time credit purchases instead of subscriptions that quietly expire. Browser-first so there's nothing to install. Honest notes about limits rather than polished overclaiming. Vibestead sits right in that current, and its "we upload the summary, not your code" line is the same instinct applied to privacy.
If you're building for other developers, that instinct is leverage. A usage reporter that can demonstrably only hold counts is far easier to get adopted than one that says "trust us." The schema is the proof. You hand someone the twenty lines of validation and they can see for themselves that there's no field for anything sensitive.
A few places this gets interesting if you keep going. You could publish and share the board as a tiny team leaderboard, where each person's local script posts their own total and nobody's code ever leaves their machine. You could layer a LanceDB knowledge base on top if you later want to search your own notes about productive days, kept entirely separate from the counts ledger. Those are extensions, not the core. The core is the discipline: decide what the minimum honest payload is, make everything else structurally impossible to store, and keep the data where a human can read and fix it.
That's the half of Vibestead worth rebuilding. The village is charming. The boundary is the engineering.