DEV Community

Azizjon
Azizjon

Posted on

Things that broke in my side project that no test caught (a hosted log service, built solo)

I've been building a small hosted log service on the side. Your program sends plain-text log lines over HTTPS, you read them in a dashboard, get a Telegram message when a run gets noisy, and can ask an AI assistant about them. It runs on a single 2 GB VPS.

The parts that went wrong were never where I expected. A few, in case they save someone an evening:

1. Floats in a webhook signature. The payment provider signs its webhook with an HMAC over JSON.stringify of the sorted payload. Python's json.dumps writes 0.00005 as 5e-05; JavaScript writes 0.00005. That happens to be the price of the cheapest plan in BTC, so the check would have failed for exactly those payments. A code review caught it. The fix was porting ECMAScript's Number::toString and checking it against Node on 20,000 random numbers.

2. A page that was quietly too wide. On phones, two pages were up to 200 px wider than the screen, and since the body had overflow-x: hidden, the right edge was simply cut off instead of scrolling. The cause: a CSS grid column of 1fr can't shrink below its content, and a long log line is wide content. minmax(0, 1fr) fixed it. I only found it because a screenshot script now flags horizontal overflow at 390 px.

3. Count usage before the work, not after. AI messages were checked against the daily limit before the request and counted after the reply. Parallel requests could all pass the same check, and closing the tab just before a streamed answer finished got it for free. Now credits are reserved first with one conditional UPDATE and refunded only if no answer was produced.

4. Logs are text other people write. Once an LLM reads them, a line in a log can tell it what to do. In testing, one model ended a correct answer with a link that had the database password from the logs in its URL. I wrote up the whole red-team pass, including the hole that's still open: https://jl3-cloud.site/blog/prompt-injection-through-log-files/

5. Boring infrastructure has been fine. One VPS, systemd, nginx, PostgreSQL and Redis, with deploys by git push to a bare repo and a post-receive hook. No containers in production, and nothing I can't hold in my head.

6. Docs get read by AI assistants now. Every docs page is also served as plain Markdown, plus an llms.txt, because a lot of people ask an assistant how to use a tool before they open the docs.

A question for people who've run free tiers: the free plan here is 5,000 log lines a day, kept for 2 days. Is that enough to try something like this on a real side project, or does it feel stingy?

It's at jl3-cloud.site if you want to poke at it.

Top comments (3)

Collapse
 
dev_in_the_fog profile image
Jason Y. (dev_in_the_fog) •

That point regarding "Top comments (0)" is really practical. Having concrete failure scenarios and reproducible steps makes dealing with these kinds of edge cases so much clearer. Appreciate you documenting this!

Collapse
 
dev_in_the_fog profile image
Jason Y. (dev_in_the_fog) •

The float serialization difference between Python's json.dumps (5e-05) and JS Number::toString (0.00005) for HMAC webhook verification is an incredible catch. Subtle cross-runtime canonical JSON formatting bugs like that are almost impossible to catch in isolated unit tests without synthetic cross-language fuzzing.

Also big +1 on reserving AI quota before streaming. We hit the exact same race condition where users would open multiple tabs or abort their SSE stream mid-flight to bypass rate limits. Reserving upfront with a conditional UPDATE then reconciling upon stream close is the only robust pattern. Awesome write-up!

Collapse
 
sgaggjhkjh profile image
Arjun Sharma •

The float serialization bug is a classic. I have hit the same class of issue with cross-language signature verification, and now I always serialize from a single canonical source. Reserving AI quota before streaming is the kind of thing nobody remembers until a race bites them.