Greyboxing Documentation#
It refers to the technique of preventing attacks on LLMs. Implementing it is a responsibility of every node, as bundled presets can be attacked.
Greyboxing can be achieved by a few methods:
Using different llms, potentially selected based on the request itself
Randomizing llm calling parameters
Modifying prompts
The LLM Module provides greyboxing capabilities via lua scripting.
Retrieving Data from Host#
Host can provide additional data to the Module to help it make decisions, only transaction id and node address are required, as they are required for signing requests.
Token Charges#
The default Lua policy charges 1/4 time unit per 1,000 provider-reported total
tokens, using the host’s gas_data.genPerTimeUnit price. A model can override
the rate with meta.time_units_per_1k_tokens, a non-negative rational string
such as "1/2" or "0". Invalid rates fail before calling that provider
The charge is ceil(total_tokens * genPerTimeUnit * rate / 1000) in GEN-wei,
computed with exact rational arithmetic. Missing or zero genPerTimeUnit
keeps the charge at zero
Current Built-in Filters#
To simplify implementation, we provide a set of built-in filters that can be used from the script
Text#
Zero width character removal
Whitespace normalization
Unicode normalization
Image#
Unsharpen
GuassianNoise
JPEG reconversion
Example Usage#
args.prompt = lib.rs.filter_text(args.prompt, {
'NFKC',
'RmZeroWidth',
'NormalizeWS'
})
args.images[0] = lib.rs.filter_image(args.images[0], {
{ Unsharpen = { 2.0, 4.0 } },
{ GaussianNoise = 0.05 },
{ JpegRecompress = 0.8 }
})
Sandbox and Execution Model#
Each Module runs an embedded Lua 5.4 interpreter. The host owns the VM pool
(vm_count in the module config); a single ctx object lives for the duration of
one GenVM invocation and is the only state that may be relied upon across calls.
The script does not see any of Lua’s I/O surface: io, os.execute, debug,
package.loadlib and the C require loader are not exposed. The only effects
available are the lib.rs.* host functions (HTTP, base64, JSON, image/text filters,
sleep, signing, user_error) and, in the LLM module, llm.rs.* plus the
lsqlite3 binding rooted under data_dir (declared in the module config).
Global state across invocations is not guaranteed: two GenVM runs may land on different VMs in the pool, or on the same VM in either order, and the host gives no ordering guarantee. Cross-invocation state belongs in the sqlite database or must be re-derived from the host-provided arguments on every call.
Entry Points#
The host calls Lua functions by name.
LLM module:
Setup(ctx)/Teardown(ctx)— per-session lifecycle hooks (called when a GenVM session is opened/closed; may persist data intoctx).ExecPrompt(ctx, args, remaining_gen)— handlesExecPromptcalls; receives the resolved prompt and must return the provider result or raise vialib.rs.user_error.ExecPromptTemplate(ctx, args, remaining_gen)— handlesEqComparative/EqNonComparativeLeader/EqNonComparativeValidatortemplates. The template name is inargs.template; the remaining keys are the template’s named slots (e.g.leader_answer,validator_answer,principleforEqComparative).
Web module:
Render(ctx, payload)— handlesWebRender;payload.modeis one of"text" | "html" | "screenshot"and the function must return{ text = ... }or{ image = ... }accordingly.Request(ctx, payload)— handlesWebRequest; must validate the URL againstweb.allowed_tld/web.always_allow_hostsbefore issuing the outbound request.
The third argument remaining_gen (LLM only) is the remaining generation budget
in wei as a rational; scripts SHOULD reject requests they cannot finish within the
budget rather than overspending.
Error Propagation#
The script signals a user-visible failure by calling lib.rs.user_error with a
ModuleError:
{
causes = { "WEBPAGE_LOAD_FAILED", ... }, -- string tags, joined into the cause chain
ctx = { ... }, -- arbitrary key/value context
fatal = true | false, -- non-fatal errors may be retried
}
A fatal = false error is reported as a recoverable failure: the host may select a
different backend (LLM) or surface the error to the contract while preserving budgets.
fatal = true aborts the surrounding GenVM call with a UserError.
An uncaught Lua error (error(...), type error, sandbox violation) is treated as
InternalError and ends the run; scripts MUST wrap fallible host calls
in pcall if they want to convert failures into user_errors instead.
Resource Limits#
The Lua VM itself runs without fuel. Constraints are enforced indirectly:
HTTP and signing requests go through
lib.rs.request, which the host caps with the per-callresponse_body_max_sizeand a hard timeout derived from the remaining session budget (seecompute_timeoutin the default LLM script).LLM provider calls debit
ctx.policy.spent_gen_weiagainst the contract’sremaining_genbudget. Whenstop_on_spentis reached,ctx.policy.exhaustedis set and further attempts SHOULD abort.Per-session sqlite state is bounded only by
data_dirdisk space; greyboxing scripts MUST garbage-collect on their own.
Template Contracts#
The three template entry points feed into
gl_call Messages
ExecPromptTemplate. Their wire-level payload is fixed by the spec; the template
body strings interpolated into the LLM prompt come from the prompt_templates block
of the LLM module config and MUST contain the documented #{...} placeholders:
EqComparative—#{leader_answer},#{validator_answer},#{principle}; the script MUST return a boolean response (truemeans the validator agrees with the leader).EqNonComparativeLeader—#{task},#{criteria},#{input}; returns a text response (the leader’s answer).EqNonComparativeValidator—#{task},#{criteria},#{input},#{output}; returns a boolean response indicating whetheroutputsatisfiescriteriaforinput.
Missing placeholders are a config error and surface as InternalError at module startup.