Anything in. A real book out.
Epub Forge is the conversion engine behind every kaOS book. A PDF walks in; a chaptered, navigable, semantically-anchored EPUB walks out. A lecture recording, a scan, a Kindle file, a folder of Markdown — the same forge handles them all. It is a workspace package, a Cloud Run service, and a sovereign local lane. One contract, three doors.
@REPO/EPUB-GENERATOR · CLOUD RUN · SOVEREIGN LANE
Twelve source formats. One forge.
Most converters handle one format and pretend the others don't exist. The forge routes each source to its strongest extractor — Unstructured for PDF, Gemini for audio and image, LangChain for DOCX, lingo-reader for MOBI — then walks them all into the same chapter builder.
Not a zipped HTML folder. A real EPUB.
The output is EPUB 3.3 — mimetype stored uncompressed, a valid package document, a navigation document with TOC and landmarks, and a production stylesheet that respects every typographic rule a real book deserves. It opens in Folio, in Apple Books, in Calibre, in any reader that honours the standard.
book.epub
├── mimetype ← stored, uncompressed
├── META-INF/
│ └── container.xml
└── OEBPS/
├── content.opf ← package document (manifest + spine)
├── nav.xhtml ← EPUB 3 navigation (TOC, landmarks, page-list)
├── toc.ncx ← fallback navigation for EPUB 2 readers
├── stylesheet.css ← 2,500-line production stylesheet
├── cover.xhtml ← image or CSS-typographic cover
├── titlepage.xhtml ← front matter
├── copyright.xhtml ← front matter
├── chapter1.xhtml ← semantic CFI anchors on every element
├── chapter2.xhtml
└── images/ · audio/Four stages. One contract.
Every source walks the same path. Ingest, extract, build, ship. The extraction strategy changes per format; the contract that crosses each stage does not.
One package. One service. One sovereign lane.
The forge is the same code three ways. Import it as a workspace package and call it from any Node process. POST to the Cloud Run service and let it run serverless. Or run the sovereign forge-processor on your own machine, where your manuscripts never leave the substrate. The lane resolver in apps/api picks the right one based on a single env var.
import { generateEpub } from '@repo/epub-generator';
const result = await generateEpub({
contentId: 'doc-0421',
userId: 'user_123',
storagePath: 'uploads/lecture.mp3',
originalFormat: 'audio',
title: 'The Face of God — Lecture',
author: 'Fr. Kaleeg Hainsworth',
language: 'en',
});POST https://kaos-epub-parser-XXXX.run.app/process-from-storage
Content-Type: application/json
{
"storagePath": "users/user_123/uploads/book.pdf",
"userId": "user_123",
"title": "An Altar in the Wilderness"
}# Sovereign lane — local forge processor MALAIKA_LANE_FORGE=local FORGE_PROCESSOR_MODE=live # The cron caller in apps/api resolves the lane: # local → LOCAL_FORGE_URL # cloud → Cloud Run # Either way, the contract is identical.
The lane resolver lives in apps/api/app/api/cron/process-embeddings/route.ts. Set MALAIKA_LANE_FORGE=local and the cron caller routes to LOCAL_FORGE_URL if set, otherwise Cloud Run. The full environment reference is in the forge-processor source.
Every conversion leaves a paper trail.
Each conversion emits structured JSON to stdout — one object per line, consumable by any log drain. Format, strategy, page count, chapter count, word count, cost (for Unstructured), duration. The sovereign forge-processor follows the same convention so the lane-stats producer can filter on source=sovereign_forge_processor.
{"severity":"INFO","source":"kaos-epub-parser","event":"epub.completed","contentId":"doc-0421","format":"pdf","strategy":"hi_res","pages":312,"chapters":18,"wordCount":84210,"costUsd":0.42,"durationMs":18420,"epubPath":"users/user_123/library/processed/doc-0421.epub"}
{"severity":"INFO","source":"sovereign_forge_processor","lane":"forge","event":"forge.received","jobId":"job-7","canvasItemCount":4,"promptLen":820}


