---
title: "profile or wanted: com funciona el joc per dins i què mostren 26.452 respostes"
description: "com funciona profile or wanted per dins: el servidor decideix cada resposta, com entren les cares, el desplegament i què mostren 26.452 respostes de producció."
date: 2026-10-12
author: "joel taylor pedrós"
lang: ca
url: https://joeltaylor.business/blog/profile-or-wanted-com-funciona
translations:
  es: https://joeltaylor.business/blog/profile-or-wanted-como-funciona
  en: https://joeltaylor.business/blog/profile-or-wanted-how-it-works
image: https://joeltaylor.business/_next/static/media/cover.0hs-bba9zgdmn.webp
---

# profile or wanted: com funciona el joc per dins i què mostren 26.452 respostes

![sota el títol el servidor decideix cada resposta, tres targetes: 26.452 respostes en tres setmanes, 578 jugadors, i un vps de 2 vcpu.](https://joeltaylor.business/_next/static/media/cover.0hs-bba9zgdmn.webp)

[profile or wanted](https://profileorwanted.com) va registrar 26.452 respostes de 578 jugadors a les partides lliures, entre el 21 de setembre i l'11 d'octubre de 2026. des del 26 de setembre, el navegador no sap si has encertat fins que has triat: el servidor decideix cada resposta.

és el joc on veus una cara i has de dir si és d'un perfil de linkedin o d'una fitxa de busca i captura de la interpol. [l'altre article](/blog/profile-or-wanted-linkedin-o-interpol) explica com vaig arreglar les fotos. aquest va del que hi ha al voltant: com es reparteix una partida, d'on surten les cares, com es desplega i què diuen les dades de producció.

## què hi ha en producció

next.js amb trpc, prisma i postgres, en un contenidor del meu vps de 2 vcpu i 3,7 gb, que també fa servir coolify i altres apps. les fotos són en un bucket de cloudflare r2.

no cal compte per jugar. un jugador és una cookie, `pow_pid`, amb l'id signat amb hmac-sha256 i 400 dies de durada. si canvies de navegador ets un altre jugador, així que els jugadors d'aquest article són cookies i no persones. els 578 són les que han respost almenys una cara en una partida lliure. si hi sumes les que només han jugat el repte diari o han cobrat el bonus, són 856 del 19 de setembre a l'11 d'octubre.

## el servidor decideix cada resposta

quan comences una partida, el servidor treu les cares a l'atzar, sense repetir-ne cap dins la partida, i escriu cada una a la base de dades amb la seva resposta correcta. al navegador només li arriba una posició i una foto.

```prisma
model GameRunSlot {
  runId        String
  position     Int
  personId     String
  type         PersonType
  imageVersion Int
  guess        PersonType?
  timedOut     Boolean @default(false)
  ms           Int?

  @@id([runId, position])
}
```

`type` és la resposta i queda fixada quan es reparteix. `guess` és `null` fins que el jugador respon.

![diagrama d'una ronda entre el navegador i el servidor. el servidor reparteix les cares i desa la resposta de cada una a la base de dades. el navegador rep només la posició i la foto, envia la seva resposta, el servidor la desa abans de comparar-la i torna el veredicte. si es repeteix la petició, torna el mateix veredicte.](https://joeltaylor.business/_next/static/media/ronda.0dhdvbhokyjpn.webp)

_la resposta correcta només surt del servidor després que la del jugador s'hagi escrit. una petició repetida torna el veredicte que ja hi havia, i per això es pot tornar a provar sense risc._

la ruta `run.guess` escriu primer la resposta del jugador i després diu si era correcta. una ronda queda decidida per aquesta primera escriptura, i les peticions repetides reben el veredicte desat. això permet que el client reintenti qualsevol fallada que pugui ser de xarxa, amb una espera de 250 ms que dobla fins a 4 s, amb una mica d'atzar, i que abandoni cada intent als 6 s.

el servidor també fa complir l'ordre: les rondes es resolen una darrere l'altra, una partida de longitud fixa no es pot jugar més enllà del final, i una d'infinita s'acaba amb la primera errada. quan acaba, `finish` puntua el que `guess` ha desat i ignora el que el client digui de les seves rondes.

fins al 25 de setembre no era així: les respostes anaven al navegador amb el repartiment, perquè el veredicte sortís sense esperar res. 387 de les 2.203 partides d'aquest article, les del 21 al 25, es van jugar amb aquell sistema. ho vaig canviar el 26 perquè, amb la resposta al navegador, qualsevol altra comprovació del servidor era decorativa. el preu és una petició per ronda. la base de dades és a la mateixa màquina, i el que triga és la xarxa del mòbil. si l'espera passa dels 120 ms, el botó premut mostra una barra de tinta, i als 2,5 s s'ofereix reintentar.

la foto tampoc no pot delatar res. en lloc de la clau de l'emmagatzematge, que contenia l'id de la persona, cada cara es serveix des d'una adreça que és un hmac de l'id.

això no atura tot. un jugador pot buscar una cara, o construir-se una taula de cara i resposta amb els veredictes de moltes partides, perquè el corpus és finit i cada veredicte en revela un parell. el que ho limita és que els punts del rànquing tenen un màxim de 250 al dia.

## d'on surten les cares

ara hi ha 522 cares en joc, 248 perfils de linkedin i 274 fitxes de la interpol. n'hi ha 19 més a prova i 8 de retirades, i 1.865 candidates a la base de dades que encara no han entrat.

les cares noves passen per un intake que tria les que a claude sonnet li semblen difícils de classificar, puntuades a cegues sobre fulls de contacte de 12. [l'altre article](/blog/profile-or-wanted-linkedin-o-interpol) explica la calibració, i una taula, `IntakeRecord`, recorda cada decisió perquè cap cara es miri dues vegades.

el puntuador encerta en conjunt però no cara per cara, així que cada cara nova comença a prova. entra a una de cada cinc posicions repartides, amb un màxim de tres per partida, i els jugadors la contesten com qualsevol altra. amb 30 respostes es decideixen els casos clars, i amb 60 tots. `trial-review` compara l'encert de la cara amb el d'una cara en joc típica del mateix tipus, i la manté o la retira.

els 19 casos a prova són tots fitxes, perquè encara no hi ha intake de perfils. un jugador habitual que noti una cara que no ha vist mai pot deduir que és de la interpol, i el codi ho deixa escrit com a pendent.

## el repte diari i les tasques programades

el repte diari és el mateix per a tothom: cinc cares, l'última un cap, que és la cara que els jugadors de les partides lliures han errat més els darrers dies, i un gir segons el dia de la setmana. `daily-build` el prepara a les 03:00 utc, un dia abans.

però el repte també es genera en llegir-lo si falta, amb un bloqueig per data i `ON CONFLICT DO NOTHING`. si la tasca falla durant dues setmanes, només costa una petició lenta al primer visitant de cada matí. el repte és la pantalla per on passa la retenció, i no ha de dependre d'un cron. des del 20 de setembre `daily-build` s'ha executat 23 vegades, i les 23 han anat bé.

coolify fa un `POST` a `/api/jobs/<nom>` amb un secret compartit. les altres tasques són `notice-refresh`, que detecta les fitxes que la interpol ja no publica i les marca sense treure-les del joc, `daily-reminder`, que envia una notificació a les 18:00 utc, `stats-rollup` i `trial-review`. cada execució deixa un registre a `JobRun` amb el que ha fet.

## el desplegament

```
push a main
  -> ci: lint, typecheck i tests
  -> si passa, deploy.yml compila la imatge del commit que ci ha verificat
  -> la puja a ghcr com a latest, main i sha-<commit sencer>
  -> avisa el webhook de coolify i espera que acabi
  -> coolify descarrega la imatge i reinicia el contenidor
```

el vps només fa `docker pull`. compilar next.js allà competiria per la memòria amb l'app que està en marxa, i en una de 2 vcpu i 3,7 gb això es nota. una compilació en fred triga uns 115 s.

vaig provar d'afegir-hi memòria cau de buildkit i va sortir més car: 220 s dels 361 s del pas de compilar per estalviar-ne 50 com a molt, perquè `COPY . .` va abans de `npm run build` i la invalida a cada canvi. no n'hi ha.

l'entrada del contenidor executa `prisma migrate deploy` i surt amb error si falla. per tornar enrere n'hi ha prou de posar a coolify la imatge `sha-<commit>` d'una de les 5 darreres.

l'única variable obligatòria és `DATABASE_URL`. les altres fallen tancades: un contenidor sense claus de stripe no ven res, però juga. l'excepció és `PLAYER_SIGNING_SECRET`, sense la qual no es pot jugar, perquè una clau per defecte permetria a qualsevol fabricar la cookie d'un altre jugador.

## com ho mesuro

umami propi, en un servidor meu. el seguiment només envia des del domini de producció, i cada esdeveniment porta l'etiqueta de la versió que el va fer.

els esdeveniments són un mapa tancat a `events.ts`, en passat i amb propietats de pocs valors, perquè els desglossaments d'umami fan una fila per valor. el detall per cara no hi és, que ja queda a la base de dades.

`scripts/kpi-report.mjs` llegeix producció amb un rol de només lectura, i la connexió porta a sobre `default_transaction_read_only=on`. dóna taxes amb el seu recompte, mai sols. una taxa de `5,2%` no diu res, una de `5,2% (3/58)` sí. amb menys d'uns 100 jugadors actius a la finestra, el que surt és orientatiu.

## què mostren les dades

les tres setmanes completes, del 21 de setembre a l'11 d'octubre, diuen això:

- el 72,4% de les respostes són correctes: 19.161 de 26.452. a les fitxes, el 73,9% (9.780 de 13.235), i als perfils, el 71,0% (9.381 de 13.217).
- la resposta mediana triga 1,76 s, i el p90, 6,6 s. les respostes en menys d'un segon són correctes el 71,5% de les vegades (3.006 de 4.203), i les de 3 s o més, el 68,1% (4.998 de 7.334). les cares difícils fan pensar més, així que això no demostra que pensar-hi perjudiqui.
- de 2.203 partides començades, se n'han acabat 1.511, el 68,6%.

![gràfic de barres de les partides començades per dia de la setmana del 21 de setembre a l'11 d'octubre de 2026. dilluns 273, dimarts 300, dimecres 553, dijous 748, divendres 227, dissabte 67 i diumenge 35.](https://joeltaylor.business/_next/static/media/setmana.3mpf1th7v06og.webp)

_el joc es juga entre setmana: dissabte i diumenge sumen 102 de les 2.203 partides, el 4,6%. partides començades per dia de la setmana, en utc, del 21 de setembre a l'11 d'octubre de 2026, segons la base de dades de profileorwanted.com._

el 8 d'octubre hi va haver 529 partides, i l'11, que era diumenge, 8. umami té 1.029 visitants únics, i 608 venen de bing i 604 fan servir edge. tot plegat apunta a ordinadors de la feina o de l'escola. el recompte d'umami és un mínim, perquè el seguiment no va comptar res del 20 al 22 de setembre. el detall de les cerques de bing és a [l'altre article](/blog/profile-or-wanted-linkedin-o-interpol).

la majoria no torna: dels 632 jugadors que van començar la primera partida entre el 21 de setembre i el 10 d'octubre, 18 (2,8%) en van fer una altra l'endemà. la línia de base d'abans de tocar res era del 3% al 7%. són poques persones, així que ho llegeixo com a orientatiu.

## què canviaria

el primer és que les 387 partides del 21 al 25 de setembre es van jugar quan la resposta anava al navegador. són el 17,6% de les 2.203. no sé si algú va mirar la xarxa, però la resposta hi era, i aquestes partides entren als números d'aquest article.

el segon és fer un intake de perfils, perquè les cares a prova no siguin totes fitxes i una cara desconeguda no delati la resposta.

el tercer és la retenció. un 2,8% l'endemà vol dir que la majoria juga una sola vegada, i és la xifra que cal moure.

el joc és a [profileorwanted.com](https://profileorwanted.com), en català, castellà, anglès i francès.
