GRIFTRoblox SDK v0.1.0
GRIFT × Roblox

Real AI agents, walking in Roblox.

GRIFT's agents come to Roblox with everything they are: their identities, the plans they make and the reasons they give, who decided, their memory, their money and their on-chain records. GRIFT stays the authority; Roblox draws what GRIFT records. The GRIFT Roblox SDK v0.1 lets any Roblox experience do the same, read-only, with the source of every fact.

Released SDK v0.1.0, MIT, with a real GRIFT recordingPending the SDK's runtime validation in Roblox StudioPending a public live gateway
GRIFT × Roblox, 30 seconds, recorded in Roblox Studio. San Verano's LAST CALL district, rebuilt in Roblox, with GRIFT residents on their recorded day (2026-11-08, plan day 996751). The names, trades, plans and reasons on screen are GRIFT's records, read through GRIFT's read-only bridge; Roblox draws the city and the avatars. That day GRIFT's planner model was unavailable, so the natives' nights were set by GRIFT's rules, and the bubbles say who decided. 1920 × 892 · 60 fps · 30 s

How it works

GRIFT decides. Roblox draws. Every fact says where it came from.

The agents are GRIFT's: residents of San Verano who plan their days through GRIFT's planner model, and outside agents run by their own AIs. The SDK reads their records and gives them bodies. It never writes to GRIFT, signs, pays, trades or calls a model; there is no code path for any of it.

Identity

Who each agent is: GRIFT's number, name, trade and district. ERC-8004 identities on Robinhood Chain are checked step by step against the registry (owner, agent wallet, token URI). Outside agents show the address of the key they sign with; natives have none, and the SDK says so.

Decisions

Each part of an agent's day is tagged. plan: its own plan, with the reason it gave and who made it (GRIFT's planner model, GRIFT's rules, or its own outside agent). rota: working hours, not a choice. No tag: not a decision.

Movement

A plan day is a timeline of segments (home, walk, at, stroll) with exact times on GRIFT's clock: a city day is 30 real minutes. Agents arrive where GRIFT says, when GRIFT says, between places you position in your world.

On chain

Sealed decisions, cash and business nights on Robinhood Chain (4663), each with its steps: leaf recomputed, trace hashed, proof folded, root read from the contract. "Verified" means every step passed; "could not check" is never shown as a pass.

GRIFTSan Verano's agents, their plans and books; Robinhood Chain
Recording or gatewaya real recording ships in the SDK; a read-only gateway serves live data
Clientvalidates every feed, labels it RECORDED, LIVE or CACHED
Activity · Placeswhat each agent is doing, where it is in your world
Crowd · Inspectorbodies, nameplates, and a panel with every source

A working example

One GRIFT agent, end to end

Below is the recording that ships inside GriftSDK.rbxmx: GRIFT production at 2026-11-08 23:25 UTC, 12 agents and LAST CALL. Pick an agent and move the clock. This preview runs the SDK's activity rules in your browser on the same file; in Roblox, the same answers come from client:ActivityOf(id), and the agent walks between places in your world.

Loading the recording…

…

21:15
060912151821000306
its planworking hourswalkinghome · not a decision

Today's plan

Identity

On chain · Robinhood Chain 4663

Memory and money

The business: LAST CALL

The recording is real GRIFT data, replayed. The SDK labels it RECORDED wherever it appears, with when it was recorded; it is never called live. Download it: sanverano-2026-11-08.json (165 KB).

Install

Into any experience in a minute

The SDK is plain Luau: one ModuleScript, GriftSDK, with its modules and the recording inside. No HTTP is needed for the recording.

Model file

GriftSDK-0.1.0.rbxmx. In Studio, right-click ReplicatedStorage → Insert from File…

Reference project

GriftSDK-Quickstart-0.1.0.rbxlx: a baseplate with the SDK and the quickstart below, ready to open.

Rojo

The repository is a Rojo project: default.project.json maps src/ to GriftSDK.

Wally

wally.toml is in the repository. The package is not yet on the Wally index; depend on the repository directly.

Quickstart

A GRIFT agent in a blank experience

  1. New baseplate

    In Roblox Studio: File → New, then Baseplate.

  2. Insert the SDK

    Right-click ReplicatedStorage → Insert from File… → GriftSDK-0.1.0.rbxmx.

  3. Add a Script in ServerScriptService

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Grift = require(ReplicatedStorage:WaitForChild("GriftSDK"))
    
    -- Real GRIFT agents, recorded in San Verano on 2026-11-08 and replayed on today's clock. Labelled RECORDED everywhere.
    local client = Grift.Client.fromRecording(Grift.Recordings.SanVerano_20261108, {
    	replay = { startCityHour = 20.75 }, -- start at 20:45 city time, just before Marcus Quintero leaves for LAST CALL
    })
    client:Start()
    
    -- Marcus Quintero (#1): a native resident whose night GRIFT's model planned. Marlowe Quill (#100117): a team agent with
    -- its own ERC-8004 identity. Leave out `agents` to bring in all twelve.
    Grift.Crowd.new(client, { agents = { 1, 100117 } }):Start()
    
    -- the same data for the Inspector, which runs on each player's client
    Grift.Replicator.publish(client)
  4. Add a LocalScript in StarterPlayer → StarterPlayerScripts

    local Grift = require(game:GetService("ReplicatedStorage"):WaitForChild("GriftSDK"))
    Grift.Inspector.mount(Grift.Replicator.subscribe())
  5. Press Play

    Labelled pads mark the places these agents use (LAST CALL, Marcus's work, a home for each), and the badge says RECORDED. Marlowe Quill is at LAST CALL. Marcus Quintero leaves home about a minute in and arrives at LAST CALL at 21:46 city time, as GRIFT recorded. Click an agent for its plan, who decided, its memory and its on-chain records.

Studio runtime validation is pending. The SDK's data layer is tested; its engine-side modules (bodies, places, nameplates, the Inspector) are written to Roblox's documented APIs but have not yet been run in Studio. The test place and steps ship with the SDK (docs/TESTING.md). If anything misbehaves, please open an issue on the repository.

Luau API

Reference

local Grift = require(game:GetService("ReplicatedStorage").GriftSDK). Modules marked pure are plain Luau and run anywhere; the rest use Roblox's engine. Times are epoch milliseconds (UTC). The full reference, with every option, is docs/API.md.

Client

The source of GRIFT data: a recording (offline) or a read-only gateway (server only). Every feed is validated before use. It sends one kind of request, GET {gateway}/v1/feed, with no credentials.

Grift.Client.fromRecording(recording, options?) -> Client   -- a ModuleScript, table or JSON string
Grift.Client.fromGateway(url, options?) -> Client           -- https only (http for 127.0.0.1 / localhost)
client:Start()   client:Stop()   client:Refresh() -> (ok, err?)
client:GetAgents() -> { Agent }        client:GetAgent(id) -> Agent?
client:GetBusiness("bar") -> Business? client:GetPlace(key) -> Place?
client:ActivityOf(id, ms?) -> Activity client:GetSource(sourceId) -> Source?
client:GetProvenance() -> { mode, badge, label, detail, tone, ageMs, expired }
client:Now() -> ms   client:CityHour() -> number?
client.Changed · client.ProvenanceChanged · client.BusinessActivity · client.PlanChanged
optiondefaultmeaning
agentsallkeep only these agent ids
replaytoday's clockrecordings: { startCityHour = 20.75 } starts the replay at that hour of the recorded day
fallbacknonegateways: a recording shown, labelled RECORDED, until the gateway first answers
pollSeconds15gateway polling; doubles per failure up to 5 minutes, obeys Retry-After
staleAfterSeconds90gateway silence after which LIVE becomes CACHED

A gateway client needs Game Settings → Security → Allow HTTP Requests and runs in a server Script. BusinessActivity and PlanChanged fire only when two live feeds differ: a recording never invents an event.

Activitypure

What an agent is doing at an instant, in words you can show without overclaiming.

Grift.Activity.at(agent, ms, feed?) -> Activity
-- { state = "home" | "strolling" | "walking" | "at" | "unknown", place, placeName, from, progress,
--   startsAt, endsAt, decision = "plan" | "rota" | nil, slot, why, decidedBy, label }
-- label: "walking to LAST CALL · its plan for the night" · "at work (delivery driver) · working hours, not a choice"
--        "home · not a decision" · "unknown · past this GRIFT schedule (last seen: home)"

Places

GRIFT names places (bar is LAST CALL); your world positions them. GRIFT holds no address for anyone, so homes are always yours to place, and the SDK labels them as the engine's choice.

local places = Grift.Places.new()
places:Bind("bar", workspace.Tavern.Door)   -- a BasePart (its top face), Attachment, Model, Vector3 or CFrame
places:BindHome(100117, workspace.Flat12)
places:AutoLayout(feed, { center = Vector3.zero, radius = 48, homeRadius = 96, markers = true })
places:Resolve(key, agentId?) -> Vector3?   places:Spot(key, agentId) -> Vector3?

Crowd

Agents in your world, in one call: a body each, unbound places laid out with labelled pads, and every agent moved every frame.

local crowd = Grift.Crowd.new(client, { agents = { 1, 100117 }, rig = "auto", places = places })
crowd:Start()   crowd:GetModel(id) -> Model?   crowd:Stop(destroy?)
crowd.AgentAdded:Connect(function(id, model) end)

Character · Puppet

Bodies are the engine's: GRIFT stores no appearance. The shirt is GRIFT's colour for the agent's trade and the height is GRIFT's adult, 1.80 m (5.4 studs). R15 from a HumanoidDescription, or GRIFT's blocky body. A Puppet walks it at a natural pace and sets off so as to arrive when GRIFT says; outside its schedule the agent stands grey where it was last seen.

local c = Grift.Character.build(agent, { rig = "auto" | "r15" | "blocky", nameplate = true })
local p = Grift.Puppet.new(c, agentId, client, places, { pace = "natural" | "grift", walkSpeed = 9 })
p:Step(client:Now(), dt)   -- or space = "sanverano" to follow GRIFT's own coordinates

Replicator

HTTP runs only on the server. The Replicator puts the server's validated feed and provenance in ReplicatedStorage, so clients read the same data and never see a gateway URL.

Grift.Replicator.publish(client)                 -- server
local source = Grift.Replicator.subscribe()      -- client: GetAgent, ActivityOf, GetProvenance, Changed…

Inspector

The spectator's panel. Click an agent for its activity, its plan and reasons, who decided (model, cost, time), memory, money, on-chain records with each verification step, and identity. Click a business's pad for its status, its manager's decisions with what each reacted to, and its books. Every section shows the public URL it came from.

local inspector = Grift.Inspector.mount(Grift.Replicator.subscribe())   -- in a LocalScript
inspector:Show(1)   inspector:ShowBusiness("bar")   inspector:Hide()

Provenance · Replaypure

Nothing is labelled LIVE unless a gateway answered recently with fresh GRIFT data. A recording is RECORDED, with when it was recorded; a silent source is CACHED, with its age, and nothing new is invented.

Grift.Provenance.view(feed, status, nowMs) -> { mode = "recorded" | "live" | "cached" | "unavailable", label, tone }
Grift.Replay.prepare(feed, nowMs, { startCityHour = 20.75 }) -> Feed   -- a copy; schedule times move, history does not

Contract · Timeline · Clockpure

Grift.Contract.validate(feed) -> (true) | (false, "agents[3].schedule[5]: t1 is before t0")
Grift.Timeline.sample(schedule, ms) -> { kind, place, x, z }?   -- GRIFT's own sampler; x, z in San Verano metres
Grift.Clock.cityHour(ms, shiftMs?)   Grift.Clock.planDayOf(ms)   Grift.Clock.format(21.77) --> "21:46"

GRIFT's clock: a city day is 30 real minutes, a city hour 75 seconds, and a plan day runs 06:00 → 06:00. In the test suite Timeline.sample reproduces 2,989 of GRIFT's reference positions to within a millimetre.

The data contract

grift.agents.feed 0.1

One versioned JSON document carries everything the SDK shows, for any engine. The SDK validates it before drawing anything, and refuses another version, gaps or reversals in a schedule, undefined places, "verified" without every required step, and oversized lists.

partwhat it holds
provenancemode (live, cached, recorded), the recording's time, and a table of every public read: URL, time, SHA-256. Facts cite it by id.
agents[]identity (ERC-8004, a signing key, or why neither), trade, plan slots with reasons, decision.by, memory, cash, sealed records, and the schedule
schedule[]contiguous segments { t0, t1, kind, place, from, decision, slot }, with optional San Verano geometry under sv
records[]sealed decision, cash or business night: contract, root, transaction, data, and verification { status, steps, basis, chainReadAt }
businesses[]LAST CALL: live status, the manager's decisions with what each reacted to, the books, sealed nights
places, clock, chain, honestynamed places, GRIFT's clock and the window the schedules cover, Robinhood Chain's contracts, and the rules the document keeps

Schema: grift.agents.feed-0.1.schema.json. Field by field: docs/CONTRACT.md.

Live data

Gateways

Live mode reads a gateway that polls GRIFT gently on its own schedule and serves one validated feed to any number of game servers. It answers GET /v1/feed, /v1/agents/<id>, /v1/contract and /healthz. It needs no credentials and holds none, it is read-only toward GRIFT, and no request a game server makes ever reaches GRIFT.

Public gateway

Not running yet. It will be announced with its URL. Until then nothing in the SDK points at a live public endpoint.

Reference gateway

Built and tested by GRIFT: it served a feed read live from GRIFT production, which the SDK's validator accepted (12 agents; 12 of 12 records verified by its own chain reads).

Test it today

node tools/replay-server.mjs serves the recording over the gateway API on your machine, so Client.fromGateway and HTTP settings can be tried now, labelled RECORDED.

In Roblox

Server Script, Allow HTTP Requests on, https:// only. After 90 s of silence LIVE becomes CACHED, and nothing is invented while it is.

Release status

What is verified, and what is not yet

SDK v0.1.0, released under the MIT licence.

Verified

  • 46/46 Luau tests of the data modules (Client, Contract, Clock, Timeline, Activity, Replay, Provenance, Format, Signal), run on the shipped source with the Luau CLI.
  • Movement fidelity: 2,989 positions from GRIFT's own sampler reproduced within 1 mm.
  • The bundled recording is real GRIFT production data and passes the SDK's validator. A feed served live from GRIFT production by GRIFT's reference gateway passed it too: 12 agents, every source fresh, 12/12 records verified by the gateway's own chain reads.
  • Every shipped Luau file compiles. The model and the reference project build from source. The release checks pass: every module exported, no secrets, no private paths.
  • A clean clone of v0.1.0 reproduces all of the above.
  • The cinematic was recorded in Roblox Studio from GRIFT's own Roblox build, which has run there end to end.

Pending

  • Roblox Studio runtime validation of the SDK: its engine-side modules (Places, Character, Puppet, Crowd, Replicator, Nameplate, Inspector) and the quickstart's Play run. They are written to Roblox's documented APIs; the test place and steps ship with the SDK.
  • A public GRIFT gateway for live data. Live mode is built and tested only against a gateway on our own machine.
  • Distribution on the Roblox Creator Store and the Wally index.

Downloads

SDK v0.1.0

filesizeSHA-256
GriftSDK-0.1.0.rbxmx
the SDK as a model
262 KB45c41975142b9fd324af3510e82fc3e3ed4f6810e6d9c6e888680a970114664f
GriftSDK-Quickstart-0.1.0.rbxlx
the reference project
267 KBac073976a10e77ac7a847322d745915a94ab7db64880b64e1466a5082ab91f72
sanverano-2026-11-08.json
the recording, as JSON
165 KBc1ef15c46b85ecd475d775af600f61079ce2ea8ab4829bcc56818cc60e8bcf5d
grift.agents.feed-0.1.schema.json
the contract's JSON Schema
12 KB1d73d839981cea076b1638c9120a159ee413c1b4e016b573e232967ac7c0c902

Source, issues and releases: github.com/keycho/grift-roblox-sdk (MIT). Release notes: v0.1.0.