Skip to content

blast752/oscura

Repository files navigation

Oscura — irreversible, on-device document redaction for Android

CI Latest release License: Apache-2.0 Android 8.0+ Kotlin 2.2 No network access

Oscura is an Android app that takes a document — Word, Excel, PowerPoint, PDF, image, plain text, or a photo straight from your camera — and produces a copy in the same format with the sensitive data irreversibly destroyed, not hidden. Names, addresses, fiscal codes, IBANs, phone numbers, emails, ID numbers: gone from the file, unrecoverable by anyone.

Built for the age of AI: share contracts, medical records, invoices and letters with chatbots, clouds or strangers after the personal data has left the file. Everything runs on your device — the app doesn't even declare the INTERNET permission — so nothing you redact ever touches a server. It also doubles as a full document scanner (edge detection, perspective correction, scan filters, multi-page PDF) that replaces the paid tier of typical scanner apps, entirely offline and open source.

Leggi il README in italiano.


Why Oscura

  • Redaction is real. Characters are overwritten inside the file (), pixels are painted over, PDFs are rebuilt image-only with no text layer. There is nothing to "un-hide" — unlike black rectangles drawn over a PDF text layer, the classic redaction failure.
  • Fails closed. If any part of a document can't be rewritten, the whole run aborts. Emitting unredacted bytes is never a fallback.
  • Metadata too. Office author/last-editor/company fields, comment and tracked-changes authors, embedded page-one thumbnails, image EXIF (including GPS) — scrubbed along with the body text.
  • Precision you can audit. Structured data is confirmed by checksums (MOD-97, Luhn, codice fiscale check character) before anything is touched; the whole detection engine is deterministic, pure JVM, and covered by unit tests.
  • Private by construction. No network permission, no telemetry, no analytics, no backup of app data. Temporary files live in the private cache and inputs are deleted when processing ends — even when cancelled.

What it detects

Category How it's recognised
Person names Open lexicons (~9,100 given names, ~36,700 surnames) + honorifics (Sig., Dott.ssa…) + field labels (Cliente:…) + document-wide coreference
Codice fiscale Structure (spaces tolerated) + checksum, with repair of OCR-confused characters (0↔O, 1↔I, 8↔B…)
Partita IVA 11 digits + official checksum + issued office-code range
IBAN / cards / BIC MOD-97 (ISO 13616), Luhn + network prefixes, labelled BIC
Addresses & places Street prefixes (Via, P.zza, Corso… + civic number), postal code + town (ISTAT lexicon), personal context (residente a…, nato a…)
Birth dates Context only (nato il…, data di nascita:) — ordinary dates survive
ID documents & plates Passport, CIE, driving licence, health card (prefix + Luhn), vehicle plates, labelled document numbers
Emails & phones Strict patterns + plausibility rules (Italian/international prefixes, Tel. labels) — protocol numbers and dates don't trigger
Custom terms Words you choose to always redact, case- and accent-insensitive

The accuracy model

Three tiers of evidence, most reliable first:

  1. Checksums (CF, P.IVA, IBAN, cards, health card): a sequence is redacted only when it is mathematically valid data — zero structural false positives.
  2. Context (titles, labels, residente a…): the context itself is the proof, and it also confirms foreign names absent from any lexicon.
  3. Lexicons + supporting rules: a known given name confirms the whole sequence ("Michele Di Pietro", "ROSSI MARIO"); a safe surname catches unknown given names ("Yuki Esposito"); the ~4,300 ambiguous entries ("Rosa", "Ferrari", "Milano") never trigger alone, so ordinary prose survives.

On top of all this sits document-wide coreference: once "Mario Rossi" is confirmed anywhere, every other "Rossi" — a signature, a header, a different sheet of the same workbook — is redacted across the entire document.

When in doubt, the engine over-redacts: for a privacy tool, the false negative is the only truly expensive error.

Supported formats

Format Strategy
Text (.txt/.csv/.md/.log) In-place character replacement with
Word (.docx) Run-level XML text (even split across formatting runs), footnotes, headers/footers, comments, tracked-changes authorship
Excel (.xlsx) Shared and inline strings, comments and threaded comments, drawing text, sheet headers; numeric cells holding validated PII (mobile numbers, P.IVA, cards) become redacted text cells
PowerPoint (.pptx) Slide, notes and comment text, classic and modern comment authors
PDF Rasterise → full-page OCR → black boxes → PDF rebuilt in streaming (constant memory per page)
Images (.jpg/.png/…) OCR → black boxes over the pixels; re-encoding also strips all EXIF (GPS included)
Camera scan Edge detection → perspective correction → scanner filters → JPEG (single page) or PDF (multi-page), then choose: redact or keep

All Office formats additionally get their identifying metadata blanked (author, last editor, company, comment/revision authors) and the embedded first-page thumbnail removed — it would otherwise leak the unredacted content wholesale.

The built-in scanner

The scanner is implemented entirely in-app — no computer-vision library, no runtime-downloaded module:

  • Sheet detection: Sobel gradients + a gradient-guided Hough transform on a small analysis raster; the four corners always remain hand-adjustable with drag handles.
  • Perspective correction: projective transform with proportions recovered from the quad's opposing sides.
  • Filters: Document (per-cell illumination flattening + auto white balance + unsharp text — shadows disappear, paper turns white) and B/W (soft antialiased binarisation). The same enhancement feeds the OCR, so it improves redaction too.
  • Multi-page: multiple shots become a single streamed PDF; at the end you choose to redact the result or save it as-is.

The camera is the system one — the app doesn't request the CAMERA permission. The math core (detection, geometry, filters) is pure Kotlin/JVM in domain/scan/, unit-tested on synthetic images.

Why redaction is irreversible

  • Text / Office: sensitive characters are replaced inside the XML, not hidden — the original no longer exists in the file. If a part can't be rewritten, processing fails closed.
  • PDF / images: pages are rasterised and the pixels under the boxes are destroyed. The output PDF is image-only, with no text layer at all — which transparently covers scanned and photographed PDFs too.

Architecture

domain/pii/        Detection engine: Lexicon (canonical form), centralized Patterns
                   and Validators, composable finders, PiiDetector with multi-chunk
                   scanning and coreference. Pure Kotlin/JVM — the entire accuracy
                   surface is covered by local unit tests.
domain/scan/       Scanner math: quad detection (Hough), geometry, pixel filters.
domain/redaction/  Irreversible replacement with █
data/processing/   One processor per format + RedactionService (single factory)
data/pdf/          ImagePdfWriter: minimal streaming PDF writer (O(1 page) memory)
data/ocr/          On-device ML Kit; OcrPage maps page-level matches to word boxes
data/              LexiconRepository (assets), PreferencesStore (versioned preset)
ui/                Jetpack Compose + Material 3 (Home / Scan review / Progress / Result)
assets/lexicons/   Versioned, regenerable data packs (see DATA_SOURCES.md)

Every processor extracts text its own way and hands it to the same DocumentScanner in a single call — that is what makes coreference work across pages, sheets and paragraphs. Patterns, validators, strings and versions are each defined exactly once.

Updatable data, not hardcoded lists

The lexicons are plain text files in assets/lexicons/, generated from open datasets (ISTAT, MIT-licensed word lists) by tools/build-lexicons.ps1 and validated by smoke tests. Improving recognition doesn't require touching code: regenerate or hand-fix the data pack. Sources and licences in DATA_SOURCES.md.

Performance

Tuned for modern 64-bit ARM SoCs — Snapdragon 8 Gen 1/2/3 and 8 Elite, MediaTek Dimensity 9300/9400 and newer, Samsung Exynos 2400/2500 — while staying compatible down to Android 8.0:

  • Release builds ship arm64-v8a + armeabi-v7a only, R8 full-mode minified with resource shrinking (~20 MB APK, OCR model included).
  • 16 KB page-size compliant (verified with zipalign -P 16), ready for Android 15+ devices booting with 16 KB pages.
  • Baseline profiles from Compose and AndroidX are compiled into the APK and installed on first run via profileinstaller — peak AOT performance without JIT warm-up.
  • The image pipeline works on flat IntArrays with fixed-point arithmetic and single-pass separable filters; PDF pages stream through one at a time, so memory stays constant regardless of document size.
  • No Play-encrypted dependency blob (dependenciesInfo off) and no VCS metadata in the APK: builds are reproducible.

Building

Requirements: JDK 17+ and Android SDK Platform 37 (or an up-to-date Android Studio).

./gradlew :app:testDebugUnitTest    # the engine: validators, finders, coreference, OOXML, PDF writer
./gradlew :app:assembleDebug        # debug APK
./gradlew :app:assembleRelease      # release APK (signed if keystore.properties exists)

To sign releases, copy keystore.properties.sample to keystore.properties and point it at your keystore. CI builds an unsigned release APK on every push; tagged releases are published by the release workflow.

Privacy

  • No network permission, no telemetry, allowBackup=false, cloud backup and device transfer disabled.
  • Temporary files live in the private cache; the imported input is deleted when processing ends, even if cancelled. Output is shared only on your explicit action.
  • Office documents get metadata and thumbnails scrubbed; images get EXIF stripped.

The full statement is in PRIVACY.md.

Known limits & direction

  • Legacy formats (.doc/.xls/.ppt) are unsupported; Office customXml/ parts and Excel chart labels are not scanned.
  • Name detection is optimised for Italian documents; structured detection (IBAN, cards, emails, phones) is already international.
  • OCR uses ML Kit's bundled on-device model — the one closed-source component (which also keeps the app off F-Droid's main repo for now). The PiiFinder seam is designed so an open on-device NER/OCR backend can be added as a drop-in finder without touching the rest.

Contributing

Bug reports, lexicon fixes and detection test cases are the most valuable contributions — see CONTRIBUTING.md. Every change to the detection engine must come with a unit test that pins the new behaviour.

License

Apache-2.0 © blast752. Third-party components are listed in THIRD_PARTY_NOTICES.md.

About

Irreversible, on-device document redaction for Android. Word, Excel, PowerPoint, PDF, images & camera scans - no network, no telemetry.

Topics

Resources

License

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors