65 lines
3.5 KiB
Markdown
65 lines
3.5 KiB
Markdown
# mobitool.wasm — Kindle→EPUB converter module
|
|||
|
|
|
||
|
|
`mobitool.wasm` is [libmobi](https://github.com/bfabiszewski/libmobi)'s `mobitool`
|
||
|
|
compiled to `wasm32-wasi`. The server runs it **in-process** via
|
||
|
|
[`wazero`](https://github.com/tetratelabs/wazero) (pure Go) to convert
|
||
|
|
MOBI/AZW/AZW3 ebooks to EPUB on demand. See the design doc:
|
||
|
|
`docs/superpowers/specs/2026-06-17-kindle-epub-conversion-design.md`.
|
||
|
|
|
||
|
|
## Why this shape
|
||
|
|
- **No cgo, no external binary.** The `.wasm` is `go:embed`-ed; the Go build stays
|
||
|
|
`CGO_ENABLED=0` and cross-compiles trivially.
|
||
|
|
- **Architecture-independent.** wasm32 bytecode runs identically on amd64/arm64
|
||
|
|
servers through wazero. Build once (CI, amd64), run anywhere.
|
||
|
|
- **Sandboxed.** Untrusted ebook bytes are parsed inside the WASM sandbox with
|
||
|
|
only a per-conversion scratch dir mounted — a libmobi memory bug on a malicious
|
||
|
|
file cannot reach the host.
|
||
|
|
|
||
|
|
## Pinned versions
|
||
|
|
|
||
|
|
| Component | Version / commit |
|
||
|
|
|-------------------|--------------------------------------------|
|
||
|
|
| wasi-sdk | 25.0 |
|
||
|
|
| libmobi | `906274205c11944b628da1c553b255acb1af7c55` |
|
||
|
|
| zlib | 1.3.1 (compiled to wasm) |
|
||
|
|
| wasmtime (smoke) | 27.0.0 (build-time smoke test only) |
|
||
|
|
|
||
|
|
License: libmobi is **LGPL-3.0-or-later**; shipping the unmodified compiled
|
||
|
|
artifact + this build recipe satisfies the relink obligation. zlib is zlib-license.
|
||
|
|
|
||
|
|
## Build config notes (validated by spike, 2026-06-17 on linux/amd64)
|
||
|
|
- `--with-libxml2=no` → libmobi's **internal xmlwriter** provides OPF/EPUB output,
|
||
|
|
so the `-e` (create EPUB) path works with no libxml2 dependency.
|
||
|
|
- libmobi is built **against a wasm-compiled zlib** (not `--with-zlib=no`). Forcing
|
||
|
|
`--with-zlib=no` makes libmobi vendor its own miniz, which then collides with
|
||
|
|
`mobitool`'s separate miniz (used for EPUB zip creation) — `wasm-ld` rejects the
|
||
|
|
duplicate symbols. Real zlib for the library + the tool's miniz for zip = no clash.
|
||
|
|
|
||
|
|
## Rebuild + install the artifact
|
||
|
|
```sh
|
||
|
|
docker build --platform linux/amd64 -t mobitool-wasm tools/mobitool-wasm
|
||
|
|
id=$(docker create mobitool-wasm)
|
||
|
|
docker cp "$id:/mobitool.wasm" internal/ebookconvert/mobitool.wasm
|
||
|
|
docker cp "$id:/mobitool.wasm.sha256" internal/ebookconvert/mobitool.wasm.sha256
|
||
|
|
docker rm "$id"
|
||
|
|
```
|
||
|
|
The build runs a **smoke conversion** of a bundled libmobi sample and fails if no
|
||
|
|
EPUB is produced. `mobitool -v` embeds `__DATE__`/`__TIME__`, so rebuilds are not
|
||
|
|
byte-for-byte reproducible — rely on the smoke test + recorded sha256, not a
|
||
|
|
bit-identical rebuild check.
|
||
|
|
|
||
|
|
## Runtime behavior characterized by the spike
|
||
|
|
- Converts MOBI6, KF8/AZW3 hybrids, HUFF/CDIC-compressed, and unicode samples to
|
||
|
|
well-formed EPUB (`mimetype` first = `application/epub+zip`, `META-INF/container.xml`,
|
||
|
|
`OEBPS/*`).
|
||
|
|
- **DRM:** `mobitool` prints `Document is encrypted` to **stdout** but still **exits 0**
|
||
|
|
and writes an EPUB (garbage content when it lacks the key). So the converter must
|
||
|
|
capture stdout and validate the output is a usable EPUB — exit code alone is not a
|
||
|
|
reliable DRM/failure signal.
|
||
|
|
- ⚠️ **Brittleness note:** DRM/print-replica detection matches the **English
|
||
|
|
stdout strings** mobitool emits (`drmMarkers` / `printReplicaMarker` in
|
||
|
|
`internal/ebookconvert/converter.go`). When bumping the pinned libmobi commit,
|
||
|
|
re-check those messages still match — a wording change silently downgrades a
|
||
|
|
DRM book to a generic "no EPUB produced" failure (it still falls back to the raw
|
||
|
|
original safely, just without the explicit DRM signal).
|