dbmask: демо санитизации PostgreSQL
Слева боевые данные источника, справа санитизированная копия. Замены детерминированные и сквозные: одно исходное значение везде превращается в один и тот же псевдоним.
Стенд работает офлайн (DBMASK_OFFLINE=1), к LLM не ходит: план и пулы значений зафиксированы заранее, прогон считает только чистые функции.
1. ДО и ПОСЛЕ
Одни и те же строки: пользователь, его тикет и ФИО внутри текста тикета.
Подсвечено ФИО: в теле тикета стоит ровно тот псевдоним, что и в колонке users.full_name той же строки. Телефон в тексте совпадает с users.phone, а payments.payer_name совпадает с ФИО владельца заказа.
| связка | в источнике | в копии | |
|---|---|---|---|
| payments.payer_name совпадает с users.full_name владельца заказа | 4706 | 4706 | OK |
| ФИО из users встречается в тексте support_tickets.body | 142 | 142 | OK |
2. VERIFY как evals-контур
Те же проверки гоняются в CI: ненулевой exit code при FAIL блокирует выкладку копии.
Прогон пересоздаёт копию целиком: санитизация плюс verify. Не чаще раза в 10 секунд.
3. Проверить самому
Копия отдаётся под read-only пользователем, доступ выдаёт оператор стенда.
Запросы к копии:
-- 1. настоящих ФИО, почт и телефонов в копии нет SELECT id, full_name, email, phone FROM users ORDER BY id LIMIT 5;
-- 2. сквозная консистентность: плательщик совпадает с владельцем заказа SELECT u.id, u.full_name, p.payer_name FROM payments p JOIN orders o ON o.id = p.order_id JOIN users u ON u.id = o.user_id WHERE p.payer_name IS NOT NULL LIMIT 5;
-- 3. в свободном тексте стоит тот же псевдоним, что и в users SELECT t.id, u.full_name, t.body FROM support_tickets t JOIN users u ON u.id = t.user_id WHERE position(u.full_name in t.body) > 0 LIMIT 3;
-- 4. исходные домены и коды телефонов не переехали (ожидается 0) SELECT count(*) FROM users WHERE email LIKE '%@mail.ru' OR email LIKE '%@yandex.ru' OR phone LIKE '+7910%';
Код, план, отчёты verify и тесты: https://github.com/xrn6px6xyy-svg/db-sanitizer
Как работала LLM (в прогоне стенда её нет)
LLM вызывается только на этапе планирования, один раз на схему: классификация колонок по классам ПДн, выбор стратегии замены для спорных колонок, генерация пулов правдоподобных ФИО и адресов.
Результат зафиксирован как ревьюируемые артефакты (plan.yaml и пулы значений), поэтому исполнение и verify детерминированы, повторяемы и не требуют ни ключей, ни сети. Per-value обращение к LLM в горячем пути отклонено осознанно: оно недетерминировано и ломает сквозную консистентность.
Сырые ответы модели живого прогона: demo/artifacts/llm_transcripts/