@reseam/browser (0.26.3)
Installation
@reseam:registry=npm install @reseam/browser@0.26.3"@reseam/browser": "0.26.3"About this package
@reseam/browser
Reseam in a web page. It inspects, patches, and signs APKs entirely in the browser; nothing is uploaded. The browser patcher at reseam.app/patch runs on it.
The Rust engine runs in a WebAssembly worker. Patch bundles run unchanged in a second worker on CheerpJ 4.3 (Java 17), calling into Rust through the same generated bindings the desktop and Android SDKs use. The package has no UI.
Build
Needs the pinned Rust toolchain, JDK 17, Bun 1.4.2, WASI SDK 33 and the BoltFFI CLI from .boltffi-version.
cargo install boltffi_cli --version "=$(cat .boltffi-version)" --locked
bun install --cwd browser --frozen-lockfile
WASI_SDK_PATH=/path/to/wasi-sdk-33.0-x86_64-linux bun run --cwd browser engine
bun run --cwd browser types
engine writes the WASM modules, Java host, Kotlin runtime and native-method manifest to public/runtime/<hash>/ and their sizes and SHA-256 sums to src/runtime.generated.ts, which the loader checks. types writes declarations to types/. Release tags publish the package to https://git.reseam.app/api/packages/reseam/npm/.
Host requirements
The package ships TypeScript sources and their workers for a Vite host to bundle. The host must:
- bundle workers as classic scripts (
worker: { format: 'iife' }): CheerpJ loads throughimportScripts; - keep the package out of dependency pre-bundling and compile it for SSR (
optimizeDeps.exclude,ssr.noExternal), so worker URLs resolve; - serve
public/runtime/as immutable files and pass its URL toJavaRuntimeasruntimeBase, keeping old hashes until pages that use them expire; - send
Cross-Origin-Opener-Policy: same-originandCross-Origin-Embedder-Policy: require-corpon the page and every worker script; - allow
https://cjrtnc.leaningtech.com/4.3/, and WebAssembly compilation in its Content Security Policy.
Browsers need WebAssembly SIMD, shared memory, Web Locks, IndexedDB and OPFS sync access handles in workers.
Use
import { BrowserSession, JavaRuntime } from '@reseam/browser';
const java = new JavaRuntime({ runtimeBase });
void java.warmup();
const session = await BrowserSession.open(files, { javaRuntime: java, signal, onEvent });
const inspection = await session.inspect({ apk_path, split_paths, bundle_paths });
await session.mount(signingFilesAndOptionFiles);
const outcome = await session.patch(request);
const artifacts = await session.artifacts();
await session.dispose();
Requests and results are the SDK models (src/models.ts). Failures are EngineErrors carrying the SDK Problem.
- A session runs one patch. Inspection keeps the opened APK and verified catalogs for it; trust and payload hashes are checked again before patch code loads.
mount()adds files after inspection but cannot replace inspected inputs.- Artifacts are OPFS files: keep the session open until they are saved, because
dispose()deletes them. - One
JavaRuntimecan serve every session.warmup()starts Java without loading patch code; a cold start takes seconds. A canceled run discards the JVM. compressionWorkers(1 to 4) sets how many workers compress DEX entries. By default it is 1 or 2, depending on the CPU.
Inputs stay browser Files. Scratch files are deleted once no name or descriptor refers to them, and a later session reclaims a crashed tab's storage after taking its Web Lock.
Signing is the CLI's APK v2 signer: P-256, Android 7 and later, no v1. With empty key paths the engine generates a pair, which the host should store and offer as a backup. ECDSA is randomized, so signing blocks differ between runs with the same key. Signer approval and key storage belong to the host.
Compare with the CLI
compare-cli.ts patches real apps in Chromium and with the CLI using the same bundle, selection, key and certificate. It then checks that every byte outside the signing block and every ZIP entry match, and that apksigner verifies both.
CARGO_BUILD_JOBS=4 cargo build --release -p reseam-cli
bun run --cwd browser harness
MATRIX_PATH=apps.json CHROMIUM_PATH=/path/to/chromium \
APKSIGNER=/path/to/build-tools/36.0.0/apksigner bun browser/scripts/compare-cli.ts
{
"bundle": "/path/to/official.reseam",
"trust": "signer public key, hex",
"key": "/path/to/reseam.pk8",
"cert": "/path/to/reseam.der",
"apps": [{ "id": "example", "apk": "/path/to/app.apk", "splits": [] }]
}
Each app may add a selection; the default is recommended. OUTPUT_DIR, RESEAM_BIN, TEST_PORT and BROWSER_PROFILE override paths, and RESUME=1 reruns only failed cases. PROFILE_BRIDGE=1 logs per-patch native-call counts and times; PROFILE_CPU=1 saves Chromium CPU profiles.
Validation
validation.json records the last comparison run: recommended patches, default options, and the same key for both hosts. All 149 APK outputs matched the CLI outside the signing block, with identical ZIP entries and valid signatures. Matching bytes don't prove the app works on a device; no device run is recorded.
CheerpJ license
CheerpJ loads from its CDN under the Community License, which requires credit; hosts show it. Self-hosting needs the commercial license. Pass licenseKey to JavaRuntime when a deployment has one.
Dependencies
Dependencies
| ID | Version |
|---|---|
| @bjorn3/browser_wasi_shim | 0.4.2 |
| lossless-json | 4.3.0 |
Development dependencies
| ID | Version |
|---|---|
| @playwright/test | 1.58.2 |
| @types/bun | 1.3.10 |
| typescript | 6.0.2 |
| vite | 8.0.7 |