DEV Community

Cover image for I built a dating app that runs in your terminal and it's backend-less
vutr
vutr

Posted on

I built a dating app that runs in your terminal and it's backend-less

Every developer I know loves working in the terminal, and they're some of the nicest people around.

In Vietnam, the news keeps running stories about our declining population, and the solution most people reach for is Tinder, or Bumble, or FB Dating - the first 2 charge you money for the privilege of being their product.

The CLI trend is obvious - it helps integrating LLM/AI a breeze. I love CLI tool. They are lightweight, smooth and look slick.

Well, I guess there is something to do about it: combine all the ideas above, build a proper dating app for our beloved software developers that run in their terminal.

Will it amount to anything? I don't know. But the engineer spirit is that, nothing is for sure, we just have to give it a try.

The Constraint That Shaped Everything

The first constraint was simple: I had almost-zero budget for infrastructure. Not that I can't afford them, but ideally the expense should be minimal - given the wars are out there and gas price keeps skyrocketing.

No VPS, no managed database, no monthly bill. But a matching platform needs a backend, and a database — somewhere to store profiles, run validation, process matches. I get that well, it's just, they shouldn't cost a leg and an arm for a free-for-community project.

I spent a while thinking about what services in the market that could fill those roles, and eventually it hit me: GitHub gives you all of it for free.

  • a repository is basically a storage
  • github actions is event-driven compute, basically like AWS Lambda, serverless, running on demand.
  • Issues with labels function like API endpoints with different routes.
  • Releases work as a CDN.

They are just all there.

When I mapped the architecture onto GitHub's primitives - well we can make registration an Issue or a PR, profile validation and encryption is an Action, the discovery index is a release asset. It sounds absurd, but it should work - since dating/matching app aint like real-time required. Nobody needs sub-millisecond latency to find out someone liked them back.

The Scraping Problem

The most fundamental feature of a dating app is Discovery. Normally it would require a centralized db and backend to do that.

But, for the same reason of cost, what if we can let user machine run that in decentralized manner? What if we let user download the database to their machines and run ranking/matching/whatever-ing on it?

There is a dilemma here:

  • Privacy matters - people profiles must not be exposed freely.
  • Discovery feature can't function with encrypted data.

Also, if the db is downloadable, then discovery might not even be necessary anymore - anybody can load the whole thing and start scrapping it in sub-seconds.

Traditional dating app use centralized server, private database, private ranking/suggestion engine and API rate-limiting to ensure end-users cant do that.

We are not doing centralized server approach for sure. Whats fun in doing that?

I guess, this is where cryptography come for rescue. Traditional rate limiting is policy, not physics — bypass the auth and you scrape everything. I wanted limits that were physically impossible to bypass, and after a week exploring verifiable delay functions and proof-of-work schemes, I decided on something which I call chain-encryption:

  • Imagine a chain of locked boxes where each contains a profile and a hint to open the next one. Opening one box takes a few seconds of brute-force computation with no shortcuts.
  • A legitimate user sees one profile every few seconds — human speed. An attacker is stuck at the exact same pace, with no premium tier to remove the limit because the cost is baked into the math.
  • There should be a caching mechanism so once you've seen a profile before, future encounters should feel almost instant — the system naturally rewards normal usage and punishes bulk extraction.

I think it works well enough in this case. The design is a lock-combination with AES Encryption.

Privacy by Architecture

Every user gets three separate hashes for registration, discovery, and chat, connected by a secret salt that only exists in the pool's GitHub Actions secrets. Without it, the namespaces are completely unlinkable — browsing profiles reveals nothing about GitHub usernames, and chat partners can't find each other in the index.

id_hash    = sha256(pool:provider:user_id)     64 hex chars
bin_hash   = sha256(salt:id_hash)[:16]         16 hex chars — .bin filename
match_hash = sha256(salt:bin_hash)[:16]        16 hex chars — chat handle
Enter fullscreen mode Exit fullscreen mode

The chat relay takes this further: it's unable to spy on users. Messages are encrypted before leaving your machine, authentication is a stateless time-based signature plus Merkle-like verification, and the relay routes ciphertext it cannot read. Compromise the server and you get encrypted blobs — there's nothing to steal.

Why the Terminal

Every developer already lives here, and tools like Claude Code have made that lifestyle even more appealing. A single binary, no app store, no web hosting, two commands:

brew install vutran1710/tap/op
op
Enter fullscreen mode Exit fullscreen mode

The TUI handles onboarding, discovery, and chat with a proper dark-themed interface. The Go ecosystem — Bubbletea and Lipgloss — deserves far more attention than it gets.

What eventually work & their limitations

┌──────────┐       ┌──────────────────┐       ┌─────────────┐
│  CLI/TUI │──────▶│  Pool Repo       │       │  Registry   │
│  (Go)    │       │  (GitHub)        │◀─────▶│  (GitHub)   │
│          │       │  pool.yaml       │       └─────────────┘
│          │       │  users/{h}.bin   │
│          │       │  matches/        │
│          │       └──────────────────┘
│          │              ▲
│          │──────▶┌──────┴───────────┐
│          │       │  Relay Server    │
└──────────┘       │  (per-pool)      │
                   └──────────────────┘
Enter fullscreen mode Exit fullscreen mode

Eventually the relay is not avoidable, meaning the whole system is not 100% backend-less in traditional way. But it's still quite minimal: github as db, actions as main backend, the relay is only thing someone has to pay for.

The relay server is basically a dumb, stateless message routing backend. By the nature of Golang, I believe it can handle the load pretty well & inexpensively until the pool operator realizes he can actually monetize his own pool.

The "grinding" delay during discovery actually makes browsing deliberate — you can't mindlessly swipe when each profile costs a few seconds.

Finally, the matching engine turned out to be general-purpose by accident: the schema is just YAML, so anyone could fork the template and run a job board, study group finder, or freelancer marketplace with zero code changes.

name: "<3 dating"
description: "terminal-native dating"
relay_url: wss://relay.example.com
operator_public_key: c251e2cf...

profile:
  age: { type: range, min: 18, max: 100 }
  interests: { type: multi, values: hiking, coding, music }
  about: { type: text, required: false }
  phone: { type: text, visibility: private }

roles:
  - man
  - woman
Enter fullscreen mode Exit fullscreen mode

So, this thing may or may not be a final product - I believe a mobile client is still needed for non-Github users. But it's still fun exploring whats possible and what not, and to remind ourselves that sometimes, what we have been looking for is just already right in front of us. All we just need to, well, think hard about whether paying is neccessary.

Where?

MIT licensed, fully open source: github.com/vutran1710/openpool


Tags: #go #golang #opensource #cli #terminal #cryptography #decentralized #privacy #sideproject

Top comments (0)