we put the final coat of paint on a house we were about to move out of. that’s the joke, and i’m the one who lived it. in the last couple of weeks before dumpsterChat got handed off to rocket chat, i did three of the most productive and honestly most frustrating things of the whole project: i hardended it like it was production software, i shipped a mobile app for it, and i chased down a single bug that made me seriously question whether owning a chat platform was a healthy hobby. that bug, more than anything, is what finally tipped me over.
so this is the last stand. it pairs with the sunset post i put up. if that one was about why i stopped, this one is about what happened on the way out.
the security pass
the first few months of dumpsterChat, i was building features at a pace that would have made a security auditor weep. sessions, bots, auth, file uploads, all running on a box i was reconfiguring weekly. around mid-july i sat down and did the pass i kept promising to do:
- tokens are hashed at rest in the database now, instead of sitting there in plain text waiting to be leaked.
- the tauri client got a proper content security policy so the desktop app isn’t a free pass for any script it loads.
- an xss guard on the message rendering side.
- coturn got pointed at the live letsencrypt certs instead of a stale self-signed bundle.
- secrets came out of tracking: api keys into the environment, an
.env.example, no hardcoded credentials in the repo. - the container got a non-root user, network isolation, a
.dockerignore, and a deploy rollback. - a rate limiter with a cleanup goroutine and database pool constraints, so a dozen friends can’t accidentally melt postgres.
it was a genuinely satisfying week. dumpsterChat went from “homelab toy” to “service i could hand to someone and not be embarrassed about.”
we shipped a mobile app
yes. a mobile app. for a chat server with twelve registered humans. the tauri client went from v0.2.7 up through v0.2.10, with real version codes and everything. i fixed the android safe-area spacing so the login form didn’t hide under the notch, made the toolbar icons actually tappable, sorted out the passkey text, and got the debug symbols structured properly so the ipa would actually submit to the play store.
and the icon is a dumpster fire. of course the icon is a dumpster fire. a cheerful little cartoon dumpster fire, right there in the launcher, daring you to question the naming.
here’s a confession: finish a client app right before you kill the server is a weird feeling. i built it because i wanted the crew to have a real app on their phones, and it was good work. and it was also, in retrospect, a small monument to not wanting to let go.
the horrible bug
this is the one that broke me. it started the day after the security pass, innocently enough. someone pinged the chat and it looked fine, but under the hood the websocket connection was dying constantly. every client would connect, and within about five seconds the socket would silently drop, and the client would immediately try to reconnect. connect, drop, retry. connect, drop, retry. all day.
the worst part was the lie. the ui would happily render “connected” while the socket was already dead in the water, and nobody could tell until a message never arrived.
i chased it for a while, reading the gateway code, checking the client. then it clicked. the security pass had started hashing session tokens at rest in the database. but the websocket handler, the one that authenticates every socket connection, was still querying the sessions table with the raw token. so every single websocket auth lookup was comparing a hashed value against an unhashed one, finding nothing, and rejecting the connection. the rest api went through the shared helper that hashed tokens, so it worked fine. the websocket path went around it, so it failed every time. one change, two code paths, one of them never got the memo.
the fix was one line: hash the token in the websocket auth path too. but that one line took me a whole evening to find, because the poison was in a path i’d stopped thinking about.
the rest of that day was a cleanup of every other seam the same class of bug could live in. i normalized channel and server ids to lowercase across the stores and the websocket layer so “ABC” and “abc” couldn’t refer to the same room differently. i fixed a date-parsing bug that would plonk a brand-new message at the top of your history instead of the bottom. i made sure the spa index wasn’t served stale, and that the caddy proxy preserved the /api/v1 prefix. i added reconnect backoff so a flaky socket didn’t hammer the server.
every fix was small. and there was always another one.
the honest takeaway
that day is when the decision started to form. not because the bug was hard. it was because it was endless. each fix was correct and careful and satisfying, and the next morning there’d be another one waiting. that is what owning a chat platform is. not the fun build. the endless, correct, lonely maintenance.
you harden it, you ship the mobile app, you find the one-line bug that was eating your sockets, and then you look at the calendar and realize you’ve spent fourteen weekends and you still haven’t touched the voice roadmap. that’s not a failure of the project. it’s a genuinely accurate accounting of the cost. and that’s the number that made sun-setting it feel less like giving up and more like doing the math.
the work wasn’t wasted. the security patterns, the tauri packaging, the lesson about auditing every auth path when you change how you hash credentials, all of it carries into the next thing. i built dumpsterChat to learn. i did. the final sprint just taught me the hardest lesson of the lot.
if you’re running your own project and you’re stuck in the same endless-correct-maintenance loop, take the note: finishing the polish isn’t the same as the project being done. sometimes done is handing the keys to someone whose maintenance bill is lower than yours.
we got the dumpster fire onto a dozen phones and then we put it out. worth every weekend. and now the kettle’s warm somewhere else.
what’s the project you poured the most polish into right before you walked away?
-dustin