Omnia Reader is an offline-first EPUB and PDF reader built from one Angular application for the web/PWA, Tauri desktop, and Android.
The current implementation includes an OPFS-first local library with a
byte-backed IndexedDB fallback, incremental worker hashing, durable EPUB/PDF
covers, explicit duplicate and partial-import outcomes, the maintained
@likecoin/epub-ts EPUB.js-compatible runtime and PDF.js reader engines,
keyboard, wheel, and guarded touch navigation, exact resume locations,
book-wide progress seeking, library reading-status filters, accessible
recoverable local-book removal, immersive
fullscreen reading, format preferences,
fixed-layout and RTL EPUB support, NAV/NCX-relative table-of-contents navigation
with numbered, initially collapsed sections and unnumbered front/back matter,
safe internal links, consent-gated HTTP(S) links, durable bookmarks, highlights
and notes, full-text search, versioned full-library backup archives, a
preserved provider-neutral sync core, PWA offline support, and narrow native
import, exact-edition deep-link, and external-link bridges.
Backups stream directly to File System Access and native save destinations
where supported, with cancellation and a compatible browser download fallback.
Reader panels, desktop Escape, and Android hardware back share deterministic
last-opened-first navigation behavior.
Each reader side panel moves focus to its first useful surface, has an explicit
close control, restores the corresponding toolbar trigger when dismissed, and
returns focus to the publication after navigation. Page arrows, wheel, and
swipe gestures are isolated while a panel is open.
Reader-owned password, external-link consent, and annotation dialogs contain
keyboard focus, start on the safest useful control, restore focus when closed,
and prevent page-navigation shortcuts from acting behind modal consent.
The synchronization core journals books, progress, bookmark tombstones, and
annotation tombstones, verifies immutable publication objects, and communicates
with Git/Git LFS and MEGA gateways. The Angular app exposes
the sync route, durable operation journal, and automatic scheduler. GitHub uses
a state-bound, S256 PKCE-protected GitHub App login with encrypted server-side
sessions; users can install the App, grant repository access, and then select
an existing private repository or create one from the settings flow. Provider
authorization revocations use signed, replay-safe webhooks and shared
user-generation invalidation across gateway replicas. Provider rate limits use
bounded retry deadlines, safe UI guidance, and persisted automatic-sync
backoff without blocking local reading.
See the
universal reader development plan for the
architecture, verified status, and remaining release work. Release candidates
follow the executable gates and recovery procedures in the
release and rollback runbook.
Use Node v26.5.0 from .nvmrc and the locked dependency set:
nvm use
npm ci
npm startThe local-only development application is served at
http://localhost:4300. It keeps books and reading state in the browser,
disables the remote-sync settings route, and never calls /api/sync.
Offline reading needs no sync server. GitHub synchronization is available through the isolated same-origin gateway. Start the gateway and the sync-enabled Angular development build together with:
cp apps/sync-gateway/.env.local.example apps/sync-gateway/.env.local
# Fill in the GitHub App values and generate OMNIA_SYNC_SESSION_KEY.
npm run start:fullFull startup waits up to 60 seconds for the gateway readiness endpoint before
starting Angular. If startup fails, check the gateway output and ensure its
HOST and PORT match apps/omnia-reader/proxy.conf.json. Gateway source
changes rebuild the bundle and restart Node automatically. Development uses a
CommonJS bundle to avoid the deprecated Nx ESM loader; production remains ESM.
The inspector is not enabled by default. Development skips deployment metadata
generation and writes to its own output directory; full startup builds the
gateway once rather than pre-building it again before watch mode.
The gateway listens on 127.0.0.1:3333; the Angular development server proxies
/api/sync to it. The GitHub App/Git LFS and MEGA adapters activate when their
documented environment variables are present and otherwise fail closed. MEGA
uses a private service built with the official SDK; its deployment boundary is
specified in the MEGA SDK bridge contract.
The provider clients use the same-origin endpoints documented in
the sync gateway contract. Provider credentials
stay in that gateway; they are never stored by the Angular app. Development
can persist encrypted sessions across gateway restarts by setting
OMNIA_SYNC_SESSION_DIRECTORY; otherwise sessions are held in memory.
Multi-replica deployments use the documented Redis store with atomic session
rotation and authorization revocation, TTL expiry, and rolling AES key
rotation.
The default browser E2E matrix covers GitHub repository onboarding. The longer two-device Git/LFS and MEGA convergence journeys remain opt-in:
REMOTE_SYNC_E2E=1 npx nx run omnia-reader-e2e:e2e -- src/sync.spec.tsThe opt-in representative publication corpus downloads hash-pinned W3C/IDPF EPUB samples and checks long-form embedded-font rendering, search, and authored RTL navigation in Chromium and WebKit:
REPRESENTATIVE_PUBLICATIONS_E2E=1 npx playwright test \
--config apps/omnia-reader-e2e/playwright.config.ts \
apps/omnia-reader-e2e/src/representative-publications.spec.tsThe pinned native bridge source is an Nx project. After installing the MEGA SDK's documented native dependencies, build it with:
npx nx build mega-sdk-bridge --skip-nx-cacheAlternatively, build the digest-pinned non-root runtime image without installing the native toolchain on the host:
npx nx run mega-sdk-bridge:container --skip-nx-cache
npx nx run mega-sdk-bridge:container-smoke --skip-nx-cacheThe resulting local image is
omnia-reader/mega-sdk-bridge:local. The bridge deliberately listens only on
loopback, so deploy it beside the sync gateway in the same network namespace
(for example, as a Kubernetes sidecar) or place an authenticated TLS proxy on
the bridge host. Publishing its Docker port from an isolated container network
is intentionally not a supported exposure model.
Useful verification commands:
npx nx test omnia-reader
npx nx test reader-domain
npx nx test reader-core
npx nx test reader-epub
npx nx test reader-pdf
npx nx test library-data-access
npx nx test platform
npx nx lint omnia-reader
npx nx lint omnia-reader-e2e
npx nx build omnia-reader --configuration production
npx nx run omnia-reader-e2e:e2e -- --project=chromium
npm run performance:e2e
PWA_E2E=1 npx nx run omnia-reader-e2e:e2e -- --project=chromium src/offline.spec.ts
npm run native:e2e
npm run release:test
npm run release:verifyThe workspace-wide test aggregate currently enters a recursive Nx invocation
through the deferred mega-sdk-bridge:configure-core target. Use the explicit
local-reader commands above until provider/native task orchestration returns to
scope.
The release verifier rebuilds the production web app and gateway, checks the
shared 0.1.0 version, production dependency vulnerabilities, reviewed
licenses, and immutable bridge inputs, then writes normalized npm, Rust, and
bridge-source CycloneDX SBOMs plus SHA-256 artifact manifests under
dist/release/. A synchronization release candidate must additionally pass
--sync-evidence <accepted-manifest.json> so the verifier binds accepted gate
evidence to the exact package version and checked-out commit and includes it in
the checksummed release output. Accepted evidence also requires a clean Git
checkout. The no-argument form verifies source artifacts only and is not
promotion evidence.
Playwright normally uses its installed Chromium. On a development machine with
only a system Chrome, set PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH to that
executable. A nonstandard WebKit launcher can be selected with
PLAYWRIGHT_WEBKIT_EXECUTABLE_PATH.
The Verify GitHub Actions workflow runs the locked test, lint, production
build, dependency-audit, release-metadata, active browser, and installed-PWA
offline gates. After the shared gates pass, it builds unsigned Tauri bundles
on Linux x64, Windows x64, and macOS arm64, plus an Android aarch64 debug APK
and AAB, and retains them as short-lived verification artifacts.
All actions are pinned to immutable commit SHAs and the workflow has read-only repository permissions. These unsigned artifacts prove packaging only; signed release publication remains a separate, protected workflow that requires platform signing identities and explicit release approval.
Deploy the production PWA behind HTTPS. Local EPUB/PDF reading remains
available when synchronization is unconfigured or temporarily unavailable.
The reference container stack serves the production build and proxies
/api/sync to an internal, unexposed gateway:
cp deployment/gateway.env.example deployment/gateway.env
# Replace the placeholders for each provider that this deployment supports.
npm run container:smoke
docker compose --file deployment/compose.yaml up --detach --buildOnly the web service is published, on port 8080 by default. Set
OMNIA_HTTP_PORT to change the host port. Terminate HTTPS at the external load
balancer or reverse proxy, forward requests to this service, and add HTTP
Strict Transport Security there. Use OMNIA_SYNC_REDIS_URL for production or
any multi-replica gateway deployment; a filesystem session directory is only
for a single development process.
The web image derives its response headers from
tools/web-security-headers.mjs, streams publication requests without proxy
buffering, and keeps provider credentials inside the gateway. The CSP meta
element in index.html is a fallback, not a replacement for response headers.
Deploy only the providers configured in the untracked
deployment/gateway.env; absent provider configuration fails closed without
disabling local reading.
Install the Tauri 2 prerequisites for the host operating system, then run:
# Debian and Ubuntu
sudo apt update
sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget file \
libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev \
dbus-x11 xvfbnpm run native:dev
npm run native:build
npm run native:e2eThe similarly named runtime packages are not sufficient for compiling: the
-dev packages provide the GLib, GTK, JavaScriptCoreGTK, and WebKitGTK
pkg-config metadata consumed by Cargo build scripts. The native E2E command
builds a debug-only, feature-gated WebDriver endpoint and drives it directly
through the W3C protocol; Ubuntu does not need a separate
webkit2gtk-driver package. When no desktop session bus is available, run it
as dbus-run-session -- xvfb-run -a npm run native:e2e. The native host
deliberately exposes no general filesystem or shell command to the webview,
and production builds do not include the test endpoint.
Install Android SDK Platform/Build Tools, NDK side-by-side, Java 21, and the
four Rust Android targets described by Tauri. Set ANDROID_HOME, NDK_HOME,
and JAVA_HOME, then run:
npm run android:init
npm run android:dev
npm run android:build -- --debug --apk --target aarch64 --ciThe generated Android Studio project lives under src-tauri/gen/android.
Release APK/AAB output must be signed before distribution.