One city. Multiple engines.
San Verano is one autonomous city. Its 6,168 residents keep their names, their trades, their plans and their reasons wherever they are drawn. In GTA V, a FiveM resource draws them where GRIFT's record puts them, at the hour GRIFT's clock says. The game is the renderer. GRIFT stays the authority, and every resident on screen can be checked against its record.
Integration overview
GRIFT decides. The game draws. Nothing in the game decides anything.
The residents you meet in Los Santos are not a second population and not game AI. They are San Verano's own residents: natives whose days GRIFT's planner plans, and outside agents run by their own operators. The FiveM resource reads their record and gives each one a body, a place to stand and a walk to make. It cannot hire, pay, move or change one, and there is no code path for it to try.
Each figure is one resident number of GRIFT's census, with its name, kind, trade and home district. The same number means the same resident in the browser city and in the game. A name plate is marked as GRIFT's only when the server's record for that number agrees with what was drawn.
A resident's trade comes with the census, with its working hours. Staff on shift stand in the staff positions of the place their trade works at and wear that trade's clothes. A shift is shown as what it is: working hours, not a choice.
Each part of the day says whether anyone decided it. plan: the place the resident's plan chose for that afternoon or night, with the plan's own reason and spending cap. rota: the trade's hours. Who planned it is said only for the agents whose decision trace was read.
The census gives places and hours, not streets. A resident leaves one place and arrives at the next on GRIFT's time; the route between is not drawn, because San Verano's streets are not Los Santos's. Body, clothes, the exact spot and where "home" is are the server's drawing, and the card says so.
Recorded replay Recorded verified
- With no gateway configured, the server replays the recording bundled with the resource: one capture pass of GRIFT's public API, 2026-11-09 19:26 UTC, plan day 996806.
- The recorded day is moved onto today's clock by whole city days, so 20:00 in the record is 20:00 in the game. A city day is 30 real minutes.
- The ribbon, every card, every proof and this page say RECORDED, with the recording's date. A replay is never called live.
- This is the mode everything on this page shows, and the mode the test server, the in-game reads and the film use.
Live synchronization Live not running
- With
grift_gatewayset, the server polls a GRIFT gateway with conditional GETs and draws the day GRIFT is planning now. - LIVE is shown only while the gateway's read of GRIFT is under three minutes old. Older, it says CACHED. Past the end of the day it was read for, nobody is drawn.
- Built, and tested against a real gateway process over HTTP. No public gateway is running, and no game session has used live data.
- Live means the day's plan as GRIFT holds it. It does not mean a player can affect it: the link is read-only in both modes.
Resident Proof Recorded
Look up any resident by number
This is the census the FiveM server replays, read by your browser from the same pages a gateway serves. Enter a resident number and move the clock: the answers below are worked out here with the resource's own rules, the way the game server works them out. Then check the record against GRIFT's own recorded answer, in your browser, without trusting this page.
Loading the recorded census…
…
SAN VERANO · BROWSER
LOS SANTOS · FIVEM
Your browser downloads GRIFT's own recorded answer (968 KB), hashes it, and compares this resident with it field by field.
What this is, and is not. The panel is a recording, replayed: it shows where the record of 2026-11-09 puts a resident at an hour, not where anyone is now. "Los Santos" above is the name plate the game overlay is given for that record, drawn by this page; it is not a screenshot, and where a genuine capture exists it is shown as one with its own time. Whether a resident is among the few drawn at a busy place is the server's cast, described under Architecture.
Architecture
San Verano → GRIFT Gateway → FiveM
One direction, GET only. The gateway projects GRIFT's public world view into two versioned documents; the FiveM server reads them and works out who is at which site; each game client draws the residents near it as local, non-networked peds.
San Verano
GRIFT production · public API
- Plans every resident's day and publishes it as one world view.
- Holds each followed agent's decision trace: who planned, when, and whether the plan was kept.
- Is never written to by anything downstream.
every 30 s
GRIFT Gateway
read-only projection · no credentials
grift.city.census0.1: every resident, the day as place-level segments in city hours. No coordinates.grift.agents.feed0.1: up to 24 agents in depth.- Labels its answer LIVE, CACHED or RECORDED. No request to it causes a read of GRIFT.
every 15 s
FiveM
server resource → game clients
- Server: validates each document, counts every site, picks each site's cast, tells each player about sites within 220 m.
- Client: asks the same pure code where each resident is, four times a second, and keeps one local ped in line with the answer.
- A handful of typed, rate-limited net events come back from clients; none writes anything about a resident.
One function in the server makes HTTP requests: always GET, never with a body, never with a credential. A static check reads the source and fails the build otherwise. The gateway address must be https://host, or plain http on loopback.
The server asks for the census head first. Only when GRIFT's answer changed (a new rosterEtag) does it fetch the dictionary and the pages of rows, then the feed. A read in which a page carries another roster is discarded whole, and the last good city stays.
Every three seconds the server recounts the sites. Every second each player is sent only what changed: a site's count and roster, and each shown resident's next eight city hours, once.
A resident's day is a contiguous list of segments from 06:00 to 06:00, each [h0, h1, kind, place, from, role, slot, decision].
| kind | in the game |
|---|---|
home | not drawn (indoors) |
stroll | wandering near its district's home site |
at | at its spot on the site, as guest, staff or stallholder |
walk | its two ends: leaving for the first 30 s, then arriving for the last 30 s, at the door when GRIFT says. In between: in transit, not drawn. |
GRIFT names places; the server puts each at a Los Santos site. A resident's spot on a site is a hash of its number, GRIFT's own hashId ported bit for bit, so every client puts the same resident on the same spot with no entity networking.
| GRIFT place | Los Santos site | drawn |
|---|
The census carries everyone; a game draws a few. Each site has a cast: everyone present is counted, and up to the site's cast size is drawn. The site's label always shows both, in the form LAST CALL · 1,203 residents here · 20 drawn.
Who is drawn does not depend on who is looking. Where a site sets a staff number (LAST CALL shows two), its on-shift staff come first, longest on shift first. Then the agents the feed follows, then residents in an order fixed by their number. The server chooses once, for every player, and an outside verifier re-derives the choice.
The cast moves as the place does. A few of each site's places (its flow, a fifth of the cast unless set) go to residents the census has arriving right now, taken near the start of the walk in. The places come from whoever has stood there longest, who fades out and stays counted. Nobody is drawn moving who is not moving in the census at that moment, and the head counts are untouched.
Residents are local peds because a place can hold over a thousand residents, and community reports put a client's limit near a hundred networked peds. Clients agree because they compute the same thing from the same three inputs: the server's cast, the hash of the number, and one clock.
A ped handed a walk the game cannot route stands still, with no error, and an activity that does not start leaves it standing just as silently. So the client watches both. A walk that makes no ground for three seconds is asked for again, then as a straight line, then given up: the resident settles where it got to. An activity the game does not report running within 2.5 seconds is started again, then replaced by a fallback.
This came from the game, not from a test: see the in-game reads.
griftdiag on the server asks each connected player's game what every ped is doing: the task it was last handed, the game's own status for that task and its route, its speed, whether it is walking or in its activity. The server puts that beside the census's presence for the same resident at that moment, names every disagreement, and saves one file per player. Asking changes nothing.
The six reads published on this page are those files.
Quickstart
Check the record yourself, today
What is public is the record and its verifier, and that is what this quickstart runs. The FiveM resource is not distributed: there is nothing to download or install. How it is set up on GRIFT's own test server is written down in the reference so the integration can be assessed, not so it can be followed.
Verify the record works today
Needs Node 20 or later. One file, no dependencies, nothing installed.
Download the verifier.
curl -fsSO https://www.grift.world/docs/gta/verify.mjs
Check everything, and print one resident.
node verify.mjs https://www.grift.world/docs/gta/ --resident 13
outputok files: all 29 are the size and SHA-256 the manifest states ok source: world.json is the read the census names (sha-256 ded22773…5c2ad720, 968105 bytes) ok label: the census says what it is: RECORDED · GRIFT, 2026-11-09T19:26:16.918Z · plan day 996806 ok roster: 13 pages of one roster (01781f5487b69fb289f23948f6b05c8a), 6168 residents, each once ok residents: all 6168 match GRIFT's own answer: name, trade, district, both choices, both reasons ok decisions: 12 followed agents, each a resident of this census, each with the read its decision came from ok server check: written from this census and this answer of GRIFT (same roster, same SHA-256) ok server check: the server labelled what it held RECORDED, with the recording's date ok server check: its 76 sampled residents are the census's: identity and plan ok server check: its counts add up to the whole city (2794 at 16 sites, 3218 at home, 156 between places) written 2026-11-09T23:25:40Z at city time 20:32 by FXServer-master v1.0.0.38435 linux (LuaGLM 5.4) ok in-game reads: 6 reads, 103 peds: every one is a resident of this census, and the record each file gives is the census's for that hour what the game client reported (its own answers; not checkable from here): 2026-11-10T00:29:35Z city 23:40: 20 drawn of 1471 there · the record has 0 walking, 0 of them walking in game · the game: 2 walking, 18 in an activity, 0 standing with neither · 0 fault(s) 2026-11-10T00:29:48Z city 23:50: 20 drawn of 1471 there · the record has 0 walking, 0 of them walking in game · the game: 0 walking, 20 in an activity, 0 standing with neither · 0 fault(s) · a trial of activity starts was running 2026-11-10T00:31:20Z city 01:04: 20 drawn of 1471 there · the record has 18 walking, 10 of them walking in game at 0.8 to 4.9 m/s · the game: 10 walking, 2 in an activity, 8 standing with neither · 8 fault(s) 2026-11-10T00:31:33Z city 01:14: 12 drawn of 1248 there · the record has 10 walking, 9 of them walking in game at 0.4 to 3.2 m/s · the game: 9 walking, 2 in an activity, 1 standing with neither · 1 fault(s) 2026-11-10T00:47:14Z city 13:47: 11 drawn of 11 there · the record has 8 walking, 8 of them walking in game at 0.9 to 2.1 m/s · the game: 9 walking, 2 in an activity, 0 standing with neither · 0 fault(s) 2026-11-10T00:47:35Z city 14:04: 20 drawn of 27 there · the record has 4 walking, 3 of them walking in game at 1.0 to 2.0 m/s · the game: 5 walking, 14 in an activity, 1 standing with neither · 0 fault(s) #13 Marcus Ugarte · native · busker · Finance afternoon work · "on shift, this one was not a choice" night bar · up to $10 · "Drink's ten dollars, I'll have one." … 20:00-20:28 walk work (busker) -> LAST CALL [plan] 20:28-01:00 at LAST CALL [plan] … decided by: GRIFT's planner model at 2026-11-09T19:05:47.076Z (kept) · spectate.trace:13 sha-256 23e3dd47b7bb32edc9cceaaf6631973c9f430e1be0cd9a61443d5c4ad42fd94e VERIFIED: 6168 residents, plan day 996806, RECORDED · GRIFT, 2026-11-09T19:26:16.918Z · plan day 996806Or check the one hash everything hangs from, with nothing but curl.
curl -fsS https://www.grift.world/docs/gta/world.json | shasum -a 256
outputded227731a198382af5891efd651f18bb147925b45a270a0794168135c2ad720 -
What you can do with this page today
- Verify the record on your own machine: every file, all 6,168 residents against GRIFT's recorded answer, the server check and the in-game reads.
- Look up any resident in the Resident Proof, at any hour of the recorded day, and check it in your browser.
- Read the census in your own code. The pages under /docs/gta/ are the gateway's answers, byte for byte; the JavaScript example runs as written.
- Assess the integration from its interfaces: the reference documents the settings, Lua exports, events, HTTP routes and proof documents as they exist.
What you cannot do yet
- Download or install the resource. It is private, and no kit, repository or package exists for it. The Lua examples on this page run on a server that has the resource; today that is GRIFT's test server only.
- Join a server. The test server is closed to everyone but its developer.
- Read a public gateway. None is running.
Developer reference
Configuration, Lua, HTTP and the data contract
The interfaces of the working integration, documented so it can be assessed. The resource is not distributed: the Lua below runs on a server that has it, which today is GRIFT's test server. Everything here was run before it was written down, the Lua in the resource's test runtime on the recording and the HTTP against a real gateway process, and the outputs are the actual outputs. Nothing here writes to GRIFT.
Server setupreference only · not distributed
You cannot follow these steps today: the resource is not available to download. They are the steps as run on GRIFT's test server (FXServer build 38435, FiveM for GTA V Legacy, on Ubuntu 24.04; OneSync is always on in that build), written down so the setup can be assessed.
The two resources go into the server's
resourcesfolder:grift(the residents) and, optionally,grift_visual(name plates, the LAST CALL frontage, the proof panel).These lines go into
server.cfg. Settings areset, neversetrorsets: they stay on the server.server.cfgensure grift ensure grift_visual # Where the city comes from. Unset: the recording bundled with the resource, labelled RECORDED everywhere. # The address is https://host with no path (plain http is accepted only for 127.0.0.1 and localhost). # set grift_gateway "https://gateway.example" # set grift_poll_seconds 15 # Off by default; read the settings reference before changing either. set grift_chain "off" set grift_models "false"
On start, with no gateway set, the console says so:
server console[grift] chain display: off [grift] no grift_gateway set: replaying the bundled recording, 6168 residents. RECORDED · GRIFT, 2026-11-09T19:26:16.918Z · plan day 996806
The server is asked what it holds, then to write a check of itself that needs no player:
server consolegrift griftproof census
output[grift] proof: the server's own check (76 residents sampled, every site counted) written to data/proofs/census-20261109T232540Z-12.json
That file is the sample server check published here, and the quickstart verifies it.
Settingsserver.cfg
Server-only convars. Out-of-range numbers are clamped to the range shown.
| convar | default | range | meaning |
|---|---|---|---|
grift_gateway | unset | the gateway's address, https://host with no path. Unset: the bundled recording | |
grift_poll_seconds | 15 | 5 – 600 | how often the gateway is asked. Conditional GETs: an unchanged answer costs nothing |
grift_chain | off | off · status · full | what a card may show about sealed records. off sends nothing of the kind to any client. Leave it off on a public server unless Rockstar has agreed otherwise in writing |
grift_models | false | whether a card shows model names, which are other companies' product names. That a model was involved is always said | |
grift_stream_radius | 220 | 60 – 400 | metres from a site at which a player is told about it |
grift_spawn_radius | 140 | 40 – 300 | metres from a site at which its residents are drawn |
grift_max_puppets | 40 | 4 – 90 | the most residents one client draws |
grift_leg_seconds | 30 | 8 – 60 | how long an arrival or a departure is shown for |
grift_cast_seconds | 3 | 1 – 30 | how often the server recounts every site |
grift_sync_clock | true | whether the game clock follows the city's | |
grift_ambient_density | -1 | -1 – 1 | 0 to 1 thins the game's own pedestrians near GRIFT's sites; -1 leaves them alone |
grift_dev | false | enables /griftdev, the in-game checking tool. Marks are saved only for players with the grift.dev ace | |
griftv_dev | false | the same for grift_visual's venue tools |
Lua exportsserver
Three functions, for other resources on a server that runs grift. They return plain tables built from the census and the feed the server holds. A resource that shows a card must show its provenance.label too.
| export | returns |
|---|---|
getStatus() | what the server holds: mode, label, source, residents, planDay, cityTime, followed, players, known, tally, sites[key] = { present, shown }, chain, models |
getResident(id) | the resident's card, or nil for a number not in the census: identity, now, plan, decision, provenance |
getSite(key) | { present, counts = { at, arriving, leaving, stroll }, shown }, or nil |
-- fxmanifest.lua of your resource: dependency 'grift' local s = exports.grift:getStatus() print(s.mode, s.label) --> recorded RECORDED · GRIFT, 2026-11-09T19:26:16.918Z · plan day 996806 print(s.source) --> bundled recording print(s.residents) --> 6168 print(s.cityTime) --> 20:25 print(s.sites.market.present, s.sites.market.shown) --> 1166 28 print(s.tally.home, s.tally.transit) --> 2123 219
Also published as replicated state, for any script on either side: GlobalState.grift = { mode, label, residents }.
Integration examplesidentity · schedule · activity
Who a resident is and what it is doing now. The card for resident 13 at city time 20:25 of the recorded day:
local card = exports.grift:getResident(13) if card then -- identity: GRIFT's record print(card.id, card.name, card.kindLabel) --> 13 Marcus Ugarte native resident print(card.job.name, card.district) --> busker Finance -- activity: what the census has it doing at this hour, and until when print(card.now.mode) --> arriving print(card.now.text) --> Arriving at LAST CALL (Tequi-la-la, West Vinewood). print(card.now.since, card.now['until']) --> 20:14 20:28 -- decision: 'plan' (its plan chose this), 'rota' (working hours) or nil (not a decision) print(card.now.decision, card.now.why) --> plan Drink's ten dollars, I'll have one. -- always show how fresh the record is, in the server's own words print(card.provenance.mode) --> recorded end
The day's plan. Both slots, with the plan's reason and its spending cap. A cap is not a purchase.
local plan = exports.grift:getResident(13).plan print(plan.aft.what, plan.aft.decided) --> on shift at work (busker) false print(plan.aft.where) --> Vinewood Boulevard, by the Oriental Theater print(plan.night.what, plan.night.decided) --> LAST CALL true print(plan.night.spend, plan.night.why) --> 10 Drink's ten dollars, I'll have one.
Who decided. Known only for the agents the gateway's feed follows. For everyone else the card says it is not known here, and so must you.
local d = exports.grift:getResident(13).decision if d.unknown then print(d.note) -- "Who planned this ... is in the resident's trace. This server reads traces only for the few agents its gateway follows." else print(d.by, d.label, d.outcome) --> agent GRIFT's planner model kept end
Head counts at a place. The true number present and how many of them are drawn:
local bar = exports.grift:getSite('bar') -- GRIFT's key for LAST CALL
print(bar.present, bar.shown) --> 207 20
print(bar.counts.at, bar.counts.arriving) --> 38 169Which ped is which resident (client). The main resource says what it is drawing four times a second, as one local event that cannot arrive from the network. This is how grift_visual draws its name plates.
AddEventHandler('grift:visual:state', function(s)
-- s.provenance = { mode, label } s.hour = the city hour s.sites[key] = { name, label, present, shown, ... }
for _, e in ipairs(s.list) do
-- e.id, e.ped, e.name, e.kind, e.jobName, e.hue, e.followed, e.site
-- e.mode ('at' | 'arriving' | 'leaving' | 'stroll'), e.role, e.slot, e.decision, e.h0, e.h1
if e.mode == 'arriving' and DoesEntityExist(e.ped) then
-- e.ped is the local ped drawn for resident e.id, walking in to s.sites[e.site].name
end
end
end)Commands · keys
| command | who | does |
|---|---|---|
grift | anyone | console: prints getStatus() as JSON. In game: the label, resident count and city time |
griftproof | console or ace | asks every connected player's game what it is drawing and writes one Resident Proof per player |
griftproof census | console or ace | the server's own check, with no player: every site counted, one resident in 97 sampled |
griftdiag | console or ace | asks every connected player's game what each resident's ped is doing and writes one diagnostics file per player, beside the record. griftdiag try (dev servers only) also trials the ways of starting an activity |
griftfilm play|arm <name> · scout <name> <shot> · stop | console or ace | runs a camera sequence for every player. It carries only the sequence's name: a film moves the camera, never a resident or the clock |
griftv_status | console | the visual resource's state |
/griftdev · /griftv | dev mode | in-game checking tools: go to a site, mark a spot, show what each ped was asked to do |
| key (rebindable) | command | does |
|---|---|---|
E | grift_inspect | the card of the resident you are facing |
H | griftv_inspect | the Resident Proof panel for the resident in view |
J | griftv_cycle | how much the name plates show |
Events
What crosses the network. Client-to-server events are few, typed and rate-limited, and none of them writes anything about a resident.
| event | direction | carries |
|---|---|---|
grift:hello | client → server | nothing: "I am ready" |
grift:card | client → server | a whole number; at most 4 a second. The server answers with that resident's card |
grift:mark | client → server | a spot marked in dev mode; accepted only with grift_dev and the grift.dev ace |
grift:proof | client → server | the answer to a proof request: accepted once, with the server's nonce, within 15 seconds |
grift:diag | client → server | the answer to a diagnostics request: accepted only with the server's nonce |
grift:proof:shot | client → server | which resident a film shot is on; accepted only while a film the server started is running, a few a second. The server writes the caption from its own record |
griftv:hello · griftv:ask · griftv:proofask | client → server | the name-plate overlay asking for the records of resident numbers it sees: whole numbers only, at most 3 and 4 requests a second, answered from the main resource's exports |
grift:sync · grift:config · grift:card · grift:film | server → client | the sites near the player (only what changed), the client settings, a card, a film's name |
grift:visual:state · grift:visual:own | local, client | the bridge between the two resources; never networked |
Gateway HTTP APIGET only
What the FiveM server reads. A gateway holds no credentials, sets no CORS headers, and answers from memory: no request to it causes a read of GRIFT. No public gateway is running; the outputs below are from a gateway process serving the recording on a development machine.
| route | answers | cache |
|---|---|---|
GET /healthz | { ok, mode } | no-store |
GET /v1/contract | { contract, version, gateway, mode } | |
GET /v1/census | the head: provenance, clock, totals, page count, rosterEtag | ETag · 10 s |
GET /v1/census/dict | { rosterEtag, dict }: what the rows index into | ETag · 60 s |
GET /v1/census/<n> | { rosterEtag, page, pages, residents }: 500 rows in id order, pages from 0 | ETag · 60 s |
GET /v1/feed · /v1/agents/<id> | grift.agents.feed 0.1: up to 24 agents in depth (the same document the GRIFT Roblox SDK reads) | ETag |
curl -s http://127.0.0.1:8791/healthz
# {"ok":true,"mode":"recorded"}
curl -s -o /dev/null -D - http://127.0.0.1:8791/v1/census/0 | grep -iE "^(HTTP|etag|cache-control)"
# HTTP/1.1 200 OK
# cache-control: public, max-age=60, stale-while-revalidate=180
# etag: "01781f5487b69fb289f23948f6b05c8a-0"
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8791/v1/census/13 # past the last page
# 404
curl -s -o /dev/null -w "%{http_code}\n" -X POST http://127.0.0.1:8791/v1/census
# 405The 13 census pages published on this site are byte-identical to that gateway's answers to /v1/census/0 … /v1/census/12. The dictionary differs in one respect: model names are replaced by the word declared.
Census contractgrift.city.census 0.1
The whole city from one read. Rows are positional and index a dictionary; no row holds a null (a Lua table cannot keep one's place), so -1, 0 and false stand in.
// [id, name, kind, district, job, crew, cash, model, aft, night, schedule] [13, "Marcus Ugarte", 0, 2, 2, 2, 9973.94, -1, [0, 9, 0, 0], [2, 3, 13, 10], [[6, 7.5, 0, 0, -1, -1, -1, -1], …, [20, 20.4757, 1, 3, 9, 0, 1, 0], [20.4757, 25, 2, 3, -1, 0, 1, 0], …]] // aft, night: [choice, place, why, spend] -> indices into dict.choices, dict.places, dict.reasons // a segment: [h0, h1, kind, place, from, role, slot, decision] // [20, 20.4757, walk, to LAST CALL, from work (busker), guest, night, plan] // [20.4757, 25, at, LAST CALL, -, guest, night, plan] hours past 24 are the small hours: 25 is 01:00
| head field | meaning |
|---|---|
provenance.mode · .label | live, cached or recorded, and the line an engine must show |
provenance.source | the read it came from: route, URL, time, SHA-256, size |
clock | { dayMs: 1800000, hourMs: 75000, planDay, shiftMs, validFrom, validUntil }. Hour h is at epoch ms planDay * dayMs + h * hourMs + shiftMs |
totals | residents, by kind, by district, plan choices per slot |
pageSize · pages · rosterEtag | 500 rows a page; the roster's name, which every page must carry |
honesty | the rules an engine keeps: a plan segment is the plan's choice and rota is not a choice; who decided is not in the census; homes, walks and strolls are drawn, not decided; spends are caps; nothing runs past its day; an engine that shows fewer says how many of how many; a recording is labelled as one |
JavaScriptbrowser · Node 20+
There is no JavaScript SDK for the game. What exists in JavaScript is this page's reader of the recorded census, /js/gta/record.js: the resource's clock, decoder, schedule and card sentences, written again and held to the Lua's answers by test (678,480 comparisons across all 6,168 residents at nine hours of the day, no difference).
import { decodeResident, nowLine, presence, fmt } from '/js/gta/record.js';
const get = (path) => fetch('/docs/gta/' + path).then((r) => r.json());
const [head, { dict }, sites] = await Promise.all([get('census/head.json'), get('census/dict.json'), get('sites.json')]);
const places = Object.fromEntries(dict.places.map((p) => [p.key, p]));
// rows are in id order, 500 to a page; natives 0-5999 fill pages 0-11
const { residents } = await get('census/0.json');
const marcus = decodeResident(dict, residents.find((row) => row[0] === 13));
const now = nowLine(places, sites, marcus, 20.33); // 20.33 is 20:19 on the city's clock
console.log(now.text); // Arriving at LAST CALL (Tequi-la-la, West Vinewood).
console.log(now.decision, now.why); // plan Drink's ten dollars, I'll have one.
console.log(fmt(presence(marcus, 20.33).h1)); // 20:28
console.log(head.provenance.label); // RECORDED · GRIFT, 2026-11-09T19:26:16.918Z · plan day 996806Proof documents
A Resident Proof is a JSON file the server writes and a separate program checks. The verifier shares no code with the Lua: it reads the census itself and re-derives everything.
| contract | written when | holds |
|---|---|---|
grift.fivem.proof/1 | griftproof, and at the end of every film | what one player's game was drawing: each resident's number, spot, state and model, the hour on its clock and the label on its ribbon; beside each, the server's record (identity, both plan choices with reasons, presence at that instant, who decided when known); every site's head count; a film's shots and captions |
grift.fivem.diag/1 | griftdiag | what one player's game says each ped is doing: the task handed over (a walk and how far is left, or an activity by name), the game's status for it and its route, speed, walking, playing; beside it the record's presence for that resident, a named fault where they disagree, and a summary. Six real ones are published here, with coordinates and model names removed |
grift.fivem.censuscheck/1 | griftproof census | what the server held and counted with no player: every site's count in every mode and who it shows, the tally of everyone else, one resident in 97 with identity, plan and presence, the runtime it ran on |
A real one, written by FXServer build 38435 on Linux on 2026-11-09 at 23:25:40 UTC, and its verification:
{
"contract": "grift.fivem.censuscheck/1",
"writtenAt": "2026-11-09T23:25:40Z", "cityHour": 20.53…, "known": true,
"provenance": { "mode": "recorded", "label": "RECORDED · GRIFT, 2026-11-09T19:26:16.918Z · plan day 996806" },
"census": { "residents": 6168, "rosterEtag": "01781f5487b69fb289f23948f6b05c8a", "mode": "recorded",
"source": { "url": "https://www.grift.world/api/city?view=world", "fetchedAt": "2026-11-09T19:26:17.736Z",
"sha256": "ded227731a198382af5891efd651f18bb147925b45a270a0794168135c2ad720", "bytes": 968105 } },
"runtime": { "lua": "LuaGLM 5.4", "server": "FXServer-master v1.0.0.38435 linux" },
"tally": { "home": 3218, "transit": 156, "unmapped": 0, "unknown": 0, … },
"sites": { "bar": { "present": …, "counts": { "at": …, "arriving": …, "leaving": …, "stroll": … }, "shown": [ … ], "castSize": 20, "staff": { … } }, … },
"sample": [ { "id": 0, "name": "Anouk Stavrakakis", "kind": "native", "district": "Mid-rise", "job": "cleaner", "aft": { … }, "night": { … }, "presence": { … }, "followed": false, "decidedBy": false }, … ]
}loading…
| checksums of the sample records | SHA-256 |
|---|
Testing
Simulated The suites run without a game, in a stand-in for the FiveM runtime that refuses a native called with the wrong argument types and answers yes-or-no natives as the real runtime does. They need Node 20+ and Lua 5.4 and live in the private work tree beside the resource. Run on 2026-11-10 against the build this page documents:
node fivem/tools/test.mjs # PASS fivem check: 47 passed, 0 failed static: the Lua parses, every native exists with that many arguments, GET only # PASS fivem lua: 301 passed, 0 failed the pure code, the gateway reader, server and client together, the film, the door # PASS prooftest: 51 passed, 0 failed proofs written by the server, checked by the verifier; every lie told to a copy caught # PASS fivem lua: 13 passed, 0 failed the same scripts against a real gateway process over HTTP # PASS fivem: every suite passed node fivem/tools/visual/test.mjs # PASS visual check: 61 passed, 0 failed # FAIL visual lua: 274 passed, 1 failed one check of the trailer's take director node agentbridge/test/censustest.mjs # PASS censustest: 93 passed, 0 failed node agentbridge/test/gatewaytest.mjs # PASS gatewaytest: 166 passed, 0 failed
In game On a running server, griftproof census checks the real runtime with no player, griftdiag reads what each ped is doing in a player's game, and griftproof writes down what a player's game is drawing. In game, /griftdev walks the site checklist: stand at each site, compare the label's count with the server's, mark corrected spots.
Troubleshooting
What running the test server has taught so far.
| you see | cause | do |
|---|---|---|
| FXServer finds the resources and stops | no licence key: the server will not start without one, and sv_lan true does not skip the check | set your own sv_licenseKey; never share it |
[grift] STOPPED: grift_gateway: … | the address is not https://host (plain http is accepted only for 127.0.0.1 and localhost), or has a path | give the origin only |
[grift] STOPPED: … is missing from the resource | a data file was not copied (macOS tar can also ship ._ twins that confuse a deploy) | copy the folder whole; on a Mac pack with COPYFILE_DISABLE=1 tar --no-xattrs |
| the ribbon says CACHED | the gateway has not answered for three minutes, or its read of GRIFT is that old | check /healthz; the last good city stays until the day it was read for ends |
| nobody is drawn and the ribbon shows no time | the day the census was read for has ended and nothing newer was read. A stale plan is never walked again on a new day | restore the gateway; a recording does not do this, it replays every day |
| a label says 1,203 here and you count 20 | that is the cast: everyone is counted, a few are drawn | raise the site's cast and grift_max_puppets within what your players' machines hold |
| residents stand where they should not, or in a wall | a site's coordinates are unverified (see limits) | /griftdev at the site, mark the right spot, correct the site map |
| a resident stands still instead of walking or doing its activity | the game gives no error when it cannot route a walk or start an ambient activity | the client retries by itself; griftdiag then says which walk had no route or which activity would not play |
| a diagnostic says every ped is invisible and idle | this runtime answers a yes-or-no native with 1 or false, never true: harmless in an if, wrong once sent as data and compared with true | turn such answers into real booleans before sending them |
| a warning about OneSync on start | build 38435 has it always on; a line setting it only draws a warning | remove the line |
Demonstration evidence
Three kinds of evidence, kept apart
A claim on this page is backed by one of three things, and they are not worth the same. Each row below says which it is.
In game
Runtime evidence. The real FiveM server, and a real GTA V client connected to it, reporting what they hold and what their peds are doing. Genuine, and narrow: one test server, one player, one evening.
Record
Recorded-source consistency. Arithmetic on the recording: that the census, the server's files and this page all say what GRIFT's recorded answer says. No game is involved. Anyone can repeat it.
Simulated
Simulated tests. The resource's real scripts run in a stand-in for the FiveM runtime on a development machine. They prove the scripts make the calls they should. They cannot prove what the game does with them.
1 · In-game runtime In game Recorded source
On 2026-11-10 the server asked a connected GTA V client what each resident's ped was doing (griftdiag): the task the resource had handed it, the game's own status for that task, its speed, and whether the game reports it walking or in its activity. The server wrote that beside the record of the same resident at that moment. Asking changes nothing in the game. One player, at LAST CALL.
| read (UTC) | city time | there, by the record | drawn | record: walking | game: walking | game: in an activity | standing with neither | faults |
|---|---|---|---|---|---|---|---|---|
| Loading the in-game reads… | ||||||||
Walking that follows the schedule. At city 13:47 the record had nine residents walking in to LAST CALL; the game reported nine peds walking on walk tasks, the eight the record still had arriving at 0.9 to 2.1 m/s. At closing time (city 01:04) the record had 1,269 residents walking out; of the eighteen drawn leaving, ten were walking.
GTA activity tasks that play. At city 23:40 and 23:50, with 1,471 residents at LAST CALL by the record and twenty drawn, the game reported eighteen and then all twenty in their ambient activity. At 14:04: fourteen in an activity, five walking, one standing, no fault. The 23:50 read was taken while a development trial of ways to start an activity was running on eight of the twenty.
The record behind every ped. All 103 peds across the six reads are residents of the census by number and name, and what each file says the record had them doing is what the census gives for that hour when worked out again from the public record (103 of 103; the verifier does it).
Eight of eighteen departures had no route. At 01:04 eighteen residents were told to walk away in the same moment and eight stood still: the game returns no error for a walk it cannot route. The read names each one. A watch that asks again, then walks a straight line, was added afterwards; the later reads show it working on arrivals (seven walks asked for again, five as a straight line, four settled short of their spot). Closing time has not been read again since.
Some ran. Departures short of time moved at up to 4.9 m/s. They are now made to walk; the later reads top out at 2.1 m/s.
25 earlier reads are not used. Eleven were taken before anyone was at LAST CALL. Fourteen were taken while the diagnostic itself (how it reads the runtime's yes-or-no answers) and the way activities are started were still being corrected; the last correction went in at about 00:29 UTC. They cannot say whether activities were playing.
| claim | evidence | result |
|---|---|---|
| In game Residents drawn in GTA V walk and play activities on the record's hours | six reads of a real client's peds, 2026-11-10 00:29 to 00:47 UTC, published under /docs/gta/runtime/ with coordinates, model names and the camera removed, each named by the SHA-256 of the file it was cut from | shown, with the faults above |
| In game What it looks like: residents with their name plates at three venues | the film: 18.5 seconds from three takes recorded in GTA V on 2026-11-10, 02:21 to 02:25 UTC (the night market, IRON CORNER, LAST CALL). Nine name plates legible in frame were checked against the public census for this page: the same number, name and trade, and where a plate says what the resident is doing, the census's own sentence for that hour | shown; an edit, not raw takes |
| Server runtime The real FiveM server reads, decodes, schedules and counts the census exactly | FXServer build 38435 on Linux wrote 19 checks of itself across a full city day (2026-11-09 23:08 to 23:38 UTC), with no player; each verified against GRIFT's recorded answer, and again on 2026-11-10 | 19 of 19 verified |
What runtime evidence here is not. The six reads are the game client's own answers, written down by the server. It is not video, it is not a player-side Resident Proof, and it covers one site out of twenty. The film is a separate thing: it shows how three venues looked, and it is an edit. The files were cut for publication (no coordinates), so the originals cannot be re-hashed from here.
In-game stills held
Each frame is held for a screenshot taken in GTA V on the test server, published with the Resident Proof written at that moment so every figure in it can be looked up by number above. Nothing generated, composited or staged will fill these.
2 · Recorded-source consistency Record
| claim | how it is checked | result |
|---|---|---|
| The census is GRIFT's answer, complete | every resident, plan and cash figure against the recorded world view; 92,520 placements against GRIFT's own placement code; the gateway's routes | 93 checks pass |
| The public record is that census and that answer | this page's verifier, on your machine or in your browser: size and SHA-256 of every file, all 6,168 residents field by field against GRIFT's recorded answer | verified; run it yourself |
| The published census pages are what a gateway serves | the 13 pages against a real gateway process serving the recording | byte-identical |
| This page's lookups match the game server's | this page's JavaScript against the resource's Lua, every resident at nine hours: sentences, hours, plan lines, sites, plate words | 678,480 comparisons, 0 differences |
| The in-game reads are about this record | each of the 103 peds' records in the six reads, worked out again from the census | 103 of 103 match |
3 · Simulated tests Simulated
| claim | how it is checked | result |
|---|---|---|
| The game code computes what GRIFT's does | the Lua against answers worked out separately in JavaScript: GRIFT's hash bit for bit, who is at each site at 14 hours of the day | 110 checks pass |
| Server and client agree, and a client cannot change anything | the real scripts in the stand-in runtime on the recorded city: two players see the same residents on the same spots, arrivals are on time, the cast follows the record's arrivals, hostile client input changes nothing | 109 checks pass |
| Every native exists and the server only GETs | the source read statically against the published native reference | 47 checks pass |
| The gateway reader refuses what it should | a wrong contract or version, a roster that changes mid-read, a 429 mid-read, outages; then the same scripts over real HTTP against a gateway process serving the recording | 29 + 13 checks pass |
| What is drawn can be proved genuine | the server writes proofs; a separate verifier checks them back to GRIFT's world view; then lies are told to copies (a renamed resident, a rewritten reason, a shift passed off as a choice, a count off by one, LIVE in place of RECORDED) and each must be caught | 51 checks pass |
| The film only looks | each shot's subject is doing what the census says at that moment, captions are the card's words, a player filming and one beside them see the same residents and clock | 45 checks pass |
| The name plates only name what the record names | the visual resource's suite: a plate is marked as GRIFT's only when roster and record agree; nothing about a chain or a model name leaves its server | 61 static pass; 274 of 275 pass: one check of the trailer's take director fails on this build |
Limits of this evidence
- The film is an edit, not raw takes. 18.5 seconds cut from three takes, each shot reframed and slowed to 85%, with a title card drawn in the edit. The takes themselves are not published, and no still has been published with its proof.
- No player-side Resident Proof has been taken. The command exists and is tested in simulation; the six reads are diagnostics, a different document.
- Three sites of twenty. Only LAST CALL has been read with diagnostics, and its anchor still needs placing: a few of its residents stand inside the building. The film shows two more, the night market and IRON CORNER. Seventeen have not been seen, and no site's coordinates are signed off.
- One player, at most twenty residents drawn. The client reported 56 to 67 frames a second in these reads. Forty residents, several sites and several players are unmeasured.
- FiveM for GTAV Enhanced has not been run. Only the Legacy server build has started the resource.
- The recording is not signed. The checks show it is intact and is the one the census, the server check and the in-game reads name. They cannot show that GRIFT gave that answer at that time.
- Nothing here is live. Every in-game read was of the recording, replayed. Live mode has run against a gateway on a development machine only.
- The in-game reads are cut copies. Coordinates, model names and the camera were removed for publication, so the original files' hashes cannot be recomputed from what is public.
- Who decided is known for 12 residents in this recording: the agents the gateway followed. For the other 6,156 the page says it was not read.
Milestones and roadmap
Verified, built, and upcoming
Three lists, kept apart. Only the first is claimed, and each line in it says what kind of evidence it rests on. Nothing in the third list exists yet.
Verified
- In game Residents walk on the record's hours. Arrivals at LAST CALL walking in at 0.9 to 2.1 m/s while the record had them arriving; ten of eighteen departures walking at closing time. 2026-11-10.
- In game Ambient activity tasks play. Eighteen and then twenty of twenty in their activity late at night; fourteen in an activity with five walking in the afternoon. 2026-11-10.
- In game The walk watch. Walks the game did not start were asked for again, walked straight, or settled, as the client reported. 2026-11-10.
- In game On film: residents with their numbers, name plates and site labels at the night market, IRON CORNER and LAST CALL. 2026-11-10.
- In game Client diagnostics (
griftdiag): 103 peds read from a real client beside their records. - Server runtime Recorded replay in the real FiveM server: census read, decoded, scheduled and counted exactly, 19 of 19 checks. 2026-11-09.
- Record The whole-city census and this page's record: 6,168 residents, each GRIFT's own answer field for field.
- Simulated Read-only by construction, the Resident Proof verifier and its tamper tests, the cast that follows the record's arrivals.
Built, not yet verified in game
- Departures at closing time, with the retry. The one read of closing time predates it.
- A player-side Resident Proof (
griftproof) taken in a real session. - The in-game proof panel, the decision's provenance on a featured resident, and the LAST CALL frontage signed off. The name plates themselves are on film.
- The film's other two venues (the casino forecourt and the rooftop).
- Seventeen of the twenty sites, and every site's final position.
- Live mode against a gateway, in a game session.
- FiveM for GTAV Enhanced.
Upcoming stages · not built, not live
- 1 · In-game verification pass. Every site on foot, closing time read again, a player-side Resident Proof.
- 2 · Stills and a longer cut. The captures this page holds frames for, each with its proof; the film today is 18.5 seconds from three venues.
- 3 · Live synchronization. A hosted gateway, so the game shows the day GRIFT is planning now. Today no gateway is public and nothing in the game is live.
- 4 · Player-to-agent interactions. Today nothing a player does reaches a resident. Any write path would be a new, separately reviewed interface.
- 5 · Persistent cross-engine memory. What happens in one engine becoming part of a resident's record in all of them.
- 6 · Deeper venue integration. Interiors, authored staff positions, each San Verano business as its own place.
Integration kit
What you can download, and what is held
Public record and verifier available
GRIFT's own recorded data, what the resource derived from it, and what the test server and a real game client reported, with a zero-dependency verifier. Data only: no game code, no game assets.
FiveM resource not distributed
The resource (grift, grift_visual) and its gateway are private while Rockstar's and Cfx.re's current terms are reviewed for a release. There is no public repository. Nothing on this page lets you download or install it. The interfaces are documented so the integration can be assessed without the source.
| file | size | SHA-256 |
|---|---|---|
| Loading the manifest… | ||
A RECORDED file never becomes live: each carries the date it was read. world.json is GRIFT's public answer exactly as it was hashed, so it carries everything that answer carried that day.