This document describes the internal architecture of RedoSan Authenticity, covering the dual-interface design, the CLI's polyfill layer, the data pipeline, MPA navigation, and how browser and Node.js environments share code.
┌─────────────────────────────────────────────────────────────────────┐
│ User Interfaces │
│ ┌──────────────┐ ┌────────────────┐ ┌────────────────────────┐ │
│ │ CLI (Node.js) │ │ Simplified │ │ Professional Mode │ │
│ │ Commander.js │ │ Mode (Browser) │ │ (Browser SPA + MPA) │ │
│ │ 14 commands │ │ 10-step wizard │ │ 23 standalone pages │ │
│ └──────┬───────┘ └───────┬────────┘ └───────┬───────────────┘ │
└─────────┼──────────────────┼──────────────────┼──────────────────┘
│ │ │
┌─────────▼──────────────────▼──────────────────▼──────────────────┐
│ Shared Algorithm Layer │
│ ┌──────────┐ ┌──────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Watermark│ │ Audio WM │ │ Document│ │ Fingerpr│ │ Forensic│ │
│ │ Core (9) │ │ (8 algo) │ │ WM (5) │ │ (21 alg)│ │ Analyzer│ │
│ └──────────┘ └──────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Pixel │ │ C2PA │ │ Metadata│ │ Timestmp│ │ DID │ │
│ │ Inject │ │ Sign/Read│ │ EXIF │ │ OTS │ │ Identity│ │
│ │ (23 alg) │ │ │ │ │ │ │ │ │ │
│ └──────────┘ └──────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌─────────┐ │
│ │Certificat│ │ Converter│ │ ID Forge│ │
│ │ PDF/DOCX │ │FFmpeg.wasm│ │ ID Gen │ │
│ └──────────┘ └──────────┘ └─────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌─────────┐ ┌───────────┐ │
│ │Face Bio │ │ Iris Bio │ │Voice Bio│ │ Removal │ │
│ │WebAuthn │ │ Quality │ │VAD/ECAPA│ │ Tools │ │
│ └──────────┘ └──────────┘ └─────────┘ └───────────┘ │
└──────────────────────────────────────────────────────────────────┘
│
┌─────────▼────────────────────────────────────────────────────────┐
│ Polyfill Layer (CLI only) │
│ crypto.subtle │ document.createElement('canvas') │ │
│ ImageData │ globalThis.window │ Buffer I/O │
└──────────────────────────────────────────────────────────────────┘
The CLI runs browser-original JavaScript files using vm.runInContext(). This is necessary because the browser code uses top-level function and var declarations that are module-scoped in Node.js — require() would fail.
The polyfills are defined in cli/utils.js and injected into the V8 context before each module is loaded:
| Browser API | Node.js Implementation |
|---|---|
crypto.subtle.digest() |
crypto.createHash() for SHA family; custom implementations for WHIRLPOOL, etc. |
crypto.subtle.importKey() |
No-op stub (not used in CLI context) |
crypto.subtle.deriveBits() |
crypto.pbkdf2Sync() with 100,000 iterations |
document.createElement('canvas') |
new canvas.Canvas() from the canvas npm package |
HTMLCanvasElement.getContext('2d') |
canvas.CanvasRenderingContext2D |
CanvasRenderingContext2D.getImageData() |
Returns { data: Buffer, width, height } |
CanvasRenderingContext2D.putImageData() |
Writes pixel data back to canvas |
globalThis.ImageData |
Constructor that accepts (data, width, height) |
globalThis.window |
Empty object (for window.location checks that always pass in CLI) |
globalThis.atob / globalThis.btoa |
Standard Node.js Buffer encoding |
globalThis.crypto.getRandomValues() |
crypto.randomFillSync() |
globalThis.TextEncoder / TextDecoder |
Standard Node.js implementation |
vm.runInContext() Instead of require()?The browser JavaScript files are written with browser conventions:
// Browser file structure (top-level declarations)
function embedWatermark(ctx, message, password) { ... }
function extractWatermark(ctx, password) { ... }
In Node.js, require() wraps the module in a function scope, so these top-level declarations would be private. vm.runInContext() executes the code in the global scope of a new V8 context, making all declarations available to subsequent modules.
cli/utils.js ──→ Polyfills ──→ Shared V8 Context
↓
┌─────────────────────┐
│ Browser JS File 1 │ ← vm.runInContext(src, context)
│ │
│ var fingerprint; │
│ function pHash() {} │
│ ... │
└─────────────────────┘
↓
┌─────────────────────┐
│ Browser JS File 2 │ ← can call fingerprint, pHash, etc.
│ │
│ ... │
└─────────────────────┘
Because the CLI loads browser files in a shared context, all functions and variables from earlier files are available to later files, exactly as they would be in the browser.
The simplified mode orchestrates a 10-step pipeline. Each step produces data that is consumed by subsequent steps:
Step 1: Upload
Output: File object, ArrayBuffer, Object URL
Stored: window._simpleFile, window._simpleArrayBuffer,
window._simpleObjectUrl
Step 2: AI Question
Output: Question string, Answer string
Stored: window._simpleAIQuestion, window._simpleAIAnswer
Step 3: User Info
Output: { name, phone, email, website, social, music }
Stored: window._simpleUserInfo
Step 4: Fingerprint
Input: ArrayBuffer → hashing.js
Output: { md5, sha1, sha256, ..., phash, ahash, dhask, whash }
Stored: window._simpleFingerprint
Visual: Progress bar, results table
Step 5: Timestamp
Input: ArrayBuffer → timestamp.js
Output: OTS file (Uint8Array), timestamp hash
Stored: window._simpleOtsData, window._simpleOtsHash
Visual: Progress bar "Creating timestamp..."
Step 6: Watermark
Input: canvas, message (includes timestamp hash), password
Algorithm: watermark_core.js (auto-selected)
Output: watermarked canvas/blob
Stored: window._simpleWmBlobUrl
Visual: Progress bar "Embedding watermark..."
Step 7: Pixel Injection
Input: watermarked canvas, OTS data, password
Algorithm: pixel_injection.js (enhanced_lsb)
Output: pixel-injected canvas/blob
Stored: window._simplePiBlobUrl
Visual: Progress bar "Injecting pixel data..."
Step 8: C2PA Signing (LAST pixel operation)
Input: pixel-injected blob, user info, social links
Process: c2pa.js → adds C2PA manifest to JPEG APP11 or PNG c2pa chunk
Output: C2PA-signed blob
Stored: window._c2paSignedUrl
Visual: Progress bar "Signing with C2PA..."
Step 9: Digital Passport
Input: All stored data, final signed blob
Output: PDF certificate with QR code
Visual: Progress bar "Generating certificate..."
Step 10: Done
Input: final signed blob, all stored data
Output: Download button, Digital Passport PDF
Visual: Lightbox preview, clear button
Critical constraint: C2PA signing must be the last pixel operation because canvas's toBlob() and toDataURL() strip C2PA chunks from the image data. Any pixel-level operation after C2PA signing would lose the Content Credentials.
The browser uses a global state object pattern (properties on window) rather than a framework like React or Vue:
| Property | Type | Purpose |
|---|---|---|
window._simpleFile |
File | Uploaded file reference |
window._simpleArrayBuffer |
ArrayBuffer | Raw file data for hashing |
window._simpleObjectUrl |
string | Object URL for image preview |
window._simpleAIQuestion |
string | AI question text |
window._simpleAIAnswer |
string | AI answer text |
window._simpleUserInfo |
object | { name, phone, email, website, social, music } |
window._simpleFingerprint |
object | All hash results |
window._simpleOtsData |
Uint8Array | OTS file bytes |
window._simpleOtsHash |
string | Timestamp hash hex |
window._simpleWmBlobUrl |
string | Watermarked image URL |
window._simplePiBlobUrl |
string | Pixel-injected image URL |
window._c2paSignedUrl |
string | Final C2PA-signed image URL |
window._wmLastBlobUrl |
string | Watermark blob URL tracker (for cleanup) |
Each professional mode tool maintains its own isolated state (e.g., window._wmAlgorithm, window._piPassword).
State is cleared by clearSimpleData() which nullifies all properties, revokes Object URLs, and calls initSimplified() to reset the UI.
The browser uses the History API for SPA routing with hash-based navigation:
history.pushState(
{ modeOverlay: 'simplified', modeSet: true, page: 'simple-page' },
'',
'#/simple-page'
)
The popstate event listener restores UI state:
| History State | Effect |
|---|---|
{ page: 'home' } |
Show landing page |
{ page: 'watermark' } |
Show watermark tool |
{ page: 'fingerprint' } |
Show fingerprint tool |
{ page: 'about' } |
Show about page |
| ... 23+ page IDs total |
SEO is handled via PAGE_TITLES and PAGE_DESCS maps in navigation.js. On every showPage() call, document.title and the <meta name="description"> tag are updated.
Each tool has its own standalone page at Style/pages/{name}/index.html. These pages share the same <head>, sidebar, footer, modals, and scripts as index.html, but contain only the single page section for their tool.
The MPA router (mpa-router.js) enables seamless navigation:
fetch()<div id="app"> is swappedhistory.pushState() updates the URLsessionStorageDetection: Pages set data-standalone="page-name" on <html>; navigation.js checks this to determine mode.
The CLI uses Commander.js v14 for command parsing with 14 subcommands:
cli/index.js
├── program.version() / .description()
├── program.command('fingerprint')
│ ├── .argument('<file>')
│ ├── .option('-a, --algo <type>')
│ ├── .option('-j, --json')
│ └── .option('-o, --output <file>')
├── program.command('watermark')
│ ├── .command('embed')
│ └── .command('extract')
├── program.command('metadata')
│ └── ...
├── program.command('audio-watermark')
│ ├── .command('embed')
│ └── .command('extract')
├── program.command('pixel-injection')
│ ├── .command('embed')
│ └── .command('extract')
├── program.command('timestamp')
│ ├── create/verify
├── program.command('c2pa')
│ ├── sign/read/verify
├── program.command('forensic')
├── program.command('did')
│ ├── generate/sign
├── program.command('certificate')
├── program.command('document-watermark')
│ ├── embed/extract
├── program.command('converter')
├── program.command('upgrade')
└── program.command('tools')
Two helpers live outside Commander: cli/menu.js (interactive numbered menu that shells out to index.js) and cli/lib/id_forge.js (standalone ID generator: node cli/lib/id_forge.js <type> [count|file]).
Each command handler:
vm.runInContext()The CLI shares a single --allow-dangerous flag at the top level that bypasses validation for all commands.
The i18n system in Style/i18n.js supports 8 languages:
i18n.js
├── let currentLang = 'en'
├── const translations = { ar: {...}, de: {...}, en: {...}, es: {...},
│ fr: {...}, ja: {...}, ko: {...}, zh: {...} }
├── function __(key, fallback)
│ └── Returns translation or fallback
├── function setLanguage(lang)
│ ├── Updates currentLang
│ ├── Sets dir="rtl" / dir="ltr" on <html>
│ ├── Updates html lang attribute
│ └── Re-translates all [data-i18n] elements
├── function initI18n()
│ └── Detects browser language, loads translations
└── function sanitizeHtml(text)
├── Tag whitelist: h2, h3, p, ul, li, a, br, strong, em, b, i, code, pre, blockquote, ol, span, div
├── Strips event handlers (onclick=, onerror=, etc.)
├── Strips javascript: URLs from href/src
└── Returns safe HTML string
The sanitizeHtml() function is applied to 5 known-safe i18n keys that contain HTML: page.about, page.privacy, page.contact, page.social, and shared.drop_file.
Translation data is loaded from Style/lang/i18n-data-{lang}.js files, aggregated by i18n-data.js.
Security is implemented at multiple layers:
| Layer | Mechanism | Scope |
|---|---|---|
| Service Worker | Extension blocklist (60+), JS/CSS/YML/HTML/JSON/CDN whitelists, URL encoding detection, logo hotlink protection | Browser |
| Domain Self-Check | IIFE regex check on window.location |
All browser JS files |
| File Validation | 6-layer pipeline (extension, magic bytes, content scan, document threats, structure, pooled buffer) | CLI + Browser |
| XSS Prevention | escHtml() for text, sanitizeHtml() for HTML content, tag whitelist |
Browser |
| Prototype Pollution | Filtering in getAdvancedOptions(), case-insensitive key blocking |
Pixel Injection |
| Bot Detection | checkAutomation(), showBotOverlay() for headless browsers |
Browser |
| VPN Detection | detectWebRTCIPs(), startAsyncVPNDetection() |
Browser |
| Biometric Privacy | Face/voice/iris templates stored encrypted on-device (WebAuthn PRF vault for face); never uploaded | Biometrics |
See the [Security] page for detailed documentation.
Browser JS (Style/) ←── Shared by SPA + MPA pages
│
├── Algorithm modules (Watermark/, Pixel_Injection/, etc.)
│ └── Shared between Browser UI + CLI (via vm polyfills)
│
├── Biometrics (Face_Biometric/, Iris_Biometric/, Voice_Biometric/)
│ └── On-device engines (browser-only, WASM/ONNX models from CDN)
│
├── Removal_Tools/ — metadata/watermark/fingerprint stripping
│
├── CLI (cli/)
│ ├── index.js — Commander.js entry (14 commands)
│ ├── menu.js — interactive menu wrapper
│ ├── utils.js — Polyfills + file I/O
│ ├── lib/id_forge.js — standalone ID generator
│ └── commands/ — 12 command handlers
│
└── Service Worker (sw.js)
└── Security enforcement for all pages
The simplified.js file was split into modular files:
simplified.js — Bootstrap and step orchestrationsimplified_countries.js — Country phone code datasimplified_helpers.js — Step helper functionssimplified_renderers.js — Step renderersThe certificate.js file was split into modular files:
certificate.js — Main entrycertificate_pdf.js, certificate_docx.js, certificate_epub.js — Format generatorscertificate_ots.js — OTS integrationcertificate_utils.js — Shared utilitiesThe Pixel Injection module grew to 6 files:
pixel_injection.js — Main classwatermark_core_advanced.js — Core algorithmswatermark_core_algorithms.js — Algorithm-specific implementationswatermark_core_transforms.js — DCT/DWT/DFT transformswatermark_advanced_ui.js — Advanced UI with robustness testingadvanced_watermarking.js — Alternative class
Wiki: API Reference
Wiki: FAQ
Wiki: Face Biometric
Wiki: Home
Wiki: Installation
Wiki: Iris Biometric
Wiki: Security
Wiki: Usage Guide
Wiki: Voice Biometric
Wiki: _Sidebar