Skip to content

Eliminate web feature#5650

Draft
deuszx wants to merge 6 commits intorust-1.93-toolchainfrom
eliminate-web-feature
Draft

Eliminate web feature#5650
deuszx wants to merge 6 commits intorust-1.93-toolchainfrom
eliminate-web-feature

Conversation

@deuszx
Copy link
Copy Markdown
Contributor

@deuszx deuszx commented Mar 10, 2026

Motivation

Replace the web Cargo feature flag with a custom wasm target (wasm32-web-unknown)
that uses target_env = "web" for cfg detection. This eliminates the need to pass
features = ["web"] through the entire dependency tree and makes web-specific code
selection automatic based on the compilation target.

Proposal

  • Introduce a custom target spec wasm32-web-unknown.json that extends
    wasm32-unknown-unknown with "env": "web", threading support (+atomics,
    --shared-memory), and TLS exports.
  • Replace all cfg(feature = "web") checks with cfg(target_env = "web") via
    cfg_aliases in each crate's build.rs.
  • Remove the web feature from all Cargo.toml files.
  • Use a forked wasmer-types with indexmap 2 to resolve a build-std dependency
    conflict with custom targets.
  • Add the default wasm32-unknown-unknown linker flags (-z stack-size=1048576,
    --stack-first, --allow-undefined, --no-entry) to the custom target spec,
    which are not inherited when defining a custom target from scratch.
  • Add COOP/COEP headers to the vitest config for SharedArrayBuffer support
    required by the atomics-enabled wasm build.

Note on target naming: The target is named wasm32-web-unknown (with web in
the vendor position) as a workaround for target-lexicon (used by wasmer-types)
not being able to parse custom OS values. The semantically correct name would be
wasm32-unknown-js-web (arch-vendor-os-env); an upstream Rust PR
(rust-lang/rust#153686) has been filed to enable this.

Test Plan

  • cd web && pnpm -r run ci passes all 5 tests (2 signer + 3 client integration),
    lint, clippy, and typedoc generation.
  • CI web workflow should pass with the existing faucet + validator setup.

Release Plan

  • Nothing to do / These changes follow the usual release cycle.

Links

@deuszx deuszx force-pushed the rust-1.93-toolchain branch 3 times, most recently from 44c1fe0 to 2b701d6 Compare March 10, 2026 11:01
@deuszx deuszx mentioned this pull request Mar 10, 2026
@deuszx deuszx force-pushed the eliminate-web-feature branch from 0d4f858 to 502cbd5 Compare March 10, 2026 11:37
@deuszx deuszx force-pushed the rust-1.93-toolchain branch from 3a49676 to a34257b Compare March 10, 2026 12:41
deuszx added 3 commits March 10, 2026 13:54
Replace the `web` Cargo feature with target-based detection using a new
`wasm32-unknown-js-web` custom target spec with `"os": "web"`. The `web`
cfg_alias in all build.rs files now checks `target_os = "web"` instead of
`feature = "web"`, so zero Rust source files need changes.

- Create wasm32-unknown-js-web.json target spec with threading support,
  shared-memory link args, and `"os": "web"` for cfg detection
- Update web/.cargo/config.toml to use the new target with json-target-spec
- Change the `web` cfg_alias in 9 build.rs files from
  `all(target_arch = "wasm32", feature = "web")` to `target_os = "web"`
- Remove `web` feature from 8 crate Cargo.toml files, moving web-only
  dependencies to `[target.'cfg(target_os = "web")'.dependencies]` sections
- Remove `"web"` feature references from web/@linera/client/Cargo.toml
- Remove wasm32-unknown-unknown from web/rust-toolchain.toml targets
  Follow-up fixes to the custom web target:

  - Rename wasm32-unknown-js-web → wasm32-web-unknown so that
    target-lexicon (used by wasmer/cranelift) can parse the target name.
    target-lexicon has no custom OS support, so "web" goes in the vendor
    position.
  - Use "env": "web" + "os": "unknown" in the target spec instead of
    "os": "web". std has no platform implementation for target_os="web"
    and fails to compile with build-std.
  - Switch all build.rs aliases and Cargo.toml target sections from
    target_os = "web" to target_env = "web" accordingly.
  - Add -Zunstable-options to web rustflags (required by rustc to load
    custom target specs).
  - Remove explicit --target from build.bash; let web/.cargo/config.toml
    handle it.

  KNOWN BLOCKER: The web build still fails because wasmer-types v4.4.0
  (upstream, depended on by linera-wasmer) cannot compile when using ANY
  custom target with build-std. This is a cargo bug where build-std
  dependency resolution for custom targets causes indexmap v2.x (from
  std's deps) to shadow indexmap v1.x (used by wasmer-types), resulting
  in generic arity mismatches. The same wasmer-types code compiles fine
  for the built-in wasm32-unknown-unknown target with identical flags.
  Possible workarounds:
    - Use wasm32-unknown-unknown as target + env var/rustflag for web
      detection (avoids build-std custom target bug entirely)
    - Patch/fork wasmer-types to use indexmap 2.x
    - File a cargo bug report for build-std + custom target dep resolution
Point wasmer git deps to wasmer-types-indexmap2 branch, which includes
wasmer-types as a workspace member with indexmap 2 (fixing build-std
conflicts with the custom wasm32-web-unknown target). Enable
dynamic-linking in the target spec so cdylib crate type is supported.
@deuszx deuszx force-pushed the eliminate-web-feature branch from 502cbd5 to cf2df1b Compare March 10, 2026 12:55
ruzstd.workspace = true
tracing-web = { optional = true, workspace = true }

[target.'cfg(target_env = "web")'.dependencies]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these are Web-specific (e.g. kywasmtime) and some are JS-specific (e.g. getrandom/js).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember we loosely also support (i.e. it's broken right now, but currently isn't too much work to fix) NodeJS, so not every JS environment is a Web environment.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be wasm32-unknown-js-web? Target triples are architecture-vendor-os-env.

Copy link
Copy Markdown
Contributor

@Twey Twey Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Not that JS is exactly an OS — but I reckon it's close enough for the analogy. Vendors would be for specific browsers I suppose, or maybe specific Wasm interpreters where they're incompatible.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't remember now which crate it was but there was an issue where, when using the target triple, the crate failed to compile b/c it didn't recognize js as the os. So Claude, after analysing its code (and some trial and error) ended up with this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes. It was in the commit message:

  • Rename wasm32-unknown-js-web → wasm32-web-unknown so that
    target-lexicon (used by wasmer/cranelift) can parse the target name.
    target-lexicon has no custom OS support, so "web" goes in the vendor
    position.
  • Use "env": "web" + "os": "unknown" in the target spec instead of
    "os": "web". std has no platform implementation for target_os="web"
    and fails to compile with build-std.

Copy link
Copy Markdown
Contributor

@Twey Twey Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the env here is unknown. The vendor in this triple is web, not the env. This is semantic nonsense, though, and Cranelift shouldn't be pulled in on the Web, so I suggest fixing it by working out why Cranelift is aware of this target at all.

For the latter, indeed the os has to be unknown so as to pull in dlmalloc according to the relevant Cargo.toml, but there's no need to mess with the vendor instead of keeping the web env.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rust-lang/rust#153686 to allow us to use wasm32-unknown-js-web, which I think should otherwise work.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dependencies are:

linera-web → enables wasmer feature on linera-execution → pulls in linera-wasmer → depends on wasmer-types → depends on target-lexicon

So wasmer-types unconditionally depends on target-lexicon and uses Triple for parsing target triples. When compiled for the web target, target-lexicon tries to parse the target name during compilation.

This can be fixed by either:

  1. Changing wasmer-types to pull in target-lexicon optionally (upstream change)
  2. Waiting for your PR to rust-lang
  3. keeping the current hack

deuszx added 2 commits March 11, 2026 11:47
The custom wasm32-web-unknown target spec was created by adding
web-specific customizations (env, threading, shared memory) but
did not carry over the default pre-link-args that the built-in
wasm32-unknown-unknown target provides. These flags are:

  -z stack-size=1048576  Allocate 1MB stack (without this, the
                         wasm binary has no stack, causing
                         "memory access out of bounds" on any
                         non-trivial function call)
  --stack-first          Place stack at the start of linear memory
  --allow-undefined      Allow undefined symbols (JS imports)
  --no-demangle          Don't demangle symbols in diagnostics
  --no-entry             No entry point required (library build)

These can be verified by running:
  rustc +nightly -Z unstable-options --print target-spec-json \
    --target wasm32-unknown-unknown

The threading flags (--shared-memory, --import-memory, TLS exports)
were already present — they were migrated from the previous
`-C link-args=...` in web/.cargo/config.toml which were added when
Rust 1.92 stopped auto-enabling shared memory for +atomics.
The wasm32-web-unknown target links with --shared-memory, causing
wasm-bindgen to create WebAssembly.Memory backed by SharedArrayBuffer.
Modern browsers restrict SharedArrayBuffer to cross-origin isolated
contexts, requiring COOP/COEP HTTP headers on the serving page.

Add the required headers to vitest's dev server config and document
the cross-origin isolation requirement in the @linera/client README.
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.

2 participants