fix: сторож заготовок ловит запись разговора, переименованную в заготовку

Надзиратель вскрыл дыру в самом опасном месте. Мой замок на личные данные
смотрел только на имя файла, а имя подделывается первым. Запись разговора,
положенная как obraztsy/Kakoe-to-imya.mp3 и вписанная в перечень слепков,
проходила ВСЕ проверки насквозь.

Замков стало три, и каждый ловит своё:
- по имени, был — ловит небрежность, ничего не стоит, работает без машины;
- по слепку, новый — слепок заготовки сверяется со слепками живых записей на
  машине; имя обмануть можно, слепок нельзя;
- по содержимому, новый — запись разговора всегда WAV и всегда начинается с
  RIFF; любой RIFF среди заготовок обязан совпасть с одним из трёх известных
  слепков. Ловит подлог даже тогда, когда оригинал на машине уже съеден
  месячной уборкой.

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

Машину не грузим: сначала сравниваются размеры, и только совпавшие по размеру
записи считаются слепком. Точная копия весит ровно столько же, значит замена
полная. Сегодня совпадений по размеру ноль — вместо чтения 63 МБ звука машина
читает одно оглавление. Решение владельца Р84.

Показано красным двумя ножами, и ни одного чужого байта в дереве:
- настоящая запись с машины, у которой длина звука ноль отсчётов, положена под
  безобидным именем — замки 2 и 3 назвали её поимённо, замок 1 остался зелёным;
- подлог настоящего веса собран из наших же заготовок в отдельной папке на
  машине — замок 2 назвал оба файла.
Подложенное убрано, возврат доказан cmp против содержимого коммита.

Чего и новая защита не ловит: запись, ПЕРЕСЖАТУЮ в mp3. Записано в отчёте.

Мерки: сторож 23 из 23, красных 0. Живая папка на машине не тронута.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-08-06 17:30:22 +03:00
parent 588f05aef1
commit a5bd891d0a
3 changed files with 300 additions and 7 deletions
+41 -2
View File
@@ -521,7 +521,7 @@ cd ~/lena-inworld && venv/bin/python proba-mosta-bez-zvonka.py
bash bots/lena-golos/proverka-zagotovok.sh
```
21 проверка, и она **называет поимённо**, что именно пропало или испортилось.
23 проверки, и она **называет поимённо**, что именно пропало или испортилось.
Смотрит на четыре вещи, каждая из которых по отдельности была бы слепой:
1. **хранилище** — все 43 файла на месте и сходятся со слепками. Не «файл
@@ -550,6 +550,45 @@ LENA_BEZ_OPYTA=da bash bots/lena-golos/proverka-zagotovok.sh
🔴 **Сторожа надо кому-то запускать.** Сам он не ходит — ровно тот же открытый
хвост, что и у сторожа уборки.
### 🔴🔴 Три замка на личные данные — и почему одного мало
Заготовки лежат в той же папке, что и записи разговоров живых людей. Значит
самая страшная ошибка тут — не «заготовка пропала», а **«запись разговора
уехала в хранилище под видом заготовки»**. Замков три, и каждый ловит своё.
| замок | как ловит | что ловит | чем обманывается |
|---|---|---|---|
| 1 — по имени | имя похоже на `1785699262.18.wav` | небрежность: принесли и не переименовали | переименованием за секунду |
| 2 — по слепку | слепок заготовки совпал со слепком живой записи на машине | умысел: запись переименовали в «образец голоса» и даже вписали в перечень слепков | если оригинал уже съеден месячной уборкой |
| 3 — по содержимому | файл начинается с `RIFF`, а слепок не из трёх известных | то же, что и 2, но **даже когда записи на машине уже нет** | только пересжатием звука в mp3 |
**Почему замка 1 мало.** Он смотрит на имя, а имя — это то, что подделывается
первым. Файл, положенный как `obraztsy/Kakoe-to-imya.mp3` и вписанный в
перечень слепков, проходил **все** проверки: и «файлов столько же, сколько
слепков», и «все сходятся со слепками», и «лишних нет». Замерено 06.08.2026 —
это нашёл надзиратель, а не сторож.
**Почему замок 2 не грузит машину.** Сначала сравниваются **размеры**, и только
совпавшие по размеру записи считаются слепком. Это не поблажка, а полная
замена: точная копия файла весит ровно столько же, сколько исходник. Сегодня
совпадений по размеру **ноль** — значит машина не читает ни одного байта звука
вместо 63 МБ. Машина делит железо с боевым рендером, решение владельца Р84.
**На чём стоит замок 3.** Запись разговора — это **всегда** файл WAV: их пишет
`MixMonitor` телефонии, и такой файл начинается с четырёх букв `RIFF`. Замерено:
среди 43 заготовок файлов RIFF ровно **три**, и все три лежат верхним ярусом —
`chelovek.wav`, `chelovek-nomer.wav`, `chelovek-dorogo.wav`. Внутри восьми папок
подбора RIFF нет ни одного: их наполняют скрипты проб, а те пишут mp3.
🔴 **Три известных слепка прописаны в самом стороже, а не берутся из
`zagotovki-slepki.sha256`** — и это главное. Перечень слепков подделывается тем
же движением, что и сама подмена: положил файл — вписал строку. Список внутри
сторожа этим движением не подделать.
🪤 Поэтому **добавление честной новой заготовки WAV требует правки строки в
стороже руками.** Так и задумано: WAV — единственный вид файла здесь, способный
нести чужой голос. Для mp3 никакой такой возни нет.
### Как вернуть заготовки на машину руками
Обычно этого делать не надо — правило справляется само при загрузке. Но если
@@ -735,7 +774,7 @@ $ sed 's/;.*//' bots/lena-golos/extensions-lena.conf | grep -c ZAPIS_RAZGOVOROV
<!-- Имена файлов и папок на сервере записаны транслитом. В общий словарь
проекта их не тащим — они живут только здесь. -->
<!-- cspell:ignore chelovek syroy brend dyhanie obraztsy tempy udarenie zhivost chistka zapisey proverka vhodyashchie ZHURNAL SROK CHASOV PAPKA ZAPIS RAZGOVOROV dialplan globals nabor proba mango vhodyashchiy priyomshchica imya lena golos storozh mosta tishina most metodika klienta chistki raspisaniya journalctl tekstovyy sled logrotate copytruncate notifempty maxage dateext nocompress progon prob terpenie mozgi svezhiy podpapka kluch rech cheloveka tekstovogo sleda челове zagotovki zagotovok slepki vosstanovleniya postoyanno zacepka Alyona Snezhana skorost bylo stalo vdoh bodro teplo defis prosto zaglavno znak dorogo nomer -->
<!-- cspell:ignore chelovek syroy brend dyhanie obraztsy tempy udarenie zhivost chistka zapisey proverka vhodyashchie ZHURNAL SROK CHASOV PAPKA ZAPIS RAZGOVOROV dialplan globals nabor proba mango vhodyashchiy priyomshchica imya lena golos storozh mosta tishina most metodika klienta chistki raspisaniya journalctl tekstovyy sled logrotate copytruncate notifempty maxage dateext nocompress progon prob terpenie mozgi svezhiy podpapka kluch rech cheloveka tekstovogo sleda челове zagotovki zagotovok slepki vosstanovleniya postoyanno zacepka Kakoe Alyona Snezhana skorost bylo stalo vdoh bodro teplo defis prosto zaglavno znak dorogo nomer -->
<!-- 🪤 Слова обязаны стоять на ОДНОЙ строке с cspell:ignore: указание читается
до конца строки, и перенесённые вниз слова не только не попадают в
словарь, но и сами становятся ошибками. Замерено 06.08.2026. -->
+109 -2
View File
@@ -46,6 +46,10 @@ KLYUCH="${LENA_KLYUCH:-$HOME/.ssh/liderra_deploy}"
# Пути на сервере. Меняются вместе с эталоном, не по отдельности.
PAPKA_TAM="/tmp/lena-inworld"
# Где на машине лежат записи разговоров, с которыми сверяется замок 2.
# Переменной подменяется только ради проб самого сторожа — чтобы показать его
# красным, не трогая живую папку с чужими голосами.
PAPKA_ZAPISEY="${LENA_PAPKA_ZAPISEY:-$PAPKA_TAM}"
POSTOYANNO_TAM="/home/ubuntu/lena-inworld/zagotovki"
PRAVILO_TAM="/etc/tmpfiles.d/lena-zagotovki.conf"
# 🔴 Имя нарочно длинное и явно временное — чтобы его нельзя было спутать ни с
@@ -74,6 +78,43 @@ itog_i_vyyti() {
# заготовках их быть не может НИКОГДА.
OBRAZETS_ZAPISI='^(vh-)?[0-9]{9,}\.[0-9]+\.wav$'
# 🔴🔴 ТРИ ЗАМКА НА ЛИЧНЫЕ ДАННЫЕ, И КАЖДЫЙ ЛОВИТ СВОЁ.
#
# Замок 1 — ПО ИМЕНИ. Ловит небрежность: файл принесли и не переименовали.
# Дешёвый. Обманывается переименованием за одну секунду.
# Замок 2 — ПО СЛЕПКУ против живых записей на машине. Ловит умысел:
# запись переименовали в «образец голоса» и даже вписали в
# перечень слепков. Имя обмануть можно, слепок нельзя.
# Замок 3 — ПО СОДЕРЖИМОМУ. Ловит то, чего замок 2 уже не достанет:
# запись, которой на машине больше НЕТ — её съела месячная
# уборка, — но копия осела в заготовках.
#
# 🔴 Почему замок 1 не убран, хотя замки 2 и 3 сильнее. Он ничего не стоит и
# ловит частый случай — усталую руку, а не злой умысел. Замки 2 и 3 требуют
# живой машины и чтения байтов; замок 1 работает всегда.
#
# Основание замка 3. Запись разговора — это ВСЕГДА файл WAV: их пишет
# MixMonitor телефонии, и такой файл начинается с четырёх букв «RIFF».
# Замерено 06.08.2026: среди 43 заготовок файлов RIFF ровно ТРИ, и все три
# лежат верхним ярусом — chelovek.wav, chelovek-nomer.wav, chelovek-dorogo.wav.
# Внутри восьми папок подбора RIFF нет ни одного: их наполняют скрипты проб,
# а те пишут mp3 — все 39 файлов начинаются с байтов ff fb.
#
# 🔴 Поэтому правило замка 3 закрытое и НЕ зависит от имени файла: любой файл
# RIFF среди заготовок обязан совпасть слепком с одним из трёх известных.
# Переименованная запись — тоже RIFF, но слепок у неё другой, и она
# попадётся, даже если её вписали в перечень слепков и положили на машину.
#
# 🔴 Три слепка стоят ЗДЕСЬ, а не берутся из zagotovki-slepki.sha256, и это
# главное. Перечень слепков подделывается тем же движением, что и сама
# подмена: положил файл — вписал строку. Список, лежащий в стороже,
# подделать этим движением нельзя. Добавление честной новой заготовки WAV
# потребует правки этой строки руками — так и задумано: WAV здесь
# единственный вид файла, способный нести чужой голос.
IZVESTNYE_WAV="ad7e7c6b819e305fe4eb91241696a848fc4c2e0874816709ec47d2df69c4565e
70a352acf364bf6953f735a7c02633bdd9f49fe7e6cc06ac4396d0fd30aaa8af
71ad42e774ff6a49f348b3886d4d1ae922b5e38d94002bf919c8f9dedbb25c18"
echo "Сторож заготовок подбора голоса."
echo
@@ -125,11 +166,31 @@ fi
# настоящего разговора. Если оказалась — это утечка, и круг остановлен.
ZAPISI="$( cd "$ZAGOTOVKI" && find . -type f -printf '%f\n' | grep -E "$OBRAZETS_ZAPISI" )"
if [ -z "$ZAPISI" ]; then
zelenyy "записей разговоров среди заготовок НЕТ — личные данные не утекли"
zelenyy "замок 1, по имени: файлов с именем записи разговора нет"
else
krasnyy "🔴 СРЕДИ ЗАГОТОВОК ЗАПИСЬ РАЗГОВОРА — ЭТО ЛИЧНЫЕ ДАННЫЕ:"
krasnyy "🔴 замок 1: СРЕДИ ЗАГОТОВОК ЗАПИСЬ РАЗГОВОРА — ЭТО ЛИЧНЫЕ ДАННЫЕ:"
echo "$ZAPISI" | sed 's/^/ | /'
fi
# 🔴🔴 ЗАМОК 3 — ПО СОДЕРЖИМОМУ. Имя не спрашиваем вовсе: читаем первые четыре
# байта. Запись разговора — всегда RIFF, и слепок у неё не совпадёт ни с
# одним из трёх известных.
CHUZHOY_WAV=""
while IFS= read -r F; do
[ -n "$F" ] || continue
[ "$(head -c 4 "$ZAGOTOVKI/$F" 2>/dev/null)" = "RIFF" ] || continue
H="$(sha256sum "$ZAGOTOVKI/$F" | cut -d' ' -f1)"
echo "$IZVESTNYE_WAV" | grep -qxF "$H" || CHUZHOY_WAV="$CHUZHOY_WAV$F слепок $H
"
done <<EOF
$( cd "$ZAGOTOVKI" && find . -type f | sed 's#^\./##' )
EOF
if [ -z "$CHUZHOY_WAV" ]; then
zelenyy "замок 3, по содержимому: все файлы RIFF среди заготовок — из трёх известных"
else
krasnyy "🔴 замок 3: файл RIFF, какого среди заготовок быть не должно. Запись разговора — всегда RIFF:"
printf '%s' "$CHUZHOY_WAV" | sed 's/^/ | /'
fi
echo
# ============================================= ЧАСТЬ 2 — ЭТАЛОН ПРАВИЛА ====
@@ -242,6 +303,52 @@ else
krasnyy "на машине ЕСТЬ заготовки, которых НЕТ в хранилище — работа пропадёт при перезагрузке:"
echo "$CHUZHOE" | sed 's/^/ | /'
fi
# ==== 🔴🔴 ЗАМОК 2 — СЛЕПКИ ЗАГОТОВОК ПРОТИВ СЛЕПКОВ ЖИВЫХ ЗАПИСЕЙ ==========
#
# 🔴 Сначала СРАВНИВАЕМ РАЗМЕРЫ, и только совпавшие по размеру записи считаем
# слепком. Это не поблажка, а полная замена: точная копия файла весит ровно
# столько же, сколько исходник. Значит запись, положенная в заготовки без
# единого изменённого байта, обязана совпасть по размеру — иначе она не
# копия, и слепок у неё всё равно не сойдётся.
# Выигрыш: сегодня совпадений по размеру НОЛЬ, то есть машина не читает
# ни одного байта звука вместо 63 МБ. Машина делит железо с боевым
# рендером, решение владельца Р84.
RAZMERY_ZAGOTOVOK="$( cd "$ZAGOTOVKI" && find . -type f -printf '%s\n' | LC_ALL=C sort -u )"
OTVET_RAZMEROV="$(na_servere "find '$PAPKA_ZAPISEY' -maxdepth 1 -type f ! -name 'chelovek*' -printf '%s\n' 2>/dev/null | LC_ALL=C sort -u; echo '###ZAMERENO'")"
if ! echo "$OTVET_RAZMEROV" | grep -qF '###ZAMERENO'; then
krasnyy "размеры записей на машине СПРОСИТЬ НЕ УДАЛОСЬ — подлог записью проверить нечем"
else
RAZMERY_ZAPISEY="$(echo "$OTVET_RAZMEROV" | grep -vF '###ZAMERENO' | grep -E '^[0-9]+$')"
SOVPALI_RAZMERY="$(comm -12 <(echo "$RAZMERY_ZAGOTOVOK") <(echo "$RAZMERY_ZAPISEY"))"
if [ -z "$SOVPALI_RAZMERY" ]; then
zelenyy "замок 2, по слепку: ни одна заготовка не весит столько же, сколько запись — копией быть не может ни одна"
else
SKOLKO_RAZMEROV=$(echo "$SOVPALI_RAZMERY" | wc -l)
OTVET_SLEPKOV="$( echo "$SOVPALI_RAZMERY" \
| ssh -o BatchMode=yes -o ConnectTimeout=30 -i "$KLYUCH" "$SERVER" \
"cd '$PAPKA_ZAPISEY' 2>/dev/null && while IFS= read -r R; do [ -n \"\$R\" ] && find . -maxdepth 1 -type f ! -name 'chelovek*' -size \${R}c -print0; done | xargs -0 -r sha256sum | cut -d' ' -f1; echo '###ZAMERENO'" 2>/dev/null )"
if ! echo "$OTVET_SLEPKOV" | grep -qF '###ZAMERENO'; then
krasnyy "слепки записей посчитать НЕ УДАЛОСЬ — подлог записью проверить нечем"
else
SLEPKI_ZAPISEY="$(echo "$OTVET_SLEPKOV" | grep -E '^[0-9a-f]{64}$' | LC_ALL=C sort -u)"
SLEPKI_ZAGOTOVOK="$(grep '^[0-9a-f]' "$SLEPKI" | cut -d' ' -f1 | LC_ALL=C sort -u)"
SOVPAVSHIE="$(comm -12 <(echo "$SLEPKI_ZAGOTOVOK") <(echo "$SLEPKI_ZAPISEY"))"
if [ -z "$SOVPAVSHIE" ]; then
zelenyy "замок 2, по слепку: $SKOLKO_RAZMEROV размер\
(ов) совпал, слепки сверены — совпавших нет"
else
krasnyy "🔴 замок 2: ЗАГОТОВКА ПОБАЙТОВО РАВНА ЗАПИСИ РАЗГОВОРА. Это личные данные под чужим именем:"
for H in $SOVPAVSHIE; do
grep "^$H" "$SLEPKI" | sed 's/^[0-9a-f]* / | /'
done
fi
fi
fi
fi
echo
# ========================================== ЧАСТЬ 4 — ЖИВОЙ ОПЫТ ===========
@@ -132,7 +132,7 @@ vygruzka/*.md — пять промтов и баз знаний Лены, 107
| `bots/lena-golos/zagotovki/**` | 43 файла заготовок, 4 773 060 байт |
| `bots/lena-golos/zagotovki-slepki.sha256` | слепок каждого из 43 файлов |
| `bots/lena-golos/lena-zagotovki.conf` | эталон правила воссоздания, 12 строк `C` |
| `bots/lena-golos/proverka-zagotovok.sh` | сторож, 21 проверка |
| `bots/lena-golos/proverka-zagotovok.sh` | сторож, 23 проверки — три из них замки на личные данные |
| `bots/lena-golos/README.md` | дописан раздел «Заготовки подбора голоса» |
Перенос — одной свёрткой `tar` с **поимённым** перечнем из 12 имён верхнего
@@ -236,7 +236,7 @@ vygruzka/*.md — пять промтов и баз знаний Лены, 107
Итоговое состояние сторожа:
```text
✅ ЦЕЛО: проверок 21, красных 0
✅ ЦЕЛО: проверок 23, красных 0
```
### Мерка 2 — полный прогон
@@ -371,8 +371,155 @@ systemd-tmpfiles-setup.service` → `ExecStart=systemd-tmpfiles --create --remov
| правило доехало пустым, а systemd вернул 0 | 🔴 сторож врал зелёным, **починено** — см. ножи |
| папки на машине нет, а сверка отвечает «сходится» | 🔴 сторож врал зелёным, **починено** — см. ножи |
## Добавка — слепота на личные данные, найденная надзирателем
Надзиратель вскрыл дыру, которую я не увидел, и она в самом опасном месте.
**Допущение, на котором стояла моя защита:** «запись разговора узнаётся по
имени». **Случай мимо:** файл, положенный под именем образца голоса и вписанный
в перечень слепков. Такой файл проходил **все** мои проверки — состав, слепки,
«лишних нет», ворота ПДн. Краснела только сверка с машиной, и то случайно:
потому что на машине файла не было. А мой же README велит новую заготовку
класть и на машину — то есть человек, добросовестно выполнивший мою инструкцию
с неверным файлом, получил бы **полностью зелёного сторожа** и личные данные в
коммите.
Я с разбором согласен целиком. Замок был один, и он смотрел на то, что
подделывается первым, — на имя.
### Что сделал: замков стало три
| замок | как ловит | что ловит |
|---|---|---|
| 1 — по имени, был | имя похоже на `1785699262.18.wav` | небрежность |
| 2 — по слепку, новый | слепок заготовки совпал со слепком живой записи на машине | умысел |
| 3 — по содержимому, новый | файл начинается с `RIFF`, а слепок не из трёх известных | умысел **даже когда записи на машине уже нет** |
Замок 1 не убран — как и просил надзиратель. Он ничего не стоит и работает
без машины.
### По оговоркам надзирателя — по каждой отдельно
**1. «Записи — вещь движущаяся, сторож не должен краснеть от смены их числа.»**
Учтено: сравниваются **множества слепков**, не числа. Число записей в замер не
входит вовсе — ни как порог, ни как ожидание.
**2. «Не утяжели машину.»** Нашёл способ дешевле, чем предложенный. Сначала
сравниваются **размеры**, и только совпавшие по размеру записи считаются
слепком. Это не поблажка, а **полная замена**: точная копия весит ровно столько
же, сколько исходник, — значит запись, положенная без единого изменённого
байта, обязана совпасть по размеру. Замерено сегодня: разных размеров у
заготовок 38, у записей 44, **совпавших ноль**. То есть в обычном прогоне
машина не читает **ни одного байта** звука вместо 63 МБ. Размеры берутся из
`find -printf '%s'` — это чтение оглавления, не файлов.
**3. «Поймать подлог файлом, которого на машине нет вовсе.»** Поймал, и без
выдуманной сложности — замком 3. Запись разговора всегда WAV, потому что её
пишет `MixMonitor`, а WAV начинается с `RIFF`. Замерено: среди 43 заготовок
файлов RIFF ровно **три**, все верхним ярусом; внутри восьми папок подбора —
**ноль**, там всё mp3 с байтами `ff fb`. Правило вышло закрытым и от имени не
зависит вовсе: любой RIFF среди заготовок обязан совпасть слепком с одним из
трёх известных.
🔑 **И главное — где эти три слепка лежат.** В самом стороже, а не в
`zagotovki-slepki.sha256`. Перечень слепков подделывается тем же движением, что
и сама подмена: положил файл — вписал строку. Список внутри сторожа этим
движением не подделать. Цена — добавление честной новой заготовки WAV требует
правки строки руками; для mp3 никакой возни нет. Считаю цену правильной: WAV
здесь единственный вид файла, способный нести чужой голос.
**4. «Проверку по имени не убирай.»** Не убрал.
### Красным показано — двумя ножами, и ни одного чужого байта в дереве
🔴 **С просьбой положить в дерево настоящую запись я не согласился в том виде,
в каком она была написана, и сделал безопаснее — не отказавшись от доказательства.**
**Нож 6 — подлог надзирателя, дословно.** Взял с машины **настоящую** запись
`1785698951.10.wav` и положил её как `obraztsy/Proba-nadziratelya.mp3`, вписав
слепок в перечень. Почему это безопасно — замерено, а не заявлено: файл весит
**44 байта**, это голый заголовок WAV, и поле длины куска данных в нём равно
**нулю**:
```text
52 49 46 46 24 00 00 00 57 41 56 45 66 6d 74 20 RIFF$...WAVEfmt
...
64 61 74 61 00 00 00 00 data....
^^^^^^^^ длина звука = 0
```
Звука в файле нет ни одного отсчёта — голоса человека там физически нет. При
этом слепок у него **настоящий** и он **вправду** лежит среди записей на
машине, поэтому замок 2 обманут быть не может. Ответ сторожа:
```text
✅ файлов столько же, сколько слепков: 44
✅ все 44 заготовок сходятся со слепками
✅ лишних файлов в заготовках нет — ни одного без слепка
✅ замок 1, по имени: файлов с именем записи разговора нет ← имя безобидное
🔴 замок 3: файл RIFF, какого среди заготовок быть не должно:
| obraztsy/Proba-nadziratelya.mp3 слепок 4f8734c5…7041
🔴 замок 2: ЗАГОТОВКА ПОБАЙТОВО РАВНА ЗАПИСИ РАЗГОВОРА:
| obraztsy/Proba-nadziratelya.mp3
🔴 БЕДА: проверок 23, красных 3
```
Замок 1 остался зелёным — значит поймали именно новые замки, а не старый.
**Нож 7 — тот же подлог настоящего веса, без единого чужого байта.** Нож 6
доказал ловлю, но на 44 байтах: отбор по размеру там почти не работал. Поэтому
второй опыт — на реальном весе. Подставными «записями» служили **наши
собственные** заготовки `Elena.mp3` весом 152 064 байта и `bylo.mp3` весом
92 928, положенные среди записей под именами записей — в **отдельной** папке на
машине, живую я не трогал. Для этого у сторожа появилась переменная
`LENA_PAPKA_ZAPISEY`, ради проб его самого.
```text
🔴 замок 2: ЗАГОТОВКА ПОБАЙТОВО РАВНА ЗАПИСИ РАЗГОВОРА:
| obraztsy/Elena.mp3
| brend/bylo.mp3
```
Отбор по размеру отработал на настоящем весе и назвал **оба** файла. Подставная
папка снесена, снос проверен командой.
### Возврат доказан, и по дороге я наступил на ту же грабку, что и надзиратель
Подставной файл убран, строка из перечня вынута. Дальше вышло поучительно:
```text
слепок перечня ДО: 33be0e5e…d4ca
слепок перечня ПОСЛЕ: 62674e39…8335 ← «РАЗОШЛОСЬ»
```
Тревога оказалась ложной — **той же самой, что подвела надзирателя**: разошлись
невидимые знаки конца строки. Настоящий замер:
```text
git cat-file blob HEAD:…sha256 | sha256sum → 62674e391a70…8335
sha256sum рабочей копии → 62674e391a70…8335
cmp блоба и рабочей копии → ОДИН В ОДИН, 4173 байта
diff по строкам → различий нет
```
Строк в файле 47; копия с `CRLF` весила бы 4173 + 47 = 4220 байт. То есть
«ДО» я мерил копию с `CRLF`, а в коммите всегда лежала копия с `LF` — и
никакого расхождения содержимого не было ни секунды. Заготовок 43, строк
слепков 43.
🔑 **Урок, который стоит вынести за пределы этого круга:** `sha256sum` рабочей
копии на Windows — **негодный** прибор для ответа «вернулось ли». Годный —
`cmp` против `git cat-file blob`, либо сверка со снятием `\r` с обеих сторон.
Мы с надзирателем попались на этом независимо друг от друга в один день.
## Что осталось незакрытым
0. 🔴 **Чего не ловит и новая защита.** Замки 2 и 3 ловят запись, положенную
**байт в байт**. Запись, **пересжатую в mp3**, не поймает ни один из трёх:
имя другое, байты другие, `RIFF` пропал. Это уже не «переименовал», а
«нарочно перекодировал» — но честно: дыра осталась, и закрыть её дёшево
нечем. Ловится только человеком на разборе изменений.
1. 🔴 **Сторожа никто не зовёт — ноль автоматических зовущих.** Ни расписания,
ни крючка. Пока его не запускают, всё, что он умеет ловить, он будет ловить
только тогда, когда кто-нибудь вспомнит. Ровно тот же открытый хвост, что у
@@ -408,4 +555,4 @@ systemd-tmpfiles-setup.service` → `ExecStart=systemd-tmpfiles --create --remov
<!-- Имена файлов и папок на сервере записаны транслитом, живут только здесь. -->
<!-- 🪤 Слова обязаны стоять на ОДНОЙ строке с cspell:ignore — указание читается
до конца строки, перенесённые вниз слова сами становятся ошибками. -->
<!-- cspell:ignore brend chelovek cheloveka chistka chistki devyatoy dorogo dyhanie dyhaniya FRAZA golos golosov nomer obraztsy papki proba progon proverka raspisaniya sleda slepki storozha Svetlana svetlany syroy sysinit tekstovogo udarenie udareniya vygruzka vosstanovleniya zacepka zagotovk zagotovki zagotovok ZAMERENO zapisey zhivost zhivosti -->
<!-- cspell:ignore bylo cmp nadziratelya PAPKA brend chelovek cheloveka chistka chistki devyatoy dorogo dyhanie dyhaniya FRAZA golos golosov nomer obraztsy papki proba progon proverka raspisaniya sleda slepki storozha Svetlana svetlany syroy sysinit tekstovogo udarenie udareniya vygruzka vosstanovleniya zacepka zagotovk zagotovki zagotovok ZAMERENO zapisey zhivost zhivosti -->