No description
  • Rust 76.7%
  • Scheme 17%
  • Makefile 4.7%
  • Shell 1.1%
  • Common Lisp 0.5%
Find a file
2026-07-30 15:47:38 -06:00
.builds ci: set umask 022 in package/verify tasks too (jerbuild creates group-writable cache dirs during pkg operations) 2026-07-22 13:11:00 -06:00
examples Initial scaffold 2026-06-29 15:47:56 -06:00
scripts Resolve security audit findings 2026-07-11 17:27:55 -06:00
src security: bound PNG/JPEG decode time on the C ABI path 2026-07-21 16:37:04 -06:00
tests Resolve security audit findings 2026-07-11 17:27:55 -06:00
vision Add Jerboa OCR contract 2026-07-15 08:32:39 -06:00
.gitignore Add Jerboa OCR contract 2026-07-15 08:32:39 -06:00
AGENTS.md docs: remove jerboa-emacs restriction 2026-07-30 15:47:38 -06:00
Cargo.lock Add Android JNI vision bridge 2026-07-15 09:28:00 -06:00
Cargo.toml security: harden release profile with panic=abort and overflow-checks 2026-07-21 16:38:53 -06:00
jpkg.sexp Add jpkg packaging 2026-07-21 15:09:28 -06:00
LICENSE Switch to MIT license 2026-07-21 13:42:21 -06:00
Makefile build: add missing security/lint targets 2026-07-21 12:27:03 -06:00
README.md security: bound PNG/JPEG decode time on the C ABI path 2026-07-21 16:37:04 -06:00
SECURITY.md security: bound PNG/JPEG decode time on the C ABI path 2026-07-21 16:37:04 -06:00
test-runner.ss Add Jerboa OCR contract 2026-07-15 08:32:39 -06:00

jerboa-vision

jerboa-vision is the Rust detector that replaces the SSD review app's OpenCV and NumPy dependency path.

It also contains the Jerboa-facing OCR contract in vision/ocr.ss. The OCR engine is deliberately a backend, not a Scheme reimplementation of Tesseract: backends return bounded word records in rendered-image coordinates, and Jerboa owns normalization, nearby-text lookup, and candidate matching. This lets the same recognizer run on the server, in the browser path, and later inside the APK using Android ML Kit or another platform text recognizer.

The first implementation is a native CLI proof:

cargo run --release -- detect /path/to/rendered-ssd-page.png > session.json

It emits JSON with:

  • image width/height
  • detected colored SSD cells
  • adjacent-cell groups

The same core algorithm can run from a CLI, plain C ABI, or Android JNI. Android passes rendered bitmap RGBA bytes directly to Rust, so the APK can detect SSD boxes without Python, OpenCV, MuPDF, Poppler, temp image files, or external process forks.

Detector

Current detector:

  1. Load an RGB image.
  2. Convert pixels to HSV-like saturation/value.
  3. Mask colored cells with s > 12, 120 < v < 252.
  4. Run 8-connected-components.
  5. Keep components with SSD-cell-like size/density.
  6. Group adjacent cells with the Python review app's adjacency heuristic.
  7. Emit stable c0001 and g0001 ids.

Inputs are treated as untrusted. The CLI opens the file once, checks its compressed size and image header before decoding, accepts only PNG/JPEG, and enforces width, height, pixel, decoded-byte, component, queue, grouping, output, and wall-clock budgets. Cell grouping uses a spatial grid rather than an all-pairs graph.

detect always delegates decoding to a disposable Unix worker with a 384 MiB process-memory limit (a kernel-accounted supervisor on macOS and an address-space limit elsewhere), ten CPU seconds, a seven-second parent deadline, and a small file-descriptor limit. The command fails closed on platforms where those worker controls are unavailable. Deployments may add stricter container or cgroup limits, but must not bypass the detect entry point for uploads.

This intentionally starts with the color detector only. Contour/edge fallback can be added after parity scoring shows it is worth the complexity.

Android

Build the Android arm64 shared library:

make android-jni

The staged output is:

build/android-jni/arm64-v8a/libjerboa_vision.so

The shared library exports:

  • jerboa_vision_detect_file_json for plain C callers with a rendered PNG/JPEG path.
  • jerboa_vision_detect_rgba_json for plain C callers with rendered RGBA bytes.
  • jerboa_vision_last_error for ABI error diagnostics.
  • Java_com_sfb_ssdreview_SsdVisionBridge_nativeDetectRgbaJson for the Android SSD review APK.

The JNI method returns compact detection JSON. On failure it returns {"ok":false,"error":"..."} so Kotlin can fall back to its detector without crashing the app.

The C ABI and JNI entry points decode in the caller's process and enforce the application-level byte, dimension, work, decode wall-clock, and output budgets, but not the disposable-worker OS limits used by detect. Sandbox untrusted uploads at the process level (container/cgroup) when calling these entry points directly.

OCR Rule

OCR is deliberately not hard-coded to one engine. The required flow is:

rendered SSD bitmap -> backend OCR words -> vision/ocr.ss nearby text helpers

Android currently uses bundled Google ML Kit text recognition as the OCR backend. A desktop/server build can use another backend, but it must return the same bounded word shape.

The backend word shape is:

((text . "PH-1") (conf . 88) (bbox . (x1 y1 x2 y2)))

The coordinate space is always the rendered SSD image in pixels. That keeps the rest of the SSD recognizer independent of whether OCR came from Android ML Kit, a Rust native backend, or a browser-side implementation.

Browser Rule

The browser path should remain:

pdf.js page render -> Canvas ImageData -> jerboa-vision WASM/native bridge -> session JSON

No native PDF renderer is required on Termux for the browser/server path.