Skip to content

Repository files navigation

Tether

CI

Lightweight DataGrout connectors for game engines.

Connect your game to DG's full intelligence layer in minutes. Free tier auto-provisions on first connect -- no API key, no dashboard, no configuration.

local Tether = require(game.ServerStorage.Tether)
local dg = Tether.connect()

-- Query game logic facts (free)
dg:query("fishing-game", "fish(X, rare, _)", function(results)
  spawnRareFish(results)
end)

-- Let the LLM write your game rules (Community+)
dg:generate_rules("rare fish only appear at night during rain", "fishing-game", function(rules)
  print("Rules generated: " .. rules)
end)

-- Call any DG tool directly -- 150+ available on signup
dg:call("data-grout@1/math.trend@1", { values = salesData }, function(result)
  updateLeaderboard(result)
end)

Engines

Engine Language Client Status
Roblox Luau tether-lua/Tether.lua shipped (Wally, Studio plugin, manual)
LÖVE (love2d) 11 / 12 Lua tether-lua/love/tether_love.lua shipped
Solar2D (Corona) Lua tether-lua/solar2d/tether_solar2d.lua shipped
Defold Lua tether-lua/defold/tether_defold.lua shipped
Godot 4.3+ GDScript tether-gdscript/addons/tether/ shipped (addon; see its README)
Unity C# tether-csharp/ planned
Unreal C++ tether-cpp/ planned

All Lua clients share one implementation, tether-lua/TetherCore.lua (plain Lua 5.1+ / LuaJIT / Luau, zero dependencies). Each engine adapter is a short file that supplies HTTP, JSON, timers and identity storage. The API below is identical on every engine; the Godot addon exposes the same methods in GDScript (tether-gdscript/README.md).

Every client is written in its language's house style and checked in CI: StyLua + luacheck for Lua, gdformat + gdlint for GDScript, rustfmt + clippy for the CLI. Both the Godot and Roblox style guides indent with tabs; .editorconfig pins them to 4 columns for editors that honor it, and on GitHub you can append ?ts=4 to a file URL.

Getting Started (Roblox)

Two paths depending on your workflow. (A Studio plugin with a live logic-cell inspector is planned; Studio gained WebSocket support in late 2025, so that is where it will land first.)

Rojo + Wally

# wally.toml
[dependencies]
Tether = "datagrout/tether@0.4.0"
wally install
tether init          # bootstrap credentials → writes TetherConfig.lua
rojo serve           # TetherConfig syncs alongside Tether into Studio

tether-cli is a small Rust binary: cargo install tether-cli or download from the releases page.

TetherConfig.lua is gitignored automatically. Teammates run tether init once on their machine.

The package builds (via tether-lua/default.project.json) to a single Tether ModuleScript with TetherCore and TetherUtils as children, so require(Packages.Tether) returns the module directly.

Manual (any setup)

Drop tether-lua/Tether.lua, TetherCore.lua and TetherUtils.lua into ServerStorage. First connect() call bootstraps a free-tier account automatically.

Security: keep Tether and TetherConfig.lua in ServerStorage (or ServerScriptService), never ReplicatedStorage — everything in ReplicatedStorage replicates to every player's client, credentials included. Tether is server-only anyway: Roblox only allows HTTP from the server.

Getting Started (LÖVE, Solar2D, Defold)

Copy tether-lua/TetherCore.lua and the adapter for your engine into one folder of your project (LÖVE also needs TetherJson.lua; it ships no JSON library). Then:

-- LÖVE
local tether = require("lib.tether.tether_love")
function love.load()   dg = tether.connect({ agent_name = "my-game" }) end
function love.update() tether.update() end          -- pumps responses + timers

-- Solar2D
local tether = require("lib.tether.tether_solar2d")
local dg = tether.connect({ agent_name = "my-game" })

-- Defold (from a long-lived game object's script)
local tether = require("tether.tether_defold")
function init(self) self.dg = tether.connect({ agent_name = "my-game" }) end

dg:query, dg:assert, dg:watch, dg:invoke, dg:call and the rest work exactly as on Roblox. LÖVE 12 has the required https module built in; LÖVE 11 needs lua-https.

Where the identity lives. On these engines the game binary is the client, so shipping a client_secret inside it would hand your credentials to every player. Instead the first connect() runs the DG onramp and stores a per-installation free-tier identity in the engine's save location (LÖVE save directory, Solar2D DocumentsDirectory, Defold sys.save). Nothing secret ships in the build. If you want one shared identity — a dev build, a game server, a relay you run — pass credentials = {...} to connect() or put a TetherConfig.lua from tether init on the package path; Tether warns when it sees bundled credentials in a fused/device/release build.

Conformance. tether-lua/test/run.lua is the protocol's test suite: it runs the core against a mock gateway and drives each adapter with fake engine globals. Every adapter must pass it (lua tether-lua/test/run.lua). tether-lua/test/live.lua runs the same contract against a real gateway (TETHER_BASE_URL=http://localhost:4000 lua tether-lua/test/live.lua, curl on PATH, onramp enabled).


Protocol

See tether-protocol/PROTOCOL.md for the JSON-RPC message format, auth flow, the _dg metadata block, failure grammars, idempotency, and the client conformance list.

Credits

Every call pays the gateway base. Deterministic tools (logic.*, data.*, math.*, frame.*, batteries.*) add nothing on top, so they cost one credit each and are what a game loop should call. dg:batch runs up to 20 logic operations for two credits: one call per tick, not four. Model-backed tools (prism.*, latent.*, inference.*, agents.*) add a model term and belong on join, on quest generation, on rule setup, not per frame.

The gateway reports what each call cost inside its _dg block; Tether strips it from your results and keeps it for you:

dg:status().credits
-- { charged = 1, estimated = 1, remaining = 4999,
--   terms = { gateway_base = 1, tool_premium = 0, model = 0 } }

When the balance runs out the gateway answers insufficient_credits; Tether switches to status credits_exhausted, fails calls locally for 30 seconds so a game loop cannot hammer the network, then lets one call through as a probe. Recovery is automatic. Low balances are warned to the developer console, never to players.

Splash Screen Program

Credits for games come from a trade: a "Powered by DataGrout" splash on join, in exchange for a larger allotment. The program is commercial, not protocol; the tiers and their allotments are being redefined alongside metered pricing for real-time clients, so treat the numbers below as the shape of the deal rather than a quote.

Tier Requirement Allotment
Free None development-sized
Community "Powered by DataGrout" splash (3 s on join) enough to run model-backed tools in a live game
Partner Splash + DG link in game description larger
Featured Splash + link + featured on the DG showcase largest
Paid Billing on the dashboard purchase-based

Splash variants a developer may pick from: "Logic by DataGrout", "Minds by DataGrout", "Intelligence by DataGrout", "Reasoning by DataGrout", or "This world thinks. Powered by DataGrout." Upgrading is self-serve at https://app.datagrout.ai/tether; Tether detects the new allotment on the next call, no code changes.

API

Named convenience methods

dg:query(namespace, prolog, callback)          -- Query LC facts
dg:assert(namespace, facts, callback?)         -- Assert facts into LC
dg:batch(namespace, ops, opts?, callback)      -- Several logic ops, one charged call
dg:watch(namespace, pattern, callback, opts)   -- Poll for fact changes
dg:invoke(prompt, opts, callback)              -- LLM inference            [model term]
dg:orchestrate(agent_id, input, callback)      -- Multi-agent workflow     [model term]
dg:generate_rules(description, ns, callback)   -- LLM writes your Prolog   [model term]
dg:batteries().list(callback)                 -- Browse battery catalog
dg:batteries().install(id, ns, callback)      -- Install a battery
dg:game_rules(namespace, callback)            -- List installed batteries

dg:batch takes the ops logic.batch accepts: { op = "query", prolog = ... }, { op = "assert", facts = {...} }, { op = "forget", ... }; later ops may reference earlier query bindings with "{{op1.X}}".

General escape hatch -- call any DG tool

dg:call(tool_name, args, callback)

Exposes the full DG catalog: 150+ tools are available on signup, more behind feature flags. discovery.summary lists what your server can see.

-- Deterministic tools -- one credit, safe in game loops
dg:call("data-grout@1/math.trend@1",    { values = {10,12,15,11,18} }, cb)
dg:call("data-grout@1/math.rank@1",     { values = playerScores }, cb)
dg:call("data-grout@1/data.filter@1",   { data = items, where = "level > 5" }, cb)
dg:call("data-grout@1/data.sort@1",     { data = leaderboard, by = "score" }, cb)
dg:call("data-grout@1/frame.group@1",   { data = events, by = "player_id" }, cb)

-- Model-backed tools -- call intentionally
dg:call("data-grout@1/prism.analyze@1", { data = gameLog, goal = "find patterns" }, cb)
dg:call("data-grout@1/latent.expand@1", { seed = "dungeon", domain_hint = "fantasy rpg" }, cb)

Meta

dg:status()
-- {
--   status    = "connecting"|"connected"|"credits_exhausted"|"disconnected",
--   credits   = { charged, estimated, remaining, terms },  -- from the last call
--   last_meta = { execution_id, ctc, cache_ref, ... },     -- the full _dg block
--   mcp_url = "...", rpc_url = "...", platform = "roblox", version = "0.4.0"
-- }

Tether.connect(opts) also accepts request_timeout (seconds, default 30), credits_cooldown (default 30), low_credits_threshold (default 20) and idempotency = false to omit idempotency keys.

Robustness

Every call carries an idempotency key that survives retries, so a dg:assert that times out and is retried cannot write twice. An expired bearer is refreshed once and the call replayed. Transport failures and 5xx retry three times with jittered backoff; 429 waits the server's Retry-After. A tool that rejects its input inside a successful envelope (error + status_code) reaches your callback as an error, not as data.

License

MIT — see LICENSE.

About

Lightweight DataGrout client for Roblox: connect your game to logic cells, batteries, and 100+ DG tools from Luau

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Contributors

Languages