Show HN: Cardstock- Free TCG Proxy Manager for Magic, Yugioh, & Pokemon
Trading cards are awesome, but paying $30 for some cardboard isn’t. I’ve upscaled 60,000 cards from the entire catalog of Yugioh, Magic, Pokemon, & a newer game, https://elestrals.com. I've made it easy to build a decklist, download it, and then print at home. Modern inkjet printers got really good when nobody was looking. While it’s clear they’re not real cards, the upscaling makes them look great for casual play (these are not tournament legal). It’s totally free, give it a try!

Supplies: https://cardstock.denta.co/kb/supplies
Printer Settings: https://cardstock.denta.co/kb/settings
Instructions: https://cardstock.denta.co/kb/instructions

Overview:
I built Cardstock because I had some scripts to do this lying around, and wanted to explore the new Rails 8 magic. Kamal 2 (kamal-deploy.org/) is a game changer, SQLite in production is fine, and the database backed solid family of gems work like a charm.

Compute
I am renting a box on https://hetzner.com located in VA for $15/mo. This box has 8 gigs of ram and 2 vCPU's. This is such a deal compared to compute prices on https://render.com.

Kamal 2
This thing is amazing. Kamal gives me everything I could want (easy console access, easy shell access, a way to manage secrets, a way to see my logs, and letsencrypt support for DNS), all without a PaaS tax. The best part is the accessories feature: https://kamal-deploy.org/docs/commands/accessory/. I am running my main app with two accessories: Meilisearch(https://meilisearch.com) and OpenObserve (https://openobserve.ai). Instead of paying Algolia to host search infrastructure and sentry to host monitoring infrastructure, I’m hosting my own OSS without any fanfare.

Upscaling:
To upscale the trading cards (a mandatory part of this build, scans are never high enough DPI). I am using this (https://replicate.com/nightmareai/real-esrgan) model. For upscaling every card, I've used under a hundred bucks of compute. This model was picked on a whim, but worked well enough that I didn’t compare other models.

I used SQLite combined with Litestream (litestream.io) for my database. While I considered Postgres, I hesitated due to uncertainties around handling backups on self-hosted infrastructure. This was my first time using SQLite in production, and it was functional but with some minor annoyances. Here’s what I encountered:
1. No Default UUID Primary Key Type
I had to set primary keys as strings and assign IDs manually from the application record. It’s an annoying workaround but manageable.
2. No Native Array Columns
Because SQLite doesn’t support array columns, I had to use its native JSON column type, which just felt icky. If I were working with something like embeddings, this would be especially annoying, because you couldn’t enforce all the records to have the same number of dimensions.
3. Cryptic Errors
At one point, a migration failed silently, leaving a cryptic error in schema.rb. The issue was resolved by rolling back the migration and redoing it, but it was once again, annoying.
4. Litestream Defaults
Litestream deletes snapshots after 24 hours by default, which is far too short. When I tried to recover some data, I found it had already been deleted. Adjusting these defaults fixed the problem.

Solid Queue/Cache/Cable
The solid family of gems are all backed by the database and were a pleasure to work with. Goal was to prevent needing to reach for redis, so you have one less thing to worry about. You end up with a little more latency, which is a totally reasonable tradeoff.

Conclusions
We are moving into a post platform as a service world. Instead of buying a bespoke render.com or heroku, you just buy commodity compute and use Kamal to manage. It's like, pretty much all there, excited to see how this space matures.