Skip to content

Match charset parameter values case-insensitively - #24

Open
rvolosatovs wants to merge 1 commit into
maxcountryman:mainfrom
rvolosatovs:fix/case-insensitive-charset
Open

rvolosatovs wants to merge 1 commit into
maxcountryman:mainfrom
rvolosatovs:fix/case-insensitive-charset

Conversation

@rvolosatovs

Copy link
Copy Markdown

RFC 9110 section 8.3.2 defines charset names as case-insensitive, so Accept: text/plain;charset=utf-8 must match an available text/plain;charset=UTF-8. Other parameter values are still compared exactly.

https://www.rfc-editor.org/rfc/rfc9110.html#section-8.3.2-1

RFC 9110 section 8.3.2 defines charset names as case-insensitive, so
`Accept: text/plain;charset=utf-8` must match an available
`text/plain;charset=UTF-8`. Other parameter values are still compared
exactly.

Assisted-by: anthropic:claude-opus-5-5
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
rvolosatovs added a commit to LFDT-Nightstream/Starstream that referenced this pull request Oct 19, 2026
`GET /rpc` returns the `starstream:ledger` package describing the
ledger queries `POST /rpc` accepts. With `?index=INDEX`, optionally
qualified by `&transaction=DIGEST`, it instead returns a
`starstream:ledger` package holding only the `utxo` interface for that
unspent output: the UTXO's methods, decoded from its contract, as the
freestanding functions `/rpc` invokes, taking the output reference
before the method parameters. Unused types are pruned and the packages
the interface depends on are printed nested. The world of the contract
is dropped from its package, so a UTXO interface using types defined in
the contract's own package does not make the two packages depend on
each other.

The output reference parameters are named `transaction` and `index`,
prefixed with `utxo-` for as long as they clash with a method parameter.

Both are served as WIT text by default, or encoded as Wasm on
`Accept: application/wasm`, so clients can generate wRPC bindings
without decoding the contract themselves. `HEAD /rpc` returns the
headers of the same response.

The UTXO WIT of every instance a contract exports is built, printed and
encoded once, when the contract is published, and served from memory.
Publishing therefore now rejects, with 400, Wasm that is not a component
and contracts that themselves define a `starstream:ledger` package.
Validation and WIT extraction run on a blocking thread, and only once the
publisher has been charged, so a rejected contract consumes its nonce
and fee, and a duplicate one is rejected before any work is done.

The ledger package is encoded to Wasm once, by a build script, only when
the `server` feature is enabled. In the query, `index` is a `u32` and
`transaction` must be a digest; omitting it refers to the genesis block.
A query naming `index` or `transaction` more than once is rejected with
400, and an empty query (`GET /rpc?`) is treated as no query.

`headers-accept` is pinned to the unreleased upstream main, whose media
range matching ignores `q` when comparing parameters, so
`Accept: text/plain;q=0.9` matches the `text/plain; charset=UTF-8` the
server responds with. Matching `charset=utf-8` against it additionally
requires case-insensitive charset comparison in `headers-accept`
(maxcountryman/headers-accept#24), so lowercase charsets get 406 until
that lands.

The ledger client gains `build_utxo_wit_request` and
`Client::get_utxo_wit`, which takes an optional `Accept` media type and
returns the raw body. GET request builders share a helper.
`TransactionInput` gains `genesis` and `new` constructors.

The CLI gains `ledger utxo wit [--transaction DIGEST] <INDEX>`, printing
a UTXO's WIT from the endpoint, in the shape `ledger utxo call` invokes
it. `ledger utxo call` now resolves the method type from that endpoint,
encoded as Wasm, instead of downloading and decoding the whole contract.

The CLI e2e test now starts the ledger with the outputs of the
simulated `example` call as genesis, prints the UTXO's WIT and calls one
of its methods. Ledger tests cover parameter name clashes, `q` values,
`HEAD`, rejection of an empty `transaction`, index overflow and a UTXO
interface using types of the contract's package.

Assisted-by: anthropic:claude-opus-5-5
Assisted-by: anthropic:claude-fable-5-1
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
rvolosatovs added a commit to LFDT-Nightstream/Starstream that referenced this pull request Oct 19, 2026
`GET /rpc` returns the `starstream:ledger` package describing the
ledger queries `POST /rpc` accepts. With `?index=INDEX`, optionally
qualified by `&transaction=DIGEST`, it instead returns a
`starstream:ledger` package holding only the `utxo` interface for that
unspent output: the UTXO's methods, decoded from its contract, as the
freestanding functions `/rpc` invokes, taking the output reference
before the method parameters. Unused types are pruned and the packages
the interface depends on are printed nested. The world of the contract
is dropped from its package, so a UTXO interface using types defined in
the contract's own package does not make the two packages depend on
each other.

The output reference parameters are named `transaction` and `index`,
prefixed with `utxo-` for as long as they clash with a method parameter.

Both are served as WIT text by default, or encoded as Wasm on
`Accept: application/wasm`, so clients can generate wRPC bindings
without decoding the contract themselves. `HEAD /rpc` returns the
headers of the same response.

The UTXO WIT of every instance a contract exports is built, printed and
encoded once, when the contract is published, and served from memory.
Publishing therefore now rejects, with 400, Wasm that is not a component
and contracts that themselves define a `starstream:ledger` package.
Validation and WIT extraction run only once the publisher has been
charged, so a rejected contract consumes its nonce and fee, and a
duplicate one is rejected before any work is done.

The ledger package is encoded to Wasm once, by a build script, only when
the `server` feature is enabled. In the query, `index` is a `u32` and
`transaction` must be a digest; omitting it refers to the genesis block.
A query naming `index` or `transaction` more than once is rejected with
400, and an empty query (`GET /rpc?`) is treated as no query.

`headers-accept` is pinned to the head of
maxcountryman/headers-accept#24, on top of the unreleased upstream main,
whose media range matching ignores `q` when comparing parameters, so
`Accept: text/plain;q=0.9` matches the `text/plain; charset=UTF-8` the
server responds with. The PR additionally compares charset values
case-insensitively, so `charset=utf-8` matches it too.

The ledger client gains `build_utxo_wit_request` and
`Client::get_utxo_wit`, which takes an optional `Accept` media type and
returns the raw body. GET request builders share a helper.
`TransactionInput` gains `genesis` and `new` constructors.

The CLI gains `ledger utxo wit [--transaction DIGEST] <INDEX>`, printing
a UTXO's WIT from the endpoint, in the shape `ledger utxo call` invokes
it. `ledger utxo call` now resolves the method type from that endpoint,
encoded as Wasm, instead of downloading and decoding the whole contract.

The CLI e2e test now starts the ledger with the outputs of the
simulated `example` call as genesis, prints the UTXO's WIT and calls one
of its methods. Ledger tests cover parameter name clashes, lowercase
charsets, `q` values, `HEAD`, rejection of an empty `transaction`, index
overflow and a UTXO interface using types of the contract's package.

Assisted-by: anthropic:claude-opus-5-5
Assisted-by: anthropic:claude-fable-5-1
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
rvolosatovs added a commit to LFDT-Nightstream/Starstream that referenced this pull request Oct 19, 2026
`GET /rpc` returns the `starstream:ledger` package describing the
ledger queries `POST /rpc` accepts. With `?index=INDEX`, optionally
qualified by `&transaction=DIGEST`, it instead returns a
`starstream:ledger` package holding only the `utxo` interface for that
unspent output: the UTXO's methods, decoded from its contract, as the
freestanding functions `/rpc` invokes, taking the output reference
before the method parameters. Unused types are pruned and the packages
the interface depends on are printed nested. The world of the contract
is dropped from its package, so a UTXO interface using types defined in
the contract's own package does not make the two packages depend on
each other.

The output reference parameters are named `transaction` and `index`,
prefixed with `utxo-` for as long as they clash with a method parameter.

Both are served as WIT text by default, or encoded as Wasm on
`Accept: application/wasm`, so clients can generate wRPC bindings
without decoding the contract themselves. `HEAD /rpc` returns the
headers of the same response.

The UTXO WIT of every instance a contract exports is built, printed and
encoded once, when the contract is published, and served from memory.
Publishing therefore now rejects, with 400, Wasm that is not a component
and contracts that themselves define a `starstream:ledger` package.
Validation and WIT extraction run only once the publisher has been
charged, so a rejected contract consumes its nonce and fee, and a
duplicate one is rejected before any work is done.

The ledger package is encoded to Wasm once, by a build script, only when
the `server` feature is enabled. In the query, `index` is a `u32` and
`transaction` must be a digest; omitting it refers to the genesis block.
A query naming `index` or `transaction` more than once is rejected with
400, and an empty query (`GET /rpc?`) is treated as no query.

`headers-accept` is pinned to the head of
maxcountryman/headers-accept#24, on top of the unreleased upstream main,
whose media range matching ignores `q` when comparing parameters, so
`Accept: text/plain;q=0.9` matches the `text/plain; charset=UTF-8` the
server responds with. The PR additionally compares charset values
case-insensitively, so `charset=utf-8` matches it too.

The ledger client gains `build_utxo_wit_request` and
`Client::get_utxo_wit`, which takes an optional `Accept` media type and
returns the raw body. GET request builders share a helper.
`TransactionInput` gains `genesis` and `new` constructors.

The CLI gains `ledger utxo wit [--transaction DIGEST] <INDEX>`, printing
a UTXO's WIT from the endpoint, in the shape `ledger utxo call` invokes
it. `ledger utxo call` now resolves the method type from that endpoint,
encoded as Wasm, instead of downloading and decoding the whole contract.

The CLI e2e test now starts the ledger with the outputs of the
simulated `example` call as genesis, prints the UTXO's WIT and calls one
of its methods. Ledger tests cover parameter name clashes, lowercase
charsets, `q` values, `HEAD`, rejection of an empty `transaction`, index
overflow and a UTXO interface using types of the contract's package.

Assisted-by: anthropic:claude-opus-5-5
Assisted-by: anthropic:claude-fable-5-1
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
rvolosatovs added a commit to LFDT-Nightstream/Starstream that referenced this pull request Oct 19, 2026
`GET /rpc` returns the `starstream:ledger` package describing the
ledger queries `POST /rpc` accepts. With `?index=INDEX`, optionally
qualified by `&transaction=DIGEST`, it instead returns a
`starstream:ledger` package holding only the `utxo` interface for that
unspent output: the UTXO's methods, decoded from its contract, as the
freestanding functions `/rpc` invokes, taking the output reference
before the method parameters. Unused types are pruned and the packages
the interface depends on are printed nested. The world of the contract
is dropped from its package, so a UTXO interface using types defined in
the contract's own package does not make the two packages depend on
each other.

The output reference parameters are named `transaction` and `index`,
prefixed with `utxo-` for as long as they clash with a method parameter.

Both are served as WIT text by default, or encoded as Wasm on
`Accept: application/wasm`, so clients can generate wRPC bindings
without decoding the contract themselves. `HEAD /rpc` returns the
headers of the same response.

The UTXO WIT of every UTXO instance a contract exports, that is every
exported interface defining a `utxo` resource, whether named by the
contract or a WIT interface, is built, printed and encoded once, when
the contract is published, and served from memory, keyed by export name
as `POST /rpc` resolves it. Publishing therefore now rejects, with 400,
Wasm that is not a component and contracts that themselves define a
`starstream:ledger` package. As before, a rejected contract consumes
neither nonce nor fee: validation and WIT extraction run before the
publisher is charged, which happens under the contracts write lock, so
of concurrent publishes of the same contract only one is charged. A
contract already published is rejected before any of that work.

The ledger package is encoded to Wasm once, by a build script, only when
the `server` feature is enabled. In the query, `index` is a `u32` and
`transaction` must be a digest; omitting it refers to the genesis block.
A query naming `index` or `transaction` more than once is rejected with
400, and a query without parameters (`GET /rpc?`, `GET /rpc?&`) is
treated as no query.

`headers-accept` is pinned to the head of
maxcountryman/headers-accept#24, on top of the unreleased upstream main,
whose media range matching ignores `q` when comparing parameters, so
`Accept: text/plain;q=0.9` matches the `text/plain; charset=UTF-8` the
server responds with. The PR additionally compares charset values
case-insensitively, so `charset=utf-8` matches it too.

The ledger client gains `build_utxo_wit_request` and
`Client::get_utxo_wit`, which takes an optional `Accept` media type and
returns the raw body. GET request builders share a helper.
`TransactionInput` gains `genesis` and `new` constructors.

The CLI gains `ledger utxo wit [--transaction DIGEST] <INDEX>`, printing
a UTXO's WIT from the endpoint, in the shape `ledger utxo call` invokes
it. `ledger utxo call` now resolves the method type from that endpoint,
encoded as Wasm, instead of downloading and decoding the whole contract.

The CLI e2e test now starts the ledger with the outputs of the
simulated `example` call as genesis, prints the UTXO's WIT and calls one
of its methods. Ledger tests cover parameter name clashes, lowercase
charsets, `q` values, `HEAD`, queries without parameters, rejection of
an empty `transaction`, index overflow, a UTXO interface using types of
the contract's package and a UTXO exported as a WIT interface next to a
non-UTXO export.

Assisted-by: anthropic:claude-opus-5-5
Assisted-by: anthropic:claude-fable-5-1
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
rvolosatovs added a commit to LFDT-Nightstream/Starstream that referenced this pull request Oct 19, 2026
`GET /rpc` returns the `starstream:ledger` package describing the
ledger queries `POST /rpc` accepts. With `?index=INDEX`, optionally
qualified by `&transaction=DIGEST`, it instead returns a
`starstream:ledger` package holding only the `utxo` interface for that
unspent output: the UTXO's methods, decoded from its contract, as the
freestanding functions `/rpc` invokes, taking the output reference
before the method parameters. The `utxo` interface is a new interface,
holding only the types its functions use: imported from the UTXO
interface of the contract if that is named, moved out of it otherwise.
The contract's interfaces are left in place and its world is dropped
from its package, so no package of the contract depends on
`starstream:ledger`, even if another of its interfaces uses the UTXO
interface. The packages the `utxo` interface depends on are printed
nested.

The output reference parameters are named `transaction` and `index`,
prefixed with `utxo-` for as long as they clash with a method parameter.

Both are served as WIT text by default, or encoded as Wasm on
`Accept: application/wasm`, so clients can generate wRPC bindings
without decoding the contract themselves. `HEAD /rpc` returns the
headers of the same response.

The UTXO WIT of every UTXO instance a contract exports, that is every
exported interface defining a `utxo` resource, whether named by the
contract or a WIT interface, is built, printed and encoded once, when
the contract is published, and served from memory, keyed by export name
as `POST /rpc` resolves it. Publishing therefore now rejects, with 400,
Wasm that is not a component and contracts that themselves define an
unversioned `starstream:ledger` package, whose name the served package
would clash with; a versioned one is printed nested next to it. As before, a rejected contract consumes
neither nonce nor fee: validation and WIT extraction run before the
publisher is charged, which happens under the contracts write lock, so
of concurrent publishes of the same contract only one is charged. A
contract already published is rejected before any of that work.

The ledger package is encoded to Wasm once, by a build script, only when
the `server` feature is enabled. In the query, `index` is a `u32` and
`transaction` must be a digest; omitting it refers to the genesis block.
A query naming `index` or `transaction` more than once is rejected with
400, and a query without parameters (`GET /rpc?`, `GET /rpc?&`) is
treated as no query.

`headers-accept` is pinned to the head of
maxcountryman/headers-accept#24, on top of the unreleased upstream main,
whose media range matching ignores `q` when comparing parameters, so
`Accept: text/plain;q=0.9` matches the `text/plain; charset=UTF-8` the
server responds with. The PR additionally compares charset values
case-insensitively, so `charset=utf-8` matches it too.

The ledger client gains `build_utxo_wit_request` and
`Client::get_utxo_wit`, which takes an optional `Accept` media type and
returns the raw body. GET request builders share a helper.
`TransactionInput` gains `genesis` and `new` constructors.

The CLI gains `ledger utxo wit [--transaction DIGEST] <INDEX>`, printing
a UTXO's WIT from the endpoint, in the shape `ledger utxo call` invokes
it. `ledger utxo call` now resolves the method type from that endpoint,
encoded as Wasm, instead of downloading and decoding the whole contract.

The CLI e2e test now starts the ledger with the outputs of the
simulated `example` call as genesis, prints the UTXO's WIT and calls one
of its methods. Ledger tests cover parameter name clashes, lowercase
charsets, `q` values, `HEAD`, queries without parameters, rejection of
an empty `transaction`, index overflow, a UTXO interface using types of
the contract's package, a UTXO exported as a WIT interface next to a
non-UTXO export and a UTXO interface used by a sibling interface.

Assisted-by: anthropic:claude-opus-5-5
Assisted-by: anthropic:claude-fable-5-1
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
rvolosatovs added a commit to LFDT-Nightstream/Starstream that referenced this pull request Oct 19, 2026
`GET /rpc` returns the `starstream:ledger` package describing the
ledger queries `POST /rpc` accepts. With `?index=INDEX`, optionally
qualified by `&transaction=DIGEST`, it instead returns a
`starstream:ledger` package holding only the `utxo` interface for that
unspent output: the UTXO's methods, decoded from its contract, as the
freestanding functions `/rpc` invokes, taking the output reference
before the method parameters. The `utxo` interface is a new interface,
holding only the types its functions use: imported from the UTXO
interface of the contract if that is named, moved out of it otherwise.
The contract's interfaces are left in place and its world is dropped
from its package, so no package of the contract depends on
`starstream:ledger`, even if another of its interfaces uses the UTXO
interface. The packages the `utxo` interface depends on are printed
nested.

The output reference parameters are named `transaction` and `index`,
prefixed with `utxo-` for as long as they clash with a method parameter.

Both are served as WIT text by default, or encoded as Wasm on
`Accept: application/wasm`, so clients can generate wRPC bindings
without decoding the contract themselves. `HEAD /rpc` returns the
headers of the same response. The `GET` and `HEAD` handlers of
`/contracts`, `/transactions` and `/rpc` are merged into one handler
each, which omits the body on `HEAD`.

The UTXO WIT of every UTXO instance a contract exports, that is every
exported interface exporting a `utxo` resource, defined by it or used
from another interface, as the runtime identifies UTXO instances,
whether named by the contract or a WIT interface, is built, printed and encoded once, when
the contract is published, and served from memory, keyed by export name
as `POST /rpc` resolves it. Publishing therefore now rejects, with 400,
Wasm that is not a component and contracts that themselves define an
unversioned `starstream:ledger` package, whose name the served package
would clash with; a versioned one is printed nested next to it. As before, a rejected contract consumes
neither nonce nor fee: validation and WIT extraction run before the
publisher is charged, which happens under the contracts write lock, so
of concurrent publishes of the same contract only one is charged. A
contract already published is rejected before any of that work.

The ledger package is encoded to Wasm once, by a build script, only when
the `server` feature is enabled. In the query, `index` is a `u32` and
`transaction` must be a digest; omitting it refers to the genesis block.
A query naming `index` or `transaction` more than once is rejected with
400, and a query without parameters (`GET /rpc?`, `GET /rpc?&`) is
treated as no query.

`headers-accept` is pinned to the head of
maxcountryman/headers-accept#24, on top of the unreleased upstream main,
whose media range matching ignores `q` when comparing parameters, so
`Accept: text/plain;q=0.9` matches the `text/plain; charset=UTF-8` the
server responds with. The PR additionally compares charset values
case-insensitively, so `charset=utf-8` matches it too.

The ledger client gains `build_utxo_wit_request` and
`Client::get_utxo_wit`, which takes an optional `Accept` media type and
returns the raw body. GET request builders share a helper.
`TransactionInput` gains `genesis` and `new` constructors.

The CLI gains `ledger utxo wit [--transaction DIGEST] <INDEX>`, printing
a UTXO's WIT from the endpoint, in the shape `ledger utxo call` invokes
it. `ledger utxo call` now resolves the method type from that endpoint,
encoded as Wasm, instead of downloading and decoding the whole contract.

The CLI e2e test now starts the ledger with the outputs of the
simulated `example` call as genesis, prints the UTXO's WIT and calls one
of its methods. Ledger tests cover parameter name clashes, lowercase
charsets, `q` values, `HEAD`, queries without parameters, rejection of
an empty `transaction`, index overflow, a UTXO interface using types of
the contract's package, a UTXO exported as a WIT interface next to a
non-UTXO export, a UTXO interface used by a sibling interface and an
interface using the `utxo` resource of another.

Assisted-by: anthropic:claude-opus-5-5
Assisted-by: anthropic:claude-fable-5-1
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
rvolosatovs added a commit to LFDT-Nightstream/Starstream that referenced this pull request Oct 19, 2026
`GET /rpc` returns the `starstream:ledger` package describing the
ledger queries `POST /rpc` accepts. With `?index=INDEX`, optionally
qualified by `&transaction=DIGEST`, it instead returns a
`starstream:ledger` package holding only the `utxo` interface for that
unspent output: the UTXO's methods, decoded from its contract, as the
freestanding functions `/rpc` invokes, taking the output reference
before the method parameters. The `utxo` interface is a new interface,
holding only the types its functions use: imported from the UTXO
interface of the contract if that is named, with the functions
referring to the imports, or moved out of it otherwise, together with
the functions of the resources among them. Types whose names clash with
a method are prefixed with `utxo-`. The contract's interfaces are left
in place and its world is dropped from its package, so no package of
the contract depends on `starstream:ledger`, even if another of its
interfaces uses the UTXO interface. The packages the `utxo` interface
depends on are printed nested.

The output reference parameters are named `transaction` and `index`,
prefixed with `utxo-` for as long as they clash with a method parameter.

Both are served as WIT text by default, or encoded as Wasm on
`Accept: application/wasm`, so clients can generate wRPC bindings
without decoding the contract themselves. `HEAD /rpc` returns the
headers of the same response. The `GET` and `HEAD` handlers of
`/contracts`, `/transactions` and `/rpc` are merged into one handler
each, which omits the body on `HEAD`.

The UTXO WIT of every UTXO instance a contract exports, that is every
exported interface exporting a `utxo` resource, defined by it or used
from another interface, as the runtime identifies UTXO instances,
whether named by the contract or a WIT interface, is built, printed and encoded once, when
the contract is published, and served from memory, keyed by export name
as `POST /rpc` resolves it. Publishing therefore now rejects, with 400,
Wasm that is not a component. If the contract already uses an
unversioned `starstream:ledger` package, for example by importing
`starstream:ledger/types`, the `utxo` interface is added to it; the
contract is rejected if that package already has a `utxo` interface,
or if the `utxo` interface would make the package depend on itself,
because the named UTXO interface uses it. A versioned
`starstream:ledger` package is printed nested next to it. As before, a rejected contract consumes
neither nonce nor fee: validation and WIT extraction run before the
publisher is charged, which happens under the contracts write lock, so
of concurrent publishes of the same contract only one is charged. A
contract already published is rejected before any of that work.

The ledger package is encoded to Wasm once, by a build script, only when
the `server` feature is enabled. In the query, `index` is a `u32` and
`transaction` must be a digest; omitting it refers to the genesis block.
A query naming `index` or `transaction` more than once is rejected with
400, and a query without parameters (`GET /rpc?`, `GET /rpc?&`) is
treated as no query.

`headers-accept` is pinned to the head of
maxcountryman/headers-accept#24, on top of the unreleased upstream main,
whose media range matching ignores `q` when comparing parameters, so
`Accept: text/plain;q=0.9` matches the `text/plain; charset=UTF-8` the
server responds with. The PR additionally compares charset values
case-insensitively, so `charset=utf-8` matches it too.

The ledger client gains `build_utxo_wit_request` and
`Client::get_utxo_wit`, which takes an optional `Accept` media type and
returns the raw body. GET request builders share a helper.
`TransactionInput` gains `genesis` and `new` constructors.

The CLI gains `ledger utxo wit [--transaction DIGEST] <INDEX>`, printing
a UTXO's WIT from the endpoint, in the shape `ledger utxo call` invokes
it. `ledger utxo call` now resolves the method type from that endpoint,
encoded as Wasm, instead of downloading and decoding the whole contract.

The CLI e2e test now starts the ledger with the outputs of the
simulated `example` call as genesis, prints the UTXO's WIT and calls one
of its methods. Ledger tests cover parameter name clashes, lowercase
charsets, `q` values, `HEAD`, queries without parameters, rejection of
an empty `transaction`, index overflow, a UTXO interface using types of
the contract's package, a UTXO exported as a WIT interface next to a
non-UTXO export, a UTXO interface used by a sibling interface, an
interface using the `utxo` resource of another, method and type name
clashes, resources with methods, an imported `starstream:ledger`
package and rejection of a package depending on itself.

Assisted-by: anthropic:claude-opus-5-5
Assisted-by: anthropic:claude-fable-5-1
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
rvolosatovs added a commit to LFDT-Nightstream/Starstream that referenced this pull request Oct 19, 2026
`headers-accept` is pinned to the head of
maxcountryman/headers-accept#24, on top of the unreleased upstream main,
whose media range matching ignores `q` when comparing parameters, so
`Accept: text/plain;q=0.9` matches the `text/plain; charset=UTF-8` the
ledger responds with. The PR additionally compares charset values
case-insensitively, so `charset=utf-8` matches it too.

Assisted-by: anthropic:claude-opus-5-5
Assisted-by: anthropic:claude-fable-5-1
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
rvolosatovs added a commit to LFDT-Nightstream/Starstream that referenced this pull request Oct 20, 2026
`headers-accept` is pinned to the head of
maxcountryman/headers-accept#24, on top of the unreleased upstream main,
whose media range matching ignores `q` when comparing parameters, so
`Accept: text/plain;q=0.9` matches the `text/plain; charset=UTF-8` the
ledger responds with. The PR additionally compares charset values
case-insensitively, so `charset=utf-8` matches it too.

The revision is fetched from the upstream repository, which holds the
PR head, rather than from the fork it was pushed to.

Assisted-by: anthropic:claude-opus-5-5
Assisted-by: anthropic:claude-fable-5-1
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
rvolosatovs added a commit to LFDT-Nightstream/Starstream that referenced this pull request Oct 20, 2026
`headers-accept` is pinned to the head of
maxcountryman/headers-accept#24, on top of the unreleased upstream main,
whose media range matching ignores `q` when comparing parameters, so
`Accept: text/plain;q=0.9` matches the `text/plain; charset=UTF-8` the
ledger responds with. The PR additionally compares charset values
case-insensitively, so `charset=utf-8` matches it too.

The revision is fetched from the upstream repository, which holds the
PR head, rather than from the fork it was pushed to.

Assisted-by: anthropic:claude-opus-5-5
Assisted-by: anthropic:claude-fable-5-1
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
rvolosatovs added a commit to LFDT-Nightstream/Starstream that referenced this pull request Oct 21, 2026
`headers-accept` is pinned to the head of
maxcountryman/headers-accept#24, on top of the unreleased upstream main,
whose media range matching ignores `q` when comparing parameters, so
`Accept: text/plain;q=0.9` matches the `text/plain; charset=UTF-8` the
ledger responds with. The PR additionally compares charset values
case-insensitively, so `charset=utf-8` matches it too.

The revision is fetched from the upstream repository, which holds the
PR head, rather than from the fork it was pushed to.

Assisted-by: anthropic:claude-opus-5-5
Assisted-by: anthropic:claude-fable-5-1
Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
rvolosatovs added a commit to LFDT-Nightstream/Starstream that referenced this pull request Oct 21, 2026
## Serve invocation WIT on `GET /rpc`

Clients can now fetch WIT describing what `POST /rpc` accepts and
generate wRPC bindings from it, without having to decode contracts
themselves.

- `GET /rpc` returns the `starstream:ledger` package that describes the
ledger queries.
- `GET /rpc?index=INDEX[&transaction=DIGEST]` returns a
`starstream:ledger` package with only a `utxo` interface for that
unspent output. Its functions are the UTXO's methods. Each
takes the output reference (`transaction`, `index`) followed by the
method parameters, which get an `arg-` prefix so names can't clash. If
`transaction` is omitted, it refers to
the genesis block. Repeating a parameter returns 400, and an empty query
(`?`, `?&`) counts as no query.
- Responses are WIT text by default, or Wasm with `Accept:
application/wasm`. `HEAD /rpc` is supported. The `GET`/`HEAD` handlers
for `/contracts`, `/transactions` and `/rpc` are
  merged into one handler each, generic over `HEAD`.
- When a contract is published, the UTXO WIT for each exported instance
with a `utxo` resource is built once and served from memory. It is
converted from the component types that
publish validation already computes, so the contract's WIT is never
decoded with wit-parser. That decoder overflows the stack on valid
components whose imported packages depend on
  each other, which would abort the node.
- The client gains `build_utxo_wit_request` and `Client::get_utxo_wit`.
`TransactionInput` gains `genesis` and `new` constructors.
- The CLI gains `ledger utxo wit [--transaction DIGEST] <INDEX>`.
`ledger utxo call` now gets the method type from this endpoint instead
of downloading and decoding the whole
  contract.

  ### Behaviour changes

- Publishing now returns 400 for Wasm that isn't a component. It also
returns 400 for contracts whose UTXO methods take or return anything
other than booleans, integers, or
options, results and tuples of those (e.g. a UTXO handle). The error
names the offending parameter or result. These are the only shapes the
compiler currently produces: structs and
  enums in UTXO method signatures don't componentize yet.
- `Contract::utxos` now treats an instance as a UTXO instance only if
its `utxo` export is a resource. This matches what `get_utxo_typed`
already required.

  ### Dependency pins (temporary)

- `headers-accept` is pinned to maxcountryman/headers-accept#24, so
`Accept` matching ignores `q` and compares charsets case-insensitively.
Switch back once it's merged and
  released.

  ### Tests

- The CLI e2e test starts the ledger with the outputs of the simulated
`example` call as genesis, prints the UTXO's WIT and calls one of its
methods.
- The ledger tests cover `GET /rpc` and the UTXO WIT of a compiled
contract using options, results and tuples, with parameters named
`index` and `transaction`. They also check that
a UTXO method taking a UTXO is rejected, and that a component whose
imported packages form a cycle publishes.

---------

Signed-off-by: Roman Volosatovs <rvolosatovs@riseup.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant