Dexter automatically redacts sensitive information from PowerPoint decks. Upload a deck, and Dexter extracts its content — text, tables, charts (including think-cell), and images — runs it through an LLM redaction pipeline to anonymize client names and confidential data, and returns a clean, redacted copy of the original deck.
It runs as three services that share a single Microsoft Entra app registration for authentication:
- .NET API — main backend: Entra SSO, Microsoft Graph relay, job orchestration, and deck extraction. Enqueues redaction work onto Azure Service Bus.
- Python worker — background consumer (no HTTP ingress): pulls tasks off Service Bus, calls Azure OpenAI to redact and review content, and writes results to Blob Storage / SQL.
- Frontend SPA — React + Vite single-page app for uploading decks and tracking jobs.
| Path | What's inside |
|---|---|
dotnet/ |
.NET 9 minimal API. Key areas: Application/Deck/ (deck + think-cell extraction), Application/Redaction/ (rule application), Application/Jobs/ (job lifecycle), Infrastructure/ (Entra/Graph, Service Bus, Blob, SQL). |
python/ |
Background redaction worker. Key areas: redact/ (extraction + LLM pipeline), prompt_db/ (feature-based prompt/example selection), utils/ (rate limiting, diffing). |
frontend/ |
React + Vite SPA. Views live in src/views/; MSAL auth and API wiring in src/. |
documentation/ |
Deeper guides: setup, environment-variable matrix, and production architecture. |
scripts/ |
Operational helpers (firewall, ad-hoc SQL). |
Additional docs:
documentation/setup.md– full local + production setup guide.documentation/config.md– environment variable matrix.documentation/prod.md– production architecture and security posture.
Work on feature branches (e.g.
dev/...). Only merge tomainwhen you want to ship — pushes tomaintrigger all production deployments.
Create a
.env.localat the repo root before running anything (seedocumentation/config.mdfor the full variable list). All services read from it.
az login
cd dotnet
ASPNETCORE_ENVIRONMENT=Development ASPNETCORE_URLS=https://localhost:5000 dotnet watch runVisit https://localhost:5000/health to confirm it's running (trust the dev
certificate if prompted).
cd python
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
LOG_LEVEL=INFO python app.pyConsumes Service Bus queues (redact/review), calls Azure OpenAI, and writes results to Blob/SQL. No HTTP ingress.
cd frontend
npm install
npm run devOpens at http://localhost:5173; MSAL scopes/URLs come from .env.local.
Deployment is fully automated via GitHub Actions on push to main:
frontend/→ Azure Static Web Appsdotnet/andpython/→ Azure Container Apps (container workflows)
The dev branch is for local testing only — nothing deploys from it. After the
main workflows finish, browse the production SPA and confirm the API status cards
return data. The Container Apps require HTTPS origins in ALLOWED_ORIGINS; keep the
SPA and PowerPoint add-in domains listed there.
Resource names below are placeholders — substitute your own.
az containerapp logs show \
--name <container-app-name> \
--resource-group <resource-group> \
--follow# List job artifacts
az storage blob list \
--account-name <storage-account> \
--container-name jobs \
--output table
# Clear job artifacts
az storage blob delete-batch \
--account-name <storage-account> \
--source jobs-- Latest jobs and their status
SELECT TOP 10 Id, Status, CreatedAt, CompletedAt, FinalBlobPath
FROM Jobs
ORDER BY Id DESC;
-- Tasks for the latest job
SELECT *
FROM JobTasks
WHERE JobId = (SELECT TOP 1 Id FROM Jobs ORDER BY Id DESC);
-- Clear jobs (tasks first, due to FK)
DELETE FROM JobTasks;
DELETE FROM Jobs;If you get ImportError: symbol not found in flat namespace '_SQLAllocHandle', your
Python is x86_64 (Intel) but unixodbc is ARM64. Fix by using ARM64 Python:
# 1. Install ARM64 Python via Homebrew
brew install python@3.9
# 2. Recreate venv with ARM64 Python
cd python
rm -rf .venv
/opt/homebrew/bin/python3.9 -m venv .venv
source .venv/bin/activate
# 3. Verify it's ARM64 (should print: arm64)
python -c "import platform; print(platform.machine())"
# 4. Install dependencies with proper linking
export CFLAGS="-I/opt/homebrew/opt/unixodbc/include"
export LDFLAGS="-L/opt/homebrew/opt/unixodbc/lib -lodbc"
pip install --upgrade pip setuptools wheel
pip install -r requirements.txt
# 5. Test pyodbc
python -c "import pyodbc; print('Success')"