8.6 KiB
Obfuscator
Obfuscator accepts documents in a browser, finds sensitive entities, replaces them consistently across a file set, and returns obfuscated documents plus a separate mapping CSV.
Runtime Boundary
The application and its upload buffer are separate deployable components:
| Component | Runs on | Responsibility |
|---|---|---|
| Obfuscator Flask application | Managed Flask / Shturval | UI, API, in-memory sessions, document extraction, LLM calls, replacement, and downloads |
| Obfuscator upload buffer | VM213, Nginx WebDAV | Temporary staging of browser uploads until Flask pulls them |
VM213 does not run the Flask application or document-processing code. The buffer is a companion service dedicated to Obfuscator and is not the separate vmfiles.service API.
Request Flow
Browser
-> GET / Flask serves the page
-> GET /file-picker/... Flask serves the picker bundle
-> PUT obfuscator-upload.sinefo.ru/obfuscator-upload/<key>
Nginx writes a temporary file on VM213
-> POST /api/upload_refs Flask pulls that file with GET, stores bytes
in the session, then sends DELETE to VM213
-> GET /api/process_stream/<sid> Flask runs document processing and streams SSE progress
-> GET /api/download/<sid> Browser downloads the obfuscated ZIP
-> GET /api/csv/<sid> Browser downloads the mapping separately
The browser sends files one at a time: it waits for the WebDAV PUT, then asks Flask to pull that file before moving to the next one. The buffer therefore holds files only during transit, not for the complete processing session. If the Flask pull fails, the buffer object may remain until manually removed or otherwise cleaned up; the current Nginx config does not define a TTL cleanup job.
Application Components
Browser UI and picker
site/templates/index.htmlprovides the Obfuscator UI, sets accepted extensions and limits, and configures the VM buffer URL.site/static/dist/file-picker.iife.jsis the browser bundle loaded by the page.upload/frontend/index.jsimplements the reusable picker API;upload/frontend/table/handles file/folder selection, tree rendering, deduplication, and status updates.upload/frontend/upload/put_to_vm.jssends a raw file body withPUTand reports progress.upload/frontend/upload/upload_via_vm.jscoordinates the per-filePUTand/api/upload_refsrequests.
The UI accepts .pdf, .doc, .docx, .txt, .md, and .zip inputs. ZIP archives are expanded by the browser picker, and their allowed inner files are uploaded individually. The backend drhider/extractor.py also expands ZIPs received directly (for example, through POST /api/upload). Per-file (50 MiB) and per-session (500 MiB) limits are enforced by the backend; the picker's entry/size/depth limits apply only while unpacking ZIP archives.
Flask composition and routes
site/app.py creates the Flask application and configures request limits and logging. The platform starts the Flask app from site/; it does not deploy this VM buffer config.
site/routes/__init__.py registers the page, health, processing API, and upload_refs blueprints. site/routes/main_bp.py serves the page, the picker bundle (/file-picker/...), and raw ES modules (/upload/..., /dist/...). site/routes/health_bp.py exposes /health with the application version.
Main endpoints:
| Endpoint | Purpose |
|---|---|
GET / |
Render the browser UI |
GET /health |
Liveness and version response |
GET /file-picker/<path> |
Serve the built picker bundle |
GET /upload/<path>, GET /dist/<path> |
Serve raw picker ES modules and dist assets |
POST /api/upload_refs |
Pull browser-uploaded buffer objects into a Flask session |
POST /api/upload |
Direct multipart upload endpoint |
GET /api/session_files/<sid> |
Inspect files in a session (diagnostic endpoint) |
GET /api/process_stream/<sid> |
Process a session and stream progress using Server-Sent Events |
POST /api/process/<sid> |
Synchronous/legacy processing endpoint |
POST /api/cancel/<sid> |
Request a cooperative stop between processing steps |
GET /api/download/<sid> |
Download the obfuscated ZIP |
GET /api/csv/<sid> |
Download the mapping CSV separately |
Upload bridge and session state
upload/backend/upload_refs/blueprint.py validates names, accepts only URLs under the configured VM-buffer prefix, enforces limits, retries failed pulls, and deletes successfully pulled objects. pull_file.py reads the HTTP response in chunks; the resulting file bytes are then kept in the Flask session.
upload/backend/session/ owns session lifecycle. Sessions use UUID identifiers and process-local RAM; files, the ZIP result, the CSV, and cancellation state are not stored in a database. The configured limits are 50 MiB per file, 500 MiB per session, and a 30-minute TTL. The TTL is paused while processing and resumes afterwards. A Flask process restart loses its sessions and results.
Document-processing core
site/routes/api_bp.py loads all session files and invokes drhider.obfuscate_files once for the set. This lets the core use one shared mapping so the same entity receives the same replacement across documents.
The core package is split by responsibility:
drhider/extractor.pyconverts.docx,.pdf, and.doccontent to text/Markdown and expands ZIP archives with ZIP-bomb protections..docconversion calls the configured conversion service.drhider/scanner.pydetects entities with regular expressions and LLM-based NER, building the original-to-replacement mapping.drhider/replacer.pyapplies that mapping to extracted text.drhider/builder.pybuilds the obfuscated ZIP and mapping CSV. The mapping CSV is deliberately not included inside the ZIP; it is downloaded through its separate endpoint.drhider/llm_client.pycalls an OpenAI-compatible LLM endpoint and tracks token/time statistics.
Processing runs in a worker thread for the SSE endpoint. Progress, per-file events, LLM activity, completion, cancellation, and errors are sent to the browser as SSE events. Cancellation is cooperative; the current operation/file may finish before the worker stops.
The LLM client reads LLM_KEY or LLM_API_KEY, with optional LLM_URL (default https://api.aillm.ru/v1/chat/completions) and LLM_MODEL (default gpt-oss-120b), from the Managed Flask environment. The .doc converter URL is configured with CONVERT_SERVICE_URL (default https://liberta.containerk8s.dev.nubes.ru/convert); .doc files are converted sequentially to avoid overloading the conversion service. Secrets belong in service environment settings, never in this repository.
VM213 WebDAV Buffer
The source of the Obfuscator Nginx config is deploy/vm213/nginx/obfuscator-upload.conf. On VM213 it is installed as /etc/nginx/sites-enabled/obfuscator-upload.conf.
- Hostname:
obfuscator-upload.sinefo.ru - WebDAV path:
/obfuscator-upload/ - Disk staging directory:
/var/www/obfuscator-upload/ - TLS certificate:
/etc/letsencrypt/live/obfuscator-upload.sinefo.ru/ - Maximum Nginx request body: 1 GiB; the Flask upload bridge is stricter at 50 MiB per file.
- HTTP redirects to HTTPS; the ACME challenge is served from
/var/www/acme.
The CORS allowlist is for the Obfuscator browser origin https://obfuscator.pythonk8s.services.ngcloud.ru. CORS is a browser policy, not authentication; direct HTTP clients are not blocked by CORS. The VM disk is temporary staging, not durable storage. A VM or disk failure can lose files that have not yet been pulled by Flask.
To update the VM config, update this repository source, install the same file on VM213, run nginx -t, and reload Nginx only if validation succeeds. The Managed Flask deployment and Nginx deployment are independent; deploying one does not deploy the other.
Repository Map
site/
app.py Flask app factory and startup entry point
routes/ UI, health, and processing/upload API blueprints
templates/ Browser application page
static/dist/ Built picker bundles served to the browser
upload/
frontend/ File picker and browser-to-buffer transport
backend/session/ In-memory sessions, limits, results, and TTL
backend/upload_refs/ VM-buffer pull bridge
drhider/
extractor.py Document and ZIP input handling
scanner.py Regex and LLM entity discovery
replacer.py Mapping application
builder.py ZIP and mapping CSV output
deploy/vm213/nginx/ Source config for the Obfuscator VM213 buffer