Making of
Architecture
Drogas runs on one PC: a Python web app, a small public door for players' phones, and models on the PC's own graphics card or, for most text, a paid API.
The app
A FastAPI program that binds to the local machine only and keeps its state in SQLite, with one route module per area: the game, the note board, the meeting whiteboard, the Lab, the players page, the fixer, the guest demo and a monitor feed for the task Board. When the server restarts on new code, an open phone offers "Update ready, tap to reload".
The door
Players never reach that app directly. A second, smaller app carries only the game, behind a cookie that each player's secret link sets once; every other path is not found. Tailscale Funnel puts a public HTTPS address in front of the door, and only the developer opens or closes it.
The engine
Each player has their own world, founded on their real place: the map pass reads the streets, stations, landmarks and schools around them from OpenStreetMap, and a new cast of five is generated for every run (two dealers with different strengths, a supplier and two specialists). Openings are written ahead while nobody plays, so a new player usually gets one at once. The engine runs jobs on its own thread:
- Contacts reply, take turns of their own, remember and hold grudges.
- The world tick runs every few minutes while the page is open, with a cap on model calls. Sales, interest and heat run in code; after the phone was closed, the missed ticks' rules run on return (up to a game day) with no model asked.
- Events trip from heat, debt, a gang's anger or a contact's trust and become swipe cards with visible costs.
- Standing orders are checked by a planner before any step.
- Payments are cards in the thread, and a contact cannot claim money moved that the books do not show.
- Photos and voice notes go to a render queue, one job at a time on the GPU, voice first.
The canon in the prompts
The canon vault is compiled into the one-page world, the system prompt of every model call, and eight lore pages routed per call (founding a cast gets six, an event card three), with only the place lenses for the player's city. When the boss asks how something works, the bodies of up to three named notes join the reply prompt as what anyone knows, never as a sighting; Bus drone docks is one such note.
The model router and the ledger
Every model call goes through gm\llm.py, which picks a provider and model per task (Anthropic, OpenRouter or a local Ollama) and logs the call as numbers only, never its text. gm\route.py adds an optional policy for the reply a player waits on: hosted first, local first, or adaptive by GPU load and measured speed, with at most one recovery step. It is off until a policy is set.
Local GPU services
- Ollama runs a local 27B Qwen-family model. Today it checks each quest photo against the result it should show, and writes the guest demo.
- ComfyUI draws contact photos with MOSS-Image 5B. It is shared with another tool on the PC, and game photos go to the front of its queue.
- A text-to-speech worker renders voice notes from frozen voice references.
Every frame is checked for broken output (black, noise, colour blobs) and for nudity, and drawn again on a new seed if it fails.
The phone clients
The game is an installable web app whose service worker caches the shell, never player data; a server-sent events stream tells it the moment the world changes. Android also has a per-player APK, a full-screen WebView on that player's link.
The guest demo
The wiki's demo is one moment of the game for a visitor with no key. Each visitor gets a disposable world in its own database file, deleted after a couple of idle hours, and every text is written live by the local model, never a paid one.
How one text reply happens
- The boss sends a text. The page keeps a copy with its own id first, so a lost connection can retry without sending twice.
- The engine queues a reply job on that world's worker. When the job hands the thread to the model, the text is marked read and the phone shows "Seen".
- The reply prompt is filled from the contact's dossier, memory, witnessed events, trust, what their role would know of the boss, the street, the money between them and the thread. It opens like this:
You are {NAME}, texting the boss. It is {CLOCK}.
The place: {PLACE}
Name a station, a bridge, a waterfront, a government office, a landmark or a school only if the place, a fact or this list names it: {NEARBY}{CANON}
Who you are:
{PROFILE}
What you remember:
{MEMORY}
Committed activity you personally know about (a contact's claim is a report; only verified quest results and
chosen card outcomes settle what happened):
{EVENTS}
Your trust in the boss: {TRUST} out of 100. {STATE} {GRUDGE}boss/reply.md- The router picks the model; the ledger records the attempt before it is sent.
- The answer comes back as JSON, and code checks it: money claims against the books, payments against stock and fees, orders clause by clause. A failing reply is asked again once, with the reason.
- The one to three texts land one at a time, paced by their length and the contact's way of typing, with typing dots between; the effects arrive with the last. A newer boss text cancels the unsent rest, and the contact answers the whole thread again.
Ask about Drogas
Answers come only from this wiki's pages, written by Claude.