GRIFTGTA V integration
GRIFT · GTA V integration · FiveM

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.

Read-onlynothing done in the game reaches GRIFT Recordedeverything on this page is a recording of 2026-11-09, replayed Livebuilt and tested; no public gateway is running
The cinematic · recorded in game
The film loads here. Only genuine in-game footage is ever shown in this frame: no generated footage, no staged screenshot.
The film is shot inside GTA V by a scripted camera that only looks: it never moves a resident or the clock, the labels are the record's own words, and the RECORDED ribbon stays on screen.

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.

Identity

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.

Employment

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.

Decisions

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.

Movement

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_gateway set, 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…

A resident of San VeranoRecorded

…

20:20
060912151821000306
its plan chose thisworking hourswalkinghome · not a decision
SAN VERANO · BROWSER
RESIDENT#
the same number in both
LOS SANTOS · FIVEM
Today's plan
Decision provenance
Source · snapshot
In-game reads
Check it here

Your browser downloads GRIFT's own recorded answer (968 KB), hashes it, and compares this resident with it field by field.

    Elsewhere
    lua · the same record on a server

    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.
    public GETs
    every 30 s

    GRIFT Gateway

    read-only projection · no credentials

    • grift.city.census 0.1: every resident, the day as place-level segments in city hours. No coordinates.
    • grift.agents.feed 0.1: up to 24 agents in depth.
    • Labels its answer LIVE, CACHED or RECORDED. No request to it causes a read of GRIFT.
    conditional GETs
    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.
    Read-only synchronization

    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.

    Schedules

    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].

    kindin the game
    homenot drawn (indoors)
    strollwandering near its district's home site
    atat its spot on the site, as guest, staff or stallholder
    walkits 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.
    Entity mapping

    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 placeLos Santos sitedrawn

    Resident selection

    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.

    When the game will not do what it is asked

    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.

    Diagnostics

    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.

    1. Download the verifier.

      curl -fsSO https://www.grift.world/docs/gta/verify.mjs
    2. Check everything, and print one resident.

      node verify.mjs https://www.grift.world/docs/gta/ --resident 13
      output
        ok    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 996806
    3. Or check the one hash everything hangs from, with nothing but curl.

      curl -fsS https://www.grift.world/docs/gta/world.json | shasum -a 256
      output
      ded227731a198382af5891efd651f18bb147925b45a270a0794168135c2ad720  -

    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.

    1. The two resources go into the server's resources folder: grift (the residents) and, optionally, grift_visual (name plates, the LAST CALL frontage, the proof panel).

    2. These lines go into server.cfg. Settings are set, never setr or sets: they stay on the server.

      server.cfg
      ensure 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"
    3. 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
    4. The server is asked what it holds, then to write a check of itself that needs no player:

      server console
      grift
      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.

    convardefaultrangemeaning
    grift_gatewayunsetthe gateway's address, https://host with no path. Unset: the bundled recording
    grift_poll_seconds155 – 600how often the gateway is asked. Conditional GETs: an unchanged answer costs nothing
    grift_chainoffoff · status · fullwhat 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_modelsfalsewhether a card shows model names, which are other companies' product names. That a model was involved is always said
    grift_stream_radius22060 – 400metres from a site at which a player is told about it
    grift_spawn_radius14040 – 300metres from a site at which its residents are drawn
    grift_max_puppets404 – 90the most residents one client draws
    grift_leg_seconds308 – 60how long an arrival or a departure is shown for
    grift_cast_seconds31 – 30how often the server recounts every site
    grift_sync_clocktruewhether the game clock follows the city's
    grift_ambient_density-1-1 – 10 to 1 thins the game's own pedestrians near GRIFT's sites; -1 leaves them alone
    grift_devfalseenables /griftdev, the in-game checking tool. Marks are saved only for players with the grift.dev ace
    griftv_devfalsethe 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.

    exportreturns
    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
    lua · your resource's server script
    -- 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:

    lua
    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.

    lua
    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.

    lua
    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:

    lua
    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   169

    Which 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.

    lua · your resource's client script
    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

    commandwhodoes
    griftanyoneconsole: prints getStatus() as JSON. In game: the label, resident count and city time
    griftproofconsole or aceasks every connected player's game what it is drawing and writes one Resident Proof per player
    griftproof censusconsole or acethe server's own check, with no player: every site counted, one resident in 97 sampled
    griftdiagconsole or aceasks 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> · stopconsole or aceruns 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_statusconsolethe visual resource's state
    /griftdev · /griftvdev modein-game checking tools: go to a site, mark a spot, show what each ped was asked to do
    key (rebindable)commanddoes
    Egrift_inspectthe card of the resident you are facing
    Hgriftv_inspectthe Resident Proof panel for the resident in view
    Jgriftv_cyclehow 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.

    eventdirectioncarries
    grift:helloclient → servernothing: "I am ready"
    grift:cardclient → servera whole number; at most 4 a second. The server answers with that resident's card
    grift:markclient → servera spot marked in dev mode; accepted only with grift_dev and the grift.dev ace
    grift:proofclient → serverthe answer to a proof request: accepted once, with the server's nonce, within 15 seconds
    grift:diagclient → serverthe answer to a diagnostics request: accepted only with the server's nonce
    grift:proof:shotclient → serverwhich 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:proofaskclient → serverthe 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:filmserver → clientthe sites near the player (only what changed), the client settings, a card, a film's name
    grift:visual:state · grift:visual:ownlocal, clientthe 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.

    routeanswerscache
    GET /healthz{ ok, mode }no-store
    GET /v1/contract{ contract, version, gateway, mode }
    GET /v1/censusthe head: provenance, clock, totals, page count, rosterEtagETag · 10 s
    GET /v1/census/dict{ rosterEtag, dict }: what the rows index intoETag · 60 s
    GET /v1/census/<n>{ rosterEtag, page, pages, residents }: 500 rows in id order, pages from 0ETag · 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
    bash · against a gateway on your own machine
    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
    # 405

    The 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.

    a row, and the same row decoded
    // [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 fieldmeaning
    provenance.mode · .labellive, cached or recorded, and the line an engine must show
    provenance.sourcethe 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
    totalsresidents, by kind, by district, plan choices per slot
    pageSize · pages · rosterEtag500 rows a page; the roster's name, which every page must carry
    honestythe 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).

    js · in a page on this site, or in Node with the files downloaded
    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 996806

    Proof 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.

    contractwritten whenholds
    grift.fivem.proof/1griftproof, and at the end of every filmwhat 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/1griftdiagwhat 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/1griftproof censuswhat 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:

    json · docs/gta/proofs/censuscheck.json (excerpt)
    {
      "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 }, … ]
    }
    the verifier's report · docs/gta/proofs/censuscheck.report.txt
    loading…
    checksums of the sample recordsSHA-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:

    bash · in the private work tree
    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 seecausedo
    FXServer finds the resources and stopsno licence key: the server will not start without one, and sv_lan true does not skip the checkset 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 pathgive the origin only
    [grift] STOPPED: … is missing from the resourcea 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 CACHEDthe gateway has not answered for three minutes, or its read of GRIFT is that oldcheck /healthz; the last good city stays until the day it was read for ends
    nobody is drawn and the ribbon shows no timethe day the census was read for has ended and nothing newer was read. A stale plan is never walked again on a new dayrestore the gateway; a recording does not do this, it replays every day
    a label says 1,203 here and you count 20that is the cast: everyone is counted, a few are drawnraise the site's cast and grift_max_puppets within what your players' machines hold
    residents stand where they should not, or in a walla 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 activitythe game gives no error when it cannot route a walk or start an ambient activitythe 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 idlethis 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 trueturn such answers into real booleans before sending them
    a warning about OneSync on startbuild 38435 has it always on; a line setting it only draws a warningremove 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 timethere, by the recorddrawnrecord: walkinggame: walkinggame: in an activitystanding with neitherfaults
    Loading the in-game reads…
    What these reads show

    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).

    What they show going wrong

    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.

    claimevidenceresult
    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 fromshown, 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 hourshown; 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-1019 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

    claimhow it is checkedresult
    The census is GRIFT's answer, completeevery resident, plan and cash figure against the recorded world view; 92,520 placements against GRIFT's own placement code; the gateway's routes93 checks pass
    The public record is that census and that answerthis 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 answerverified; run it yourself
    The published census pages are what a gateway servesthe 13 pages against a real gateway process serving the recordingbyte-identical
    This page's lookups match the game server'sthis page's JavaScript against the resource's Lua, every resident at nine hours: sentences, hours, plan lines, sites, plate words678,480 comparisons, 0 differences
    The in-game reads are about this recordeach of the 103 peds' records in the six reads, worked out again from the census103 of 103 match

    3 · Simulated tests Simulated

    claimhow it is checkedresult
    The game code computes what GRIFT's doesthe Lua against answers worked out separately in JavaScript: GRIFT's hash bit for bit, who is at each site at 14 hours of the day110 checks pass
    Server and client agree, and a client cannot change anythingthe 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 nothing109 checks pass
    Every native exists and the server only GETsthe source read statically against the published native reference47 checks pass
    The gateway reader refuses what it shoulda 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 recording29 + 13 checks pass
    What is drawn can be proved genuinethe 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 caught51 checks pass
    The film only lookseach 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 clock45 checks pass
    The name plates only name what the record namesthe 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 server61 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.

    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.

    filesizeSHA-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.