dbmask: демо санитизации PostgreSQL

Слева боевые данные источника, справа санитизированная копия. Замены детерминированные и сквозные: одно исходное значение везде превращается в один и тот же псевдоним.

Стенд работает офлайн (DBMASK_OFFLINE=1), к LLM не ходит: план и пулы значений зафиксированы заранее, прогон считает только чистые функции.

1. ДО и ПОСЛЕ

Одни и те же строки: пользователь, его тикет и ФИО внутри текста тикета.

ДО: source / users.id = 428
ФИОМихайлов Денис Олегович
emailsolovev.e428@bk.ru
телефон+79101871644
адресг. Омск, ул. Садовая, д. 69
тикет 7Здравствуйте, я Михайлов Денис Олегович, свяжитесь со мной по телефону +79101871644
ПОСЛЕ: target / users.id = 428
ФИОГлафира Антоновна Боброва
emailu44a0a0732f37aede@live.ru
телефон+79290315326
адресМосква, ул. Кедрова, д. 16, кв. 36
тикет 7Здравствуйте, я Глафира Антоновна Боброва, свяжитесь со мной по телефону +79290315326
ДО: source / users.id = 855
ФИОБогданова Ксения Николаевна
emailsokolov.o855@mail.ru
телефон+79103738915
адресNULL
тикет 14Здравствуйте, я Богданова Ксения Николаевна, свяжитесь со мной по телефону +79103738915
ПОСЛЕ: target / users.id = 855
ФИОПанфил Анисимович Капустин
emailu79eff9555db5d3c3@inbox.ru
телефон+79064163819
адресNULL
тикет 14Здравствуйте, я Панфил Анисимович Капустин, свяжитесь со мной по телефону +79064163819
ДО: source / users.id = 1282
ФИОГолубев Матвей Иванович
emailsidorov.m1282@gmail.com
телефон+79105606186
адресг. Новосибирск, ул. Лесная, д. 83
тикет 21Здравствуйте, я Голубев Матвей Иванович, свяжитесь со мной по телефону +79105606186
ПОСЛЕ: target / users.id = 1282
ФИОАггей Эдуардович Фадеев
emailub453111e996c1334@tula.ru
телефон+79351909264
адресКазань, ул. Максимова, д. 37, кв. 21
тикет 21Здравствуйте, я Аггей Эдуардович Фадеев, свяжитесь со мной по телефону +79351909264

Подсвечено ФИО: в теле тикета стоит ровно тот псевдоним, что и в колонке 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 блокирует выкладку копии.

Ссылочная целостность OK
0 осиротевших строк
проверено FK-связей: 3
Объём строк OK
13000 из 13000
таблиц: 4
Сквозная консистентность OK
4848 связок сохранено
проверок: 2, расхождений: 0
Разнообразие OK
минимум 56% от исходного distinct-ratio
колонок: 9, порог: 30%
Остаточные ПДн OK
0 матчей в сэмплах
regex-детектор плюс словарь значений источника
вердикт PASS, снимок 13:16:19, прогон 0.5 c, строк 13000

Прогон пересоздаёт копию целиком: санитизация плюс verify. Не чаще раза в 10 секунд.

3. Проверить самому

Копия отдаётся под read-only пользователем, доступ выдаёт оператор стенда.

источник (боевые данные, доступ не выдаётся)недоступен
санитизированная копия (read-only)psql postgresql://viewer:viewer@sanitizer.zzzai.ru:5433/demo

Запросы к копии:

-- 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/