feat: complete Epic 1 production foundation

This commit is contained in:
2026-08-25 01:24:11 +03:00
parent 767428436d
commit 182bde8ac0
298 changed files with 35719 additions and 5299 deletions
+88 -3
View File
@@ -16,12 +16,17 @@ docker compose -f deploy/community/docker-compose.yml --env-file deploy/communit
2. Создайте и проверьте согласованный backup PostgreSQL и artifact storage. Для первой пустой установки зафиксируйте, что восстанавливать нечего.
3. Проверьте immutable plan: `cargo run -p admin-api --bin crank-migrate -- plan --check` в source checkout либо `<compose> run --rm migrate crank-migrate plan` для образа.
4. Примените sequence: `<compose> run --rm migrate crank-migrate apply`.
5. Повторите preflight и убедитесь в `{"status":"current","version":3}`.
5. Повторите preflight и убедитесь в `{"status":"current","version":11}`.
6. Только теперь запускайте long-running services: `<compose> up -d`.
Обычный `up` также содержит обязательный migration job, но при upgrade он не заменяет предварительные preflight и backup. Migrator делает до десяти bounded попыток подключения с секундной паузой и затем безопасно завершается ошибкой.
Команда читает только `CRANK_DATABASE_URL`/`POSTGRES_*`. Master key, session secret, bootstrap password, MCP credentials и другие service secrets не входят в её config projection.
Команда `plan|preflight|apply` читает только `CRANK_DATABASE_URL`/`POSTGRES_*`.
Master key, session secret, MCP credentials и другие service secrets не входят
в её migration config projection. Подкоманды `admin-auth bootstrap-create` и
`admin-auth recover` также используют только database config. Bootstrap
выводит одноразовый token; recovery читает новый пароль, password pepper и
current master key только из локальных файлов.
## Фактический brownfield inventory
@@ -35,6 +40,26 @@ docker compose -f deploy/community/docker-compose.yml --env-file deploy/communit
Legacy extension row без зарегистрированного exact `(name, version, checksum)` несовместим. Community пока не публиковала extension migrations, поэтому authority не выдумывает им checksum и блокирует такие строки с `legacy_conflict`.
Version 4 (`operation-lifecycle-v4`) добавляет version-local Operation identity/provenance и DB immutability guard. Legacy rows получают `legacy_observed` с cutover timestamp; неизвестные historical actor/publication timestamps не фабрикуются. Published payload и parent cascade delete блокируются на уровне PostgreSQL.
Version 5 (`execution-outcome-v5`) добавляет nullable exact Operation version,
execution stage, stable error code, retryability и outcome certainty в Invocation
History. Legacy v4 строки остаются `NULL`; backfill не фабрикует классификацию.
Version 7 (`master-key-identity-v7`) добавляет durable master-key identity,
rotation ledger и epoch-aware Secret ciphertext metadata. Это expand-изменение:
legacy `secret_versions` получают default epoch `1`, а target ciphertext поля
остаются `NULL` до operator-controlled rotation.
Version 8 (`admin-auth-lifecycle-v8`) добавляет local bootstrap contracts,
CSRF hash для browser sessions, login backoff ledger и bounded admin security
audit. Static startup password больше не является production bootstrap
authority: первый администратор создаётся через `crank-migrate admin-auth
bootstrap-create` и одноразовый token в UI. Потерянный Admin password
восстанавливается локально через `crank-migrate admin-auth recover` с проверкой
active master-key identity; команда не раскрывает старый пароль, Secrets или
key material и отзывает browser sessions.
## Контракт sequence
Machine plan находится в [`schemas/migration-sequence.json`](schemas/migration-sequence.json) и проверяется командой:
@@ -46,6 +71,20 @@ cargo run -p admin-api --bin crank-migrate -- plan --check
- V1 — immutable brownfield baseline с историческим ledger token и отдельным exact-source SHA-256.
- V2 — единый exact-byte expand SQL artifact, создающий canonical ledgers и MCP session schema; его SHA-256 закреплён в executable descriptor.
- V3 — append-only expand для независимого nullable `invocation_logs.trace_id`, canonical-format constraint и partial request/trace indexes; исторические строки остаются `NULL` без fabricated backfill.
- V4 — immutable Operation lifecycle и honest legacy snapshot provenance.
- V5 — nullable typed execution outcome для N/N-1 чтения истории.
- V7 — master-key identity/rotation foundation: non-secret active epoch,
durable rotation state/checkpoint и target ciphertext metadata.
- V8 — admin auth lifecycle foundation: one-time bootstrap contract, CSRF <!-- community-scope: allow=one-time-token -->
session verifier, login backoff и bounded auth audit.
- V9 — immutable Agent catalog lifecycle: catalog revision и DB guards для
published Agent snapshots/bindings.
- V10 — approval side-effect safety: workspace-scoped pending approval fingerprint
index для full-scope deduplication.
- V11 — onboarding ProductEvents и exact credential provenance: nullable scoped
`invocation_logs.platform_api_key_id`, immutable local `product_events`, daily
denominator rollups и partial success index. Legacy history не backfill-ится;
external/unscoped credentials не получают fabricated key identity.
- Каждая версия имеет contiguous `i64` version, стабильное имя, lowercase SHA-256, owner, phase, explicit readable schema min/max и backfill policy.
- `migrate` требует bounded cursor/batch policy; `contract` дополнительно требует tracked compatibility evidence и закрытого окна.
- Добавление descriptor без executable implementation блокируется `invalid_contract` до DB I/O.
@@ -68,9 +107,55 @@ CLI/stderr возвращают bounded JSON: `code`, `stage`, nullable numeric
| `apply_failed` | Transaction migration откатилась | Проверить matching artifact/backup, затем повторить preflight |
| `storage_unavailable` | PostgreSQL/transport недоступен | Проверить сеть/TLS/права; секреты в diagnostic не копировать |
| `config_invalid` | Database-only config невалиден | Исправить указанный config contract |
| `invalid_command` | Неизвестная CLI команда/аргумент | Использовать только `plan`, `preflight`, `apply` |
| `invalid_command` | Неизвестная CLI команда/аргумент | Использовать только `plan`, `preflight`, `apply`, `admin-auth bootstrap-create|recover` или `master-key status|preflight|rotate|verify|promote|abort` |
| `contract_drift` | Committed machine plan расходится с Rust authority | Перегенерировать только для новой append-only version и проверить diff |
| `invalid_contract` | Descriptor/implementation/window/evidence несовместимы | Исправить authoring contract до любого DB I/O |
| `admin_recovery_rejected` | Recovery request не прошёл local/database checks | Проверить email, secret files и active master-key identity; не создавать второго Admin |
| `master_key_identity_mismatch` | Local master key не совпадает с active PostgreSQL identity | Использовать правильный current/target key; не менять ciphertext вручную |
| `master_key_rotation_in_progress` | Есть active rotation | Выполнить resume/verify/promote или abort |
| `master_key_rotation_verification_failed` | Target ciphertext не прошёл проверку | Повторить rotate/verify или abort до promotion |
## Admin auth operator commands
Первичная и recovery-инициализация Admin identity выполняется только локальным
operator command. Значения secret material не передаются через argv:
```bash
crank-migrate admin-auth bootstrap-create --email owner@example.local
crank-migrate admin-auth recover \
--email owner@example.local \
--password-file /secure/new-admin-password.txt \
--password-pepper-file /secure/password-pepper.txt \
--master-key-file /secure/current-master.key
```
`recover` проверяет active master-key identity в PostgreSQL, заменяет verifier
существующего Admin account, отзывает все browser sessions/CSRF state и пишет
bounded audit event. Команда не выводит password, pepper, master key, Secret
plaintext или ciphertext.
## Master-key rotation operator commands
`crank-migrate master-key` использует тот же database-only config, что и
миграции. Raw key material передаётся только через локальные файлы:
```bash
crank-migrate master-key status
crank-migrate master-key preflight --current-key-file /secure/current.key --target-key-file /secure/target.key --backup-ref offline-backup-ref
crank-migrate master-key rotate --current-key-file /secure/current.key --target-key-file /secure/target.key --backup-ref offline-backup-ref
crank-migrate master-key verify --target-key-file /secure/target.key
crank-migrate master-key promote --target-key-file /secure/target.key
crank-migrate master-key abort --rotation-id master-key-e1-to-e2
```
`preflight` не меняет active epoch, ciphertext или rotation ledger. `rotate`
можно повторять после interruption; команда пропускает уже staged rows и
обновляет checkpoint/counts. `verify` расшифровывает staged target ciphertext
target key. `promote` атомарно retired old active identity, registers target
identity as active и переносит target ciphertext в основной ciphertext. До
promotion текущий key остаётся рабочим; после promotion процессы нужно
перезапустить с target key.
## Правила разработчика