Skip to content
Ricklayer
RUEN
← All work

04 · A marketplace for life in Serbia, in 12 languages

Kvart

A marketplace for life in Serbia, in 12 languages

Housing, goods, local pros and services for people who don't read Serbian. Our own marketplace with listings, chats, identity checks, payments and an AI assistant: 12 languages, 73,936 lines of TypeScript in 3 weeks and 741 tests, attack tests included.

An icon set of our own

For the marketplace we designed 126 icons in one style: 3D ones for sections and the catalog in the brand blue and line icons for the interface, from the feed to the account.

Icons from the Kvart app

0

3D icons for sections and the catalog

0

line icons for the interface

How the icon system was built

It started with a short brief: icons for four sections. Instead of drawing them one by one we proposed a system: a shared palette, material and camera, one prompt where only the subject changes, and a mascot we built in Blender. That is how the set grew to 126 icons and stayed coherent.

Prompts from the Kvart repository

One prompt for every icon

Asset type: category icon for Kvart marketplace. Subject: a small sculptural apartment building, 3 stories, rounded cobalt blue facade, four pale lavender windows, warm apricot entrance. Unified brand family: soft rounded geometry, matte molded surfaces, cobalt blue #3b60e4, pale lavender #d9d8f2, warm apricot #f3b58d. Recognizable at 100px, slight elevated front three-quarter camera, 80% of a square canvas, pure white background. No text, logos or watermark.

Only the subject changes: style, palette, camera and framing are shared, so a new icon fits the set on the first try

#3b60e4#d9d8f2#f3b58d

Kvartik: try it yourself

We prepared and trained an AI assistant for Kvart: a character, a knowledge base on Serbian law, and skills to count days of stay and search the marketplace. Below is its interface as it runs in production: pick a question or write your own.

Interface from the Kvart app, answers in the demo are a stub

Kvartik

Hi! How can I help?

Your helper for life in Serbia: counts visa-free days, finds homes, things and pros on Kvart, and explains the law with links to sources.

Kvartik helps you figure things out but does not replace a lawyer or the police. This is a test environment, the live Kvartik is on kvartrs.com.

Kvartik keeps learning

At its core is a model trained on huge amounts of text. On top of it we gave Kvartik the character and knowledge of Kvart, and now it learns from users' conversations: with their consent, from good and bad ratings, with no names, emails or numbers.

The screen shows fine-tuning on a Mac Studio; the numbers in it are made up

Mac Studio M5 Ultra

Our ML engineer trains the model

The process is ours: a dataset only with consent and without personal data, fine-tuning on a Mac Studio, a judge checks the result. The numbers on screen are made up

Volume of work and tests

In three weeks, almost 74 thousand lines. No release reaches the server until it passes the whole round of checks: from scanning the code for keys to a browser and attacks on the built site. The screens show how the work goes: a layout, tests with scanners and mutations, the server, and how it all comes together and ships through two clean rounds.

The scenes on screen are close to real work, the data in them is made up

Designer and 3D artist
Developer and QA
DevOps engineer

The scenes on screen are as close to real work as we can show. We can't show the real tests and server, so the data here is made up: it differs from the real thing only slightly

The task

Expats and tourists in Serbia look for housing and services on local sites in Serbian. Language becomes the main barrier, and listings are hard to trust.

The task: a marketplace of our own where everything, from the feed to the chat, is in the person's language, and owners are verified.

Slice by layer

Interfacethe pixel
A light site in brand blue, mobile first, search in plain words, chat like a messenger, a dark moderator workspace
Logicmoney and rules
Housing, goods, pros, transfers, moderation with reasons in the owner's language, paid promotion, an AI assistant
Dataschema and migrations
54 tables, 72 SQL migrations, listing translations stored separately, personal fields sealed with encryption
Serverdeploy and operations
Own server in Amsterdam, Caddy and PostgreSQL on localhost, one-command deploy with auto-rollback, nightly copies
Securitythe kernel
12 threat classes per decision, mutation and attack tests, admin on its own host behind a second factor

12 languages, no compromises

Interface, listings and emails in the person's language: from Russian and Serbian to Chinese.

  1. 01Interface strings for all 12 languages live together, a test checks keys and placeholders across every file
  2. 02Listings are translated into a separate table and shown in the original when no translation exists
  3. 03Event emails go out in the recipient's language and without chat text: only the event and a link

Kvartik, the AI assistant

Answers questions about life in Serbia, counts visa-free days and searches the marketplace in plain words.

  1. 01Claude via the Anthropic Messages API, its own character and rules, streamed answers
  2. 02Laws and deadlines only from a knowledge base built on primary sources, every point linked to an article
  3. 03Skills on our own code: a stay-days calculator and search across sections with cards
  4. 04The cost of every answer is metered in millionths of a dollar: 15 free questions a month, 6 a minute, a daily spending cap
  5. 05Injection defence: the model role cannot be forged, listing text is never treated as instructions, cross-site requests are rejected

Personal data only as ciphertext

Even with a database dump in hand, emails, phones and chats stay unreadable.

  1. 01Every personal field is written as kv1. + AES-256-GCM with a random nonce
  2. 02A table.column label is part of the authentication: a ciphertext from one column will not open in another
  3. 03Emails are looked up by an HMAC-SHA256 fingerprint, keys are derived from one master key via HKDF
  4. 04An ORM column type encrypts and decrypts, the app code works with plain text
  5. 05End-to-end chat encryption was rejected on purpose: it breaks reports, moderation and Kvartik's help

Server and deploys

Production built from scratch and closed so that breaking the app gives neither the code nor root.

  1. 01Code belongs to the deploy user, the app runs as a system user without a shell or sudo and writes to three directories
  2. 02systemd sandbox: strict system protection, private /tmp, no capabilities, a syscall filter, a memory ceiling
  3. 03Deploy: checks → rsync → DB snapshot → migrations → build → release switch. No answer within a minute and the link flips back
  4. 04One deploy at a time under a lock, three releases kept, rollback in one command

Result

systemd-analyze security rates the service at 1.3 out of 10, lower is better.

Stories from production

Incident 01 · open

The backup that would not restore

pg_restore runs with an empty search_path, and the accent-insensitive search function called unaccent without a schema. Listing tables were never created, and that affected every older copy.

Incident 02 · open

The sandbox, tested by breaking in

Sandbox settings are easy to write and easy to never verify.

Incident 03 · open

Client address spoofing

Forged X-Forwarded-For and X-Real-IP could fool rate limits and the log.

Stack

Next.js 16 · React 19 · TypeScript strict · Tailwind 4 · next-intl · Drizzle · PostgreSQL 17 · Zod · Vitest · Playwright · Claude API · Caddy · systemd