You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add OP_INSPECTINTENTMESSAGE opcode to inspect intent message in SubmitIntent context
Depends on:#60 (unified big number arithmetic). This opcode's numeric
encoding assumes #60's BigNum model; it should land after that refactor or
be rebased onto it.
Motivation
An Arkade script executed in SubmitIntent today has access to the intent
proof transaction (standard introspection opcodes) and to transaction
extension packets (OP_INSPECTPACKET / OP_INSPECTINPUTPACKET), but has no
way to read the RegisterMessage JSON that accompanies the proof.
The introspector validates the message shape (ValidAt / ExpireAt) at the
service layer, but scripts cannot express policies such as:
"This VTXO may only be spent via an intent that expires within N seconds."
"This VTXO may only be co-registered with a specific set of cosigner keys."
"This VTXO may only be registered for an intent whose output 2 is declared
as an onchain output."
"Reject registration for this VTXO if a given cosigner key is present."
All of these policies turn on fields of RegisterMessage. Exposing the
message to scripts keeps policy enforcement in the Arkade script itself
rather than requiring bespoke introspector logic per use case.
Proposal
Add a new opcode:
Word
Opcode
Hex
Input
Output
OP_INSPECTINTENTMESSAGE
246
0xf6
path
value 1 or <empty> 0
0xf6 is currently OP_UNKNOWN246, sitting next to the other
packet-introspection opcodes (OP_INSPECTPACKET = 0xf4, OP_INSPECTINPUTPACKET = 0xf5).
Semantics
Pop path as a byte string.
If no intent message is bound to the current execution context (i.e. the
script is running in SubmitTx, SubmitOnchainTx, or SubmitFinalization), push <empty> 0.
Interpret path as a gjson path
evaluated against the raw JSON bytes of the RegisterMessage.
If the query yields Null or NotExists, push <empty> 0.
Otherwise, encode the result and push value 1.
Value encoding
Numeric encoding follows the unified BigNum model from #60
(minimally-encoded sign-magnitude little-endian, up to 520 bytes), matching
what OP_INSPECTOUTPUTVALUE / OP_INSPECTINPUTVALUE push after that
refactor. Pushed values feed directly into standard arithmetic opcodes with
no conversion.
gjson result type
Pushed value
String
Raw UTF-8 bytes of the string (no JSON quoting).
Number (integer)
Minimally-encoded BigNum (sign-magnitude LE).
Number (non-integer)
Miss (<empty> 0).
True / False
OP_1 (0x01) / OP_0 (empty push).
JSON (obj/arr)
Raw JSON bytes (gjson.Result.Raw).
Null / missing
Miss (<empty> 0).
A JSON number is "integer" iff its raw textual form has no fractional part
and its exponent (if any) resolves to an integer. Numbers are parsed from gjson.Result.Raw directly, not via gjson's float64 accessor, so the
BigNum range — not float64's 2^53 exact-integer ceiling — is the
effective limit.
Why a gjson string selector
Agnostic. One opcode covers every field of RegisterMessage, now and
in the future. New fields require no code change.
Shape-flexible. Scalars, arrays, and nested additions are all
addressable through the same path language (arr.0, arr.# for length, arr.#(cond), etc.).
Whole-message access. Path "@this" returns the full raw JSON — useful
for hashing or substring commitments — without a dedicated opcode or
special sentinel.
Context propagation
The service layer plumbs the raw message bytes into the engine via a new ExecuteOption, analogous to how SetAssetPacket / SetIntrospectorPacket
work today:
application.SubmitIntent is the only caller that sets it. Other entry
points (SubmitTx, SubmitOnchainTx, SubmitFinalization) leave it unset,
so the opcode naturally returns a miss in those contexts.
The gRPC handler currently parses message into intent.RegisterMessage and
discards the raw string; it must additionally preserve the raw bytes on application.Intent for the opcode to consume.
Examples
Expiry cap — require the intent to expire within 60 s of reference time T. Under #60's unified arithmetic the pushed number feeds OP_LESSTHANOREQUAL directly:
(Clients using this pattern must reproduce the exact JSON byte sequence the
introspector received — whitespace and key ordering included.)
Non-goals
Exposing the intent message in SubmitTx / SubmitOnchainTx / SubmitFinalization. The opcode evaluates to a miss there.
Defining a canonical binary encoding for RegisterMessage. The opcode
operates on the JSON the client sent.
Supporting message types other than RegisterMessage. The opcode is
message-type-agnostic (it queries whatever JSON was supplied), but RegisterMessage is the only type reaching Arkade scripts today.
Open questions
gjson dependency. The repo does not currently depend on github.com/tidwall/gjson. Small, widely used, no transitive deps.
Alternative: a minimal dotted-path walker over encoding/json.
Recommendation: take the dep.
Non-integer numbers. Current proposal: miss. Alternative: script-fatal.
Miss is more robust against malformed messages. Parsing goes through the
raw JSON number string, so the BigNum range from Unified big number arithmetic #60 is the ceiling — not float64 precision.
Reference
Full design doc: docs/superpowers/specs/2026-04-21-op-inspectintentmessage-design.md
Add
OP_INSPECTINTENTMESSAGEopcode to inspect intent message in SubmitIntent contextMotivation
An Arkade script executed in
SubmitIntenttoday has access to the intentproof transaction (standard introspection opcodes) and to transaction
extension packets (
OP_INSPECTPACKET/OP_INSPECTINPUTPACKET), but has noway to read the
RegisterMessageJSON that accompanies the proof.The introspector validates the message shape (
ValidAt/ExpireAt) at theservice layer, but scripts cannot express policies such as:
as an onchain output."
All of these policies turn on fields of
RegisterMessage. Exposing themessage to scripts keeps policy enforcement in the Arkade script itself
rather than requiring bespoke introspector logic per use case.
Proposal
Add a new opcode:
OP_INSPECTINTENTMESSAGE0xf6pathvalue 1or<empty> 00xf6is currentlyOP_UNKNOWN246, sitting next to the otherpacket-introspection opcodes (
OP_INSPECTPACKET = 0xf4,OP_INSPECTINPUTPACKET = 0xf5).Semantics
pathas a byte string.script is running in
SubmitTx,SubmitOnchainTx, orSubmitFinalization), push<empty> 0.pathas a gjson pathevaluated against the raw JSON bytes of the
RegisterMessage.NullorNotExists, push<empty> 0.value 1.Value encoding
Numeric encoding follows the unified BigNum model from #60
(minimally-encoded sign-magnitude little-endian, up to 520 bytes), matching
what
OP_INSPECTOUTPUTVALUE/OP_INSPECTINPUTVALUEpush after thatrefactor. Pushed values feed directly into standard arithmetic opcodes with
no conversion.
StringNumber(integer)Number(non-integer)<empty> 0).True/FalseOP_1(0x01) /OP_0(empty push).JSON(obj/arr)gjson.Result.Raw).Null/ missing<empty> 0).A JSON number is "integer" iff its raw textual form has no fractional part
and its exponent (if any) resolves to an integer. Numbers are parsed from
gjson.Result.Rawdirectly, not via gjson'sfloat64accessor, so theBigNum range — not
float64's 2^53 exact-integer ceiling — is theeffective limit.
Why a gjson string selector
RegisterMessage, now andin the future. New fields require no code change.
addressable through the same path language (
arr.0,arr.#for length,arr.#(cond), etc.)."@this"returns the full raw JSON — usefulfor hashing or substring commitments — without a dedicated opcode or
special sentinel.
Context propagation
The service layer plumbs the raw message bytes into the engine via a new
ExecuteOption, analogous to howSetAssetPacket/SetIntrospectorPacketwork today:
application.SubmitIntentis the only caller that sets it. Other entrypoints (
SubmitTx,SubmitOnchainTx,SubmitFinalization) leave it unset,so the opcode naturally returns a miss in those contexts.
The gRPC handler currently parses
messageintointent.RegisterMessageanddiscards the raw string; it must additionally preserve the raw bytes on
application.Intentfor the opcode to consume.Examples
Expiry cap — require the intent to expire within 60 s of reference time
T. Under #60's unified arithmetic the pushed number feedsOP_LESSTHANOREQUALdirectly:Array length assertion — require exactly one onchain output:
Whole-message hash commitment:
(Clients using this pattern must reproduce the exact JSON byte sequence the
introspector received — whitespace and key ordering included.)
Non-goals
SubmitTx/SubmitOnchainTx/SubmitFinalization. The opcode evaluates to a miss there.RegisterMessage. The opcodeoperates on the JSON the client sent.
RegisterMessage. The opcode ismessage-type-agnostic (it queries whatever JSON was supplied), but
RegisterMessageis the only type reaching Arkade scripts today.Open questions
github.com/tidwall/gjson. Small, widely used, no transitive deps.Alternative: a minimal dotted-path walker over
encoding/json.Recommendation: take the dep.
Miss is more robust against malformed messages. Parsing goes through the
raw JSON number string, so the BigNum range from Unified big number arithmetic #60 is the ceiling — not
float64precision.Reference
Full design doc:
docs/superpowers/specs/2026-04-21-op-inspectintentmessage-design.md