Contract ID: wasm-browser-quickstart-migration-v1
Bead: asupersync-umelq.16.2
Depends on: asupersync-umelq.16.1, asupersync-umelq.2.5
Provide a single onboarding and migration path for Browser Edition that is:
- deterministic and reproducible,
- explicit about capability and cancellation semantics,
- aligned with deferred-surface policy decisions.
This guide is intentionally command-first so users can move from "new to Browser Edition" to "verified onboarding evidence" without improvisation.
Required:
- Rust toolchain from
rust-toolchain.toml wasm32-unknown-unknowntargetrchfor offloaded cargo operations
Setup:
rustup target add wasm32-unknown-unknown
rch doctorChoose exactly one Browser profile for wasm32:
| Profile | Feature set | Intended usage |
|---|---|---|
FP-BR-MIN |
--no-default-features --features wasm-browser-minimal |
Contract-only or ABI boundary checks |
FP-BR-DEV |
--no-default-features --features wasm-browser-dev |
Local development and diagnostics |
FP-BR-PROD |
--no-default-features --features wasm-browser-prod |
Production-lean build envelope |
FP-BR-DET |
--no-default-features --features wasm-browser-deterministic |
Deterministic replay-oriented validation |
Guardrails:
- On wasm32, exactly one canonical browser profile must be enabled.
- Forbidden surfaces (
cli,io-uring,tls,sqlite,postgres,mysql,kafka) are compile-time rejected.
Reference: docs/integration.md ("wasm32 Guardrails"), src/lib.rs compile
error gates.
Use this table to decide whether Browser Edition runs directly in the current environment or must be used through a bridge-only boundary.
| Runtime context | Direct Browser Edition runtime | Guidance |
|---|---|---|
| Browser main thread (client-hydrated app) | Supported | Use one canonical browser profile and capability-scoped APIs |
| Dedicated worker context (when required Web APIs are present) | Supported for direct Browser Edition runtime | Bootstrap through a dedicated-worker module, run profile checks, and keep deterministic evidence artifacts |
| Node.js server runtime | Bridge-only | Keep runtime execution in browser boundary; call server logic over explicit RPC/API seams |
| Next.js server components / route handlers | Bridge-only | Do not run browser runtime core in server contexts |
| Edge/serverless runtimes (non-browser Web API subsets) | Bridge-only unless explicitly validated | Treat missing APIs as unsupported-runtime diagnostics, not partial support |
Non-goals for Browser Edition v1:
- native-only surfaces (
fs,process,signal,server) - native DB clients (
sqlite,postgres,mysql) inside browser runtime - native transport stacks (
kafka, native QUIC/HTTP3 lanes) in browser closure
When a runtime is outside the supported envelope, route through the bridge-only pattern and keep capability boundaries explicit instead of adding ambient runtime fallbacks.
Use this table when the author is writing browser-facing code in Rust rather than consuming the shipped JS/TS packages.
| Goal | Status today | Recommended lane |
|---|---|---|
| Verify browser-safe cfg/feature closure for the semantic core | Supported | rch exec -- cargo check --target wasm32-unknown-unknown --no-default-features --features wasm-browser-<profile> against asupersync |
| Maintain the wasm ABI/export boundary from Rust | Supported for workspace contributors | rch exec -- cargo check -p asupersync-browser-core --target wasm32-unknown-unknown --no-default-features --features dev or rch exec -- cargo check --manifest-path asupersync-wasm/Cargo.toml --target wasm32-unknown-unknown --no-default-features --features dev |
| Ship a browser app that constructs Browser Edition runtimes directly from external Rust consumer code | Preview public lane | Use RuntimeBuilder::browser() plus execution-ladder inspection for truthful lane negotiation and structured fail-closed diagnostics, while keeping the support claim narrower than the shipped JS/TS Browser Edition packages |
Rules for migration guidance:
- Do not describe
asupersync-browser-coreorasupersync-wasmas the public end-user Browser Edition SDK for Rust consumers. They are binding/export crates that feed the JS/TS packages. - Do not promise broad
RuntimeBuilderparity or stable directCx/Scopebrowser bootstrapping beyond the current preview dispatcher-backedRuntimeBuilder::browser()lane. - Keep the Rust-authored browser story on the same support matrix as JS/TS: browser main thread and dedicated worker are the direct-runtime contexts for the shipped product, while service/shared workers, SAB-based parallelism, and native-only surfaces remain deferred or unsupported.
Use this table to choose the correct lane before writing browser-facing Rust.
| Situation | Recommended lane | Why |
|---|---|---|
| You need a shipped browser SDK for application code today | Start from @asupersync/browser, @asupersync/react, or @asupersync/next |
These are the public Browser Edition product surfaces; they own the supported runtime diagnostics and packaging story |
| You need to prove the semantic core still closes under browser cfg/profile rules | Run the canonical rch exec -- cargo check --target wasm32-unknown-unknown --no-default-features --features wasm-browser-<profile> commands against asupersync |
This validates wasm-safe semantic closure without implying stable parity for the preview Rust browser builder lane |
| You maintain the wasm ABI/package boundary inside this repository | Work in asupersync-browser-core or asupersync-wasm |
These crates are the Rust-side binding/export infrastructure for the JS/TS packages, not the end-user browser SDK |
| You need the maintained Rust-authored browser example that the tree actually proves | Use tests/fixtures/rust-browser-consumer/ plus scripts/validate_rust_browser_consumer.sh |
This is the truthful current Rust-authored browser workflow: an in-repo fixture with deterministic validation, not general external RuntimeBuilder parity |
You need service/shared workers, cross-origin-isolated SharedArrayBuffer parallelism, or native-only modules |
Do not start from the Rust-authored browser lane | Those surfaces are deferred, guarded optional, bridge-only, or explicit non-goals today |
The current supported Rust-authored browser workflow is the repository-maintained
fixture at tests/fixtures/rust-browser-consumer/. It proves that the tree can
build and run a browser-facing Rust-authored wasm package layout while keeping
the scope honest:
- it uses the existing wasm dispatcher/provider helpers rather than inventing a second browser startup story alongside the preview public builder
- it keeps the contract honest about the remaining blocker:
src/runtime/builder.rsstill assumesstd::thread-backed worker and deadline-monitor startup, so this fixture does not pretend the preview public browser bootstrap API is already broad stable parity - it proves lifecycle semantics plus truthful execution-ladder diagnostics through a real browser matrix on both browser main-thread and dedicated-worker entrypoints
- it does not widen the public contract beyond what
docs/WASM.md,tests/wasm_browser_feasibility_matrix.rs, andtests/wasm_rust_browser_example_contract.rsalready enforce
Supported and unsupported contexts for this workflow:
| Context / surface | Status for Rust-authored guidance | What to do |
|---|---|---|
Browser main thread with window, document, and WebAssembly |
Maintained repository workflow | Use the fixture pattern and validate it with scripts/validate_rust_browser_consumer.sh |
| Dedicated worker | Maintained repository workflow inside the Rust browser matrix, still not a stable Rust browser constructor | Use the same fixture workflow and read it as proof of truthful dedicated-worker diagnostics plus lifecycle behavior, not as broad external RuntimeBuilder parity |
| Service worker / shared worker | Deferred / not shipped | Keep them on explicit message/data boundaries until host contracts and docs are promoted together |
| Next.js server components, route handlers, edge runtimes, or plain Node.js | Bridge-only or native-only | Keep direct runtime creation in the browser/client boundary and move Rust server logic behind explicit RPC/API seams |
SharedArrayBuffer worker pools / multi-threaded WASM |
Guarded optional, not shipped | Do not treat SAB-based parallelism as a default migration target |
Native-only modules (fs, process, signal, native DB clients, native transports) |
Non-goal for browser runtime | Keep these on native/server lanes and cross the boundary with explicit bridges |
Canonical validation command:
PATH=/usr/bin:$PATH bash scripts/validate_rust_browser_consumer.shExpected evidence bundle:
target/e2e-results/rust_browser_consumer/<timestamp>/consumer_build.logtarget/e2e-results/rust_browser_consumer/<timestamp>/browser-run.jsontarget/e2e-results/rust_browser_consumer/<timestamp>/summary.json
The validation is only considered healthy when the browser-run report confirms:
scenario_id = "RUST-BROWSER-CONSUMER"support_lane = "repository_maintained_rust_browser_fixture"ready_phase = "ready"anddisposed_phase = "disposed"completed_task_outcome = "ok"cancel_event_count = 1- browser capabilities show
has_window,has_document, andhas_webassembly
If you are evaluating the preview public builder directly, inspect the truthful execution ladder before promoting it in your own crate:
let ladder = RuntimeBuilder::new().inspect_browser_execution_ladder();
let preferred = RuntimeBuilder::new()
.inspect_browser_execution_ladder_with_preferred_lane(
BrowserExecutionLane::DedicatedWorkerDirectRuntime,
);
let selection = RuntimeBuilder::browser().build_selection();The minimum fields to log or inspect are:
selected_lanehost_rolereason_codepreferred_lanedowngrade_ordermessageguidance
Migration rule of thumb:
- if you want a shipped product surface, use the JS/TS packages
- if you want to keep the Rust-authored lane honest inside this repository, use the maintained fixture and its validation script
- if you need runtime authority outside a browser main-thread page, move that logic to a bridge/native lane instead of stretching the Rust browser contract
Browser onboarding and migration should flow through the release-channel contract before promotion beyond local/dev usage.
Canonical policy:
docs/wasm_release_channel_strategy.md
Channel model:
nightly(wasm-browser-dev) for rapid iteration,canary(policy label carried by the release process, typically validated withwasm-browser-prod) for pre-stable validation,stable(policy label, not a Cargo feature) only after thewasm-browser-prodlane plus the required deterministic/minimal evidence lanes and release gates all pass.
Important:
wasm-browser-canaryandwasm-browser-releaseare not Cargo feature names in this repository.- The canonical browser feature flags remain exactly:
wasm-browser-minimal,wasm-browser-dev,wasm-browser-prod, andwasm-browser-deterministic.
Minimum gate bundle before promotion:
python3 scripts/check_wasm_optimization_policy.py \
--policy .github/wasm_optimization_policy.json
python3 scripts/check_wasm_dependency_policy.py \
--policy .github/wasm_dependency_policy.json
python3 scripts/check_security_release_gate.py \
--policy .github/security_release_policy.json \
--check-deps \
--dep-policy .github/wasm_dependency_policy.jsonCargo-heavy profile checks remain rch-offloaded:
rch exec -- cargo check --target wasm32-unknown-unknown \
--no-default-features --features wasm-browser-dev
rch exec -- cargo check --target wasm32-unknown-unknown \
--no-default-features --features wasm-browser-prodIf any release-blocking gate fails, treat as promotion-blocking and follow the
demotion/rollback sequence in docs/wasm_release_channel_strategy.md.
Rust-author quick rule:
- use
wasm-browser-devwhile iterating on the repository-maintained Rust browser fixture or ABI crates, - use
wasm-browser-prodwhen validating the browser package boundary you intend to promote, - keep
wasm-browser-deterministicandwasm-browser-minimalas evidence lanes, not as ad hoc replacement feature names for canary/stable.
Before onboarding framework adapters, verify workspace slicing closure for the browser path.
Core slicing intent:
- Keep semantic runtime invariants in the wasm-safe core path.
- Keep native-only modules behind
cfg(not(target_arch = "wasm32")). - Keep optional adapters out of default browser closure unless explicitly needed.
Validation commands:
rch exec -- cargo check --target wasm32-unknown-unknown \
--no-default-features --features wasm-browser-minimal \
| tee artifacts/onboarding/profile-minimal-check.log
rch exec -- cargo check --target wasm32-unknown-unknown \
--no-default-features --features wasm-browser-dev \
| tee artifacts/onboarding/profile-dev-check.log
rch exec -- cargo check --target wasm32-unknown-unknown \
--no-default-features --features wasm-browser-deterministic \
| tee artifacts/onboarding/profile-deterministic-check.logExpected outcomes:
- each profile compiles independently,
- no native-only module leaks into wasm32 compilation closure,
- profile guardrails reject invalid multi-profile feature combinations.
Use this as the canonical package-selection and install flow for Browser Edition. Start with the highest layer that matches your runtime boundary; only drop down to lower-level packages when you need that surface explicitly.
Package layering:
@asupersync/browser-core(low-level ABI/types)@asupersync/browser(recommended default app-facing browser SDK)@asupersync/react(React client-boundary adapter over browser SDK)@asupersync/next(Next boundary adapter over browser SDK)
Install/quickstart decision table:
| Package | Use when | Install | First-success checkpoint |
|---|---|---|---|
@asupersync/browser-core |
You need raw ABI handles/types or metadata-driven compatibility checks | npm install @asupersync/browser-core |
Run rch exec -- cargo test --test wasm_packaged_abi_compatibility_matrix -- --nocapture |
@asupersync/browser |
You need direct browser runtime APIs without framework adapters (recommended starting point) | npm install @asupersync/browser |
Run rch exec -- cargo test --test wasm_js_exports_coverage_contract -- --nocapture |
@asupersync/react |
You are running Browser Edition inside a client-rendered React tree | npm install @asupersync/react react react-dom |
Run python3 scripts/run_browser_onboarding_checks.py --scenario react |
@asupersync/next |
You need Next-specific client/server/edge boundary guidance | npm install @asupersync/next next react react-dom |
Run python3 scripts/run_browser_onboarding_checks.py --scenario next |
Selection rules:
- choose
@asupersync/browserfirst unless you have an explicit low-level ABI need (browser-core) or framework boundary need (react/next) - keep direct runtime creation in browser/client boundaries only
- treat Next server/edge paths as bridge-only lanes; do not run direct Browser Edition runtime APIs there
- keep package versions aligned across
browser-core,browser,react, andnext
Each flow has:
- a deterministic command bundle,
- expected verification outcomes,
- artifact pointers for replay/debug.
Automated runner (preferred for CI/replay bundles):
python3 scripts/run_browser_onboarding_checks.py --scenario allScenario-scoped runs:
python3 scripts/run_browser_onboarding_checks.py --scenario vanilla
python3 scripts/run_browser_onboarding_checks.py --scenario react
python3 scripts/run_browser_onboarding_checks.py --scenario nextCanonical framework examples and deterministic replay pointers:
docs/wasm_canonical_examples.md.
Goal: verify scheduler, cancellation/quiescence, and capability boundaries.
mkdir -p artifacts/onboarding
rch exec -- cargo test -p asupersync browser_ready_handoff -- --nocapture \
| tee artifacts/onboarding/vanilla-browser-ready.log
rch exec -- cargo test --test close_quiescence_regression \
browser_nested_cancel_cascade_reaches_quiescence -- --nocapture \
| tee artifacts/onboarding/vanilla-quiescence.log
rch exec -- cargo test --test security_invariants browser_fetch_security -- --nocapture \
| tee artifacts/onboarding/vanilla-security.logExpected outcomes:
- browser handoff tests pass (no starvation regressions)
- nested cancel-cascade reaches quiescence
- browser fetch security policy tests pass with default-deny behavior
Goal: verify browser-capable seams and deterministic behavior before integrating React adapters.
rch exec -- cargo test --test native_seam_parity \
browser_clock_through_trait_starts_at_zero -- --nocapture \
| tee artifacts/onboarding/react-clock.log
rch exec -- cargo test --test native_seam_parity \
browser_clock_through_trait_advances_with_host_samples -- --nocapture \
| tee artifacts/onboarding/react-clock-advance.log
rch exec -- cargo test --test obligation_wasm_parity \
wasm_full_browser_lifecycle_simulation -- --nocapture \
| tee artifacts/onboarding/react-obligation.logExpected outcomes:
- browser clock abstraction is monotonic and deterministic
- obligation lifecycle invariants hold across browser-style lifecycle phases
Goal: verify profile closure and dependency policy before wiring App Router boundaries.
Reference template and deployment guidance:
docs/wasm_nextjs_template_cookbook.md.
python3 scripts/check_wasm_dependency_policy.py \
--policy .github/wasm_dependency_policy.json \
| tee artifacts/onboarding/next-dependency-policy.log
rch exec -- cargo check --target wasm32-unknown-unknown \
--no-default-features --features wasm-browser-dev \
| tee artifacts/onboarding/next-wasm-check.log
python3 scripts/check_wasm_optimization_policy.py \
--policy .github/wasm_optimization_policy.json \
| tee artifacts/onboarding/next-optimization-policy.logExpected outcomes:
- dependency policy script exits cleanly and produces summary artifacts
- wasm profile check confirms chosen profile closure rules
- optimization policy summary is emitted for downstream CI gates
Known failure signature and remediation:
- Signature:
getrandomcompile error requiringwasm_jssupport duringnext.wasm_profile_check. - Immediate action: treat this as a blocker for Next onboarding, capture
artifacts/onboarding/next.wasm_profile_check.log, and route fix through the wasm profile/dependency closure beads before retrying this flow.
Goal: validate the repository-maintained Rust-authored browser workflow without claiming a general external Rust browser SDK.
Reference surfaces:
tests/fixtures/rust-browser-consumer/README.mdtests/wasm_rust_browser_example_contract.rsscripts/validate_rust_browser_consumer.sh
PATH=/usr/bin:$PATH bash scripts/validate_rust_browser_consumer.shExpected outcomes:
- the nested Rust crate builds through
wasm-packwith cargo execution routed throughrch exec -- ... - the staged Vite bundle contains both JavaScript and
.wasmassets - the browser-run report records
scenario_id = "RUST-BROWSER-CONSUMER" - the support lane is
repository_maintained_rust_browser_fixture - the run reaches
readybefore unmount anddisposedafter unmount - exactly one cancellation event is emitted during teardown and the completed
task reports
ok
Expected artifacts:
target/e2e-results/rust_browser_consumer/<timestamp>/consumer_build.logtarget/e2e-results/rust_browser_consumer/<timestamp>/browser-run.jsontarget/e2e-results/rust_browser_consumer/<timestamp>/summary.json
Common legacy pattern:
Promise.race([...])returns winner, losers continue silently
Asupersync browser model:
- race winner returned,
- losers explicitly cancelled and drained,
- obligation closure is required before region close.
What to do:
- model the competing operations as scoped tasks,
- wire explicit cancellation on loser branches,
- verify with quiescence and obligation parity tests.
Verification commands:
rch exec -- cargo test --test close_quiescence_regression browser_ -- --nocapture
rch exec -- cargo test --test obligation_wasm_parity wasm_cancel_drain_ -- --nocaptureCommon legacy pattern:
- direct
fetch, timers, or storage calls without explicit authority envelope
Asupersync browser model:
- effects must flow through explicit capability contracts,
- default-deny policy for browser fetch authority.
What to do:
- define explicit origin/method/credential/header constraints,
- pass capability through call chain; avoid ambient globals,
- add policy tests before exposing API surface.
Verification command:
rch exec -- cargo test --test security_invariants browser_fetch_security -- --nocaptureCommon legacy pattern:
- detached async work that outlives UI/component lifecycle
Asupersync browser model:
- each task belongs to one region,
- region close requires quiescence,
- cancellation follows request -> drain -> finalize.
What to do:
- move detached work into explicit scope/region ownership,
- ensure close paths drive cancel+drain,
- reject lifecycle completion while obligations remain unresolved.
Verification command:
rch exec -- cargo test --test close_quiescence_regression browser_ -- --nocaptureMigration docs must not promise deferred capabilities as available. Authoritative
register: PLAN_TO_BUILD_ASUPERSYNC_IN_WASM_FOR_USE_IN_BROWSERS.md, Section
6.6 ("Deferred Surface Register").
Required mappings:
| DSR ID | Deferred surface | Browser guidance |
|---|---|---|
DSR-001 |
OS network sockets and listener stack | Use browser transport envelopes (fetch, WebSocket, browser stream bridges) through capability wrappers |
DSR-002 |
Reactor + io-uring paths | Use browser event-loop scheduling contract and timer adapters; do not reference native pollers |
DSR-003 |
Native TLS stack | Use browser trust model; no native cert-store assumptions in browser guides |
DSR-004 |
fs/process/signal/server modules |
Treat as explicit non-goals for browser-v1; route to server-side companion services |
DSR-005 |
Native DB clients (sqlite/postgres/mysql) |
Use browser-safe RPC boundaries and keep DB access out of browser runtime core |
DSR-006 |
Native-only transport protocols (kafka/quic-native/http3-native) | Use browser-compatible transport facade and declare protocol availability explicitly |
DSR-007 |
Runtime-dependent observability sinks | Use browser-safe tracing/export pathways and preserve deterministic artifact contracts |
Each onboarding run should capture:
scenario_id(vanilla-smoke,react-readiness,next-readiness)- command bundle used
- profile flags and target triple
- pass/fail outcome per step
- artifact paths
- remediation hint per step (
remediation_hint) - terminal failure excerpt (
failure_excerpt) for failing steps
Minimum artifact set:
artifacts/onboarding/*.logartifacts/onboarding/*.ndjsonartifacts/onboarding/*.summary.jsonartifacts/wasm_dependency_audit_summary.jsonartifacts/wasm_optimization_pipeline_summary.json
Use this quick triage table before deep debugging:
| Symptom | First command | Expected artifact |
|---|---|---|
| wasm profile compile failure | rch exec -- cargo check --target wasm32-unknown-unknown --no-default-features --features wasm-browser-dev |
artifacts/onboarding/*-wasm-check.log |
| profile/policy mismatch | python3 scripts/check_wasm_dependency_policy.py --policy .github/wasm_dependency_policy.json |
artifacts/wasm_dependency_audit_summary.json |
| onboarding scenario drift | python3 scripts/run_browser_onboarding_checks.py --scenario all |
artifacts/onboarding/*.summary.json + *.ndjson |
| unclear capability/authority failure | rch exec -- cargo test --test security_invariants browser_fetch_security -- --nocapture |
artifacts/onboarding/vanilla-security.log |
Escalate only after capturing command output + artifact pointers. Treat missing artifacts as a workflow failure that must be fixed before filing runtime bugs.
Use this bundle for documentation drift detection:
# Core compile/lint/format gates
rch exec -- cargo check --all-targets
rch exec -- cargo clippy --all-targets -- -D warnings
rch exec -- cargo fmt --check
# Browser policy checks referenced by this guide
python3 scripts/check_wasm_dependency_policy.py --policy .github/wasm_dependency_policy.json
python3 scripts/check_wasm_optimization_policy.py --policy .github/wasm_optimization_policy.json
python3 scripts/run_browser_onboarding_checks.py --scenario allDrift policy:
- Any command change in this guide must be accompanied by updated expected outcomes.
- Any profile/surface statement change must be validated against DSR mappings.
- Any migration guidance change must keep an explicit verification command.