Kontinuum Node — Operations
Server-side storage, replication, lifecycle, admin surfaces, threat model, DR plan.
Audience: node operators, DevOps, SRE, security review.
Связанные документы:
architecture.md— overview / glossary / tier model — start hereprotocols.md— DHT / mailbox / inter-node wire protocolsapp-integration.md— client-side flows that interact с этими operations (PRO lapse, pairing, etc.)pricing.md— billing model affecting cert lifecycle и replication
Section codes (§7, §9, §12, §15.1, §16) preserved from единой v0.6 спеки. §7.3 family-mode UX, §9.5 user pins UX, §15.2 app-side control — в app-integration.md.
7. Слой хранения
7.1 S3-gateway через embedded rustfs
S3-compatible endpoint реализован на embedded rustfs.
Client (kontinuum-app::clouds::S3) ─┐
▼
┌─────────────────────────┐
│ Auth shim (Ed25519) │
└────────────┬────────────┘
▼
┌─────────────────────────┐
│ rustfs (presigned URLs) │
└────────────┬────────────┘
▼
┌─────────────────────────┐
│ FS backend + quota mgmt │
└─────────────────────────┘Модель аутентификации:
- AWS SigV4 не используется.
- Client:
POST /presign { operation, key, signature_over_request }с Ed25519 подписью. - Shim проверяет подпись, верифицирует permission, генерит 60-секундный rustfs presigned URL.
- Client использует URL для GET/PUT/DELETE напрямую к rustfs.
Раскладка namespace:
- Bucket:
cnt-${identity_id_short}(per identity). - Key:
${space_id}/${blake3_hash}. - Иммутабельные blob'ы (CAS by hash).
Fallback strategy: если rustfs окажется недостаточно стабильным — самописный handler ~1000 строк Rust. Решение откладывается до этапа прототипа.
7.2 Relay-node как coordination + free S3
Купленный relay-node содержит:
- DHT shard для Spaces владельца (~2-3 GB)
- Mailbox владельца (TTL'd queue)
- Cache transit blob'ов (resume uploads, thumbnails, streaming, ~2-3 GB)
- System reserved (~2 GB)
- Остаток свободного места — бесплатный S3 для владельца (входит в стоимость relay-node)
Пример распределения 20GB VPS:
2 GB system reserved
3 GB DHT records
3 GB transit cache
12 GB free S3 (бесплатно для владельца, в Virtual S3 Pool)S3-bucket покупается сверх этого бесплатного только если 12GB мало или хочется geo-divergent копия в другом провайдере.
Capabilities на relay-node: dht_routing (mandatory), relay, mailbox, s3_gateway, anti_entropy, rendezvous — все default ON для tenant'а (владельца + опционально whitelisted family/team).
7.3 Family / team mode (multi-tenancy на byo-ноде)
PRO-пользователь может расшарить свою ноду (VPS или home server) с whitelisted identities как byo:* × multi.
Правила:
- Оплачивает только владелец ноды. Tenants ничего не платят, не имеют billing-отношений с org.
- Whitelist по identity_id — owner добавляет/удаляет вручную в PRO admin UI.
- Per-tenant квота на storage и mailbox — задаёт owner.
- Tenant имеет полный S3 + DHT + mailbox на этой ноде в пределах квоты, как будто это его собственная single-tenant нода.
- Trust model: tenant trust'ит owner'а не злоупотреблять. E2E защищает от чтения данных, но не от DoS / capricious shutdown. Это family/team use case, не публичный hosting.
- Revenue share отсутствует. Это «family plan», не reseller marketplace.
7.4 Virtual S3 Pool
App видит один логический pool — сумма всех S3-источников владельца, агрегированных в единое пространство.
Источники:
| Bucket type | Описание | Cost | CDN |
|---|---|---|---|
LocalRelayFree | Free S3 на собственных relay-нодах (§7.2) | бесплатно | — |
OrgPaid | S3-quota купленная у org | GB-month + egress | optional |
OrgPaidWithCDN | S3-quota у org + CDN-tier | дороже GB + дешевле egress | да |
ExternalPRO | External bucket (Backblaze / R2 / AWS), прикручен в PRO | прямой billing провайдеру | — |
ExternalPROwithCDN | External provider с CDN-front (Cloudflare R2, Bunny.net) | прямой billing | да |
UX:
- Space привязывается к pool с лимитом
max_bytes, не к конкретному bucket'у. - User не выбирает bucket вручную; placement-algorithm работает за него.
Placement-algorithm:
- Priority 1:
LocalRelayFree(бесплатно) — пока есть место. - Priority 2:
OrgPaid— если geo-zone подходит и есть квота. - Priority 3:
ExternalPRO— если PRO с прикрученными external. - Внутри priority — geo-aware: предпочтение bucket'ам в нужной geo-zone.
- Если у blob'a высокий read-rate (thumbnails, streaming) → предпочтение
*WithCDN.
Per-blob mapping blob_hash → physical_bucket хранится в Space DHT.
7.5 Матрица External vs Internal storage
| Пользователь | Primary storage | Coordination/cache |
|---|---|---|
| Free-tier | Только локально на собственных устройствах | — |
| Standard + relay | Локально + Free S3 на relay-ноде | Собственная relay-node |
| Standard + S3-quota | Org-managed S3 в Virtual Pool | Собственная relay-node (если куплена) |
| PRO (subscription) | Собственный external S3 в Virtual Pool | Собственная(ые) attached VPS / home server |
| PRO + family-mode | + расшарено с whitelisted identities | + multi-tenant квоты |
9. Репликация и geo-политика
9.1 Durability tiers по типу storage
Разные источники в Virtual S3 Pool дают разный уровень durability. Это явный design choice — free-tier не равен paid-tier по гарантиям.
| Storage source | Durability guarantee | Защита от чего |
|---|---|---|
LocalRelayFree | Best-effort (single-node rustfs, нет RF) | Только software corruption; SSD-fail = loss |
OrgPaid / OrgPaidWithCDN | 11-девяток (cloud-провайдер встроенно) | Disk-fail, регион-outage в пределах провайдера |
ExternalPRO* | 11-девяток (cloud-провайдер встроенно) | Disk-fail, регион-outage в пределах провайдера |
| Cross-provider replica | + защита от outage конкретного провайдера | Whole-provider blackout |
| Cross-region replica | + защита от регионального outage | Regional disaster |
| Friend-replication (§9.3) | Bonus copy в чужом Tier 1/2 | Survives non-payment владельца |
Critical: LocalRelayFree приоритезируется placement-algorithm (§7.4) как cheap-tier для frequently-accessed данных, но не подходит для durability-critical content.
Рекомендация в UX: для важного контента user должен pin (§9.5) — это форсирует копию в OrgPaid / ExternalPRO (11 девяток) в дополнение к LocalRelayFree.
replication_factor в нашем смысле = «в скольких разных провайдерах / регионах хранится копия», а не «сколько физических блоков».
9.2 Размещение копий
Для Space:
- Tier 1/2 нода владельца = mandatory replica #1 (через LocalRelayFree).
- Дополнительные копии (по желанию владельца):
- В другом провайдере той же geo-zone — защита от провайдер-failure.
- В другом geo-zone — защита от regional outage / DR.
- Размещение управляется placement-algorithm Virtual Pool (§7.4).
9.3 Friend-replication бонус
Когда identity участвует в Shared Space — её Tier 1/2 ноды автоматически становятся secondary replicas для blob'ов этого Space.
- ✅ улучшает durability бесплатно (за счёт чужой оплаченной инфры)
- ✅ обеспечивает выживание контента при non-payment владельца (см. §12)
- ✅ снижает latency для участников из других регионов
9.4 Geo-zone
Geo-zone — строка из enum (eu-west, eu-east, us-east, us-west, ap-east, ap-south, ...). Атрибут не ноды, а двух разных уровней — потому что один account у провайдера может иметь bucket'ы в разных регионах:
host_geo_zone— где физически работает compute (VPS / home server). Атрибутnode:{node_id}записи.storage_geo_zone— где физически хранятся blob'ы. Атрибут per-bucket в Virtual S3 Pool.
Для single-region provider'ов (Hetzner отдельная локация, home server) host_geo_zone == storage_geo_zone. Для multi-region provider'ов (Cloudflare R2 / Backblaze / AWS S3) одна нода может иметь bucket'ы в разных регионах:
| Provider | Регионы | Тип |
|---|---|---|
| AWS S3 | us-east-1, us-west-2, eu-west-1, ap-south-1, ... | Storage |
| Backblaze B2 | us-west, eu-central | Storage |
| Cloudflare R2 | global (CDN edges) | Storage |
| Hetzner | de-fsn1, de-nbg1, fi-hel1 | Compute |
| OVH | eu-fra1, eu-rbx1, ca-bhs1 | Compute |
| DigitalOcean | nyc1, ams3, sgp1, ... | Compute |
Placement-algorithm учитывает per-bucket zone (не per-node) при cross-region replica decisions.
Более тонкая стратегия (latency-aware, regulatory-aware) — open question, defer.
9.6 Blob pin record
struct BlobPin {
blob_hash: BlobHash,
pinned_by: Vec<IdentityId>, // кто pin'ит — может быть несколько
providers: Vec<ProviderEntry>, // кто реально хранит сейчас
paid_until: u64,
}
struct ProviderEntry {
node_id: NodeId,
region: GeoZone,
provider_kind: ProviderKind, // LocalRelayFree | OrgPaid | ExternalPRO | …
stored_at: u64,
verified_until: u64, // истекает при отсутствии PoS-confirm
signature: Vec<u8>,
}- Когда
pinned_byпуст и нет других reasons держать → eligible для GC. actual_providers = providers.iter().filter(|p| now < p.verified_until).count().- При недостаче providers'ов для pin'нутого blob'а — запускается repair через anti-entropy (§11d).
12. Жизненный цикл cert'a и таймлайн при неоплате
12.1 Этапы
| Период от истечения cert | Режим | Что нода делает | Кто платит за ресурс |
|---|---|---|---|
| Day 0 → 7 | warning | full service, daily reminder | владелец (текущая подписка) |
| Day 7 → 14 | read-only freeze | reads OK, writes reject, replication-in OK | оператор (org / byo) |
| Day 14 → 21 | cold archive | reads медленные, paid restore offer | оператор |
| Day 21 → 28 | tombstone | reads = 410 Gone, restore возможен | оператор |
| Day 28+ | hard delete | физическое удаление | — |
Reactivation: в любой момент до day 28 — достаточно возобновить обычную оплату.
12.2 Кто платит за extra-период
org × *→ kontinuum.org поглощает.byo:* × *→ пользователь продолжает оплачивать VPS своему провайдеру (или сам держит home server); org даёт только cert + lifecycle.
12.3 Распространение в сети
- Day 7: Tier 0 публикует
CertRevocationв global DHT. - Peer'ы видят за 1–2 минуты, помечают ноду как read-only.
- Inter-node операции
ReplicaPush/DhtPutк этой ноде отвергаются. DhtGet/ReplicaPullпродолжают работать до этапа tombstone.
12.4 Cross-replication safety net
Контент владельца, размещённый в Shared Spaces, переживает full timeline, потому что копии лежат на Tier 1/2 нодах участников. UX-сообщение: «после tombstone ваши shared-space данные останутся доступными у друзей; personal данные исчезнут».
15. Поверхности управления
15.1 Org-side: используем готовые инструменты
Никакого custom Vue3 frontend'а. Стек:
- Prometheus + Grafana — metrics dashboards: per-node health, DHT lookup latency, replication factor distribution, mailbox queue depths, challenge pass/fail rates, geo-zone distribution.
- Directus — headless admin panel поверх SQL ноды (CRUD по identity, cert, quota, partner-info, billing-events). Заменяет «admin UI» для day-to-day ops.
- Operator chat bot — platform-agnostic интерфейс для quick ops (drain mode, force-rebalance, kick offender, ad-hoc queries). Реализуется через
operator_bot::BotBackendtrait в admin crate; конкретный backend (Kontinuum native / Telegram / Matrix / Signal / Discord) подключается на этапе deployment. - REST API (
kontinuum-node-admincrate) — для billing webhooks, Tier 0 cert issuance, automation.
16. Модель угроз
| Угроза | Митигация | ||
|---|---|---|---|
| Sybil в global DHT | Tier 0 anchor + Tier 1 cert-based admission; Tier 2 PoS-challenges; anti-Eclipse: peer-selection обязан включать ≥1 Tier 0/1 ноду | ||
| Eclipse конкретной identity | identity-stub в global DHT помещается у k=20 широко рассеянных нод; lookups через ≥3 disjoint paths | ||
| MITM identity lookup | Все DHT-записи подписаны owner; client верифицирует подпись | ||
| Node compromise | E2E-шифрование данных at rest и in transit; нода ничего не расшифровывает | ||
| Tier 0 key compromise | v0.1: 1-of-1 + offline backup для emergency recovery. v1.0: 3-of-5 multi-sig для cert issuance / CRL update; 2-of-3 для routine config. Ключи geographically distributed (US, EU, AP) | ||
| byo-нода malicious tenant abuse | Whitelist by identity_id; per-tenant квоты; revoke в любой момент через PRO admin UI | ||
| byo-нода malicious owner | Tenant trust'ит owner — это family/team use case; E2E защищает от чтения; tenant может в любой момент мигрировать на другую инфру (Personal Space portable) | ||
| Censorship / takedown | E2E + cross-provider / cross-region replication; Tier 3-style relay в дружественных регионах | ||
| DDoS на ноду | Технические rate limits (token bucket); rejects при перегрузке; partner-level CDN опционально | ||
| Cert revocation race | CRL distribution через gossip; nodes ttl-кешируют cert validity максимум 60 секунд | ||
| Vault backup leak | Backup encrypted with master_key (Argon2id from BIP39); нода видит только ciphertext | ||
| Metadata-leak через redirect target | Default presigned URLs (требуют app-refresh); opt-in public-readable с UX-warning; obscure bucket naming через blake3(identity_id + nonce); disable LIST permission; minimal HEAD responses | ||
| Tenant data exposure через host-admin | Tenant E2E-encrypts с собственным space_key, который admin-владелец host's ноды не имеет; UX-warning при join family-mode (см. §7.3); рекомендация купить собственную relay-node для критичных данных |
16.1 DR / chaos planning
Disaster Recovery plan для всей сети при потере целого geo-zone / провайдера.
Recovery primitives (что у нас есть в архитектуре):
| Primitive | Где определено |
|---|---|
| Cross-region replication | §9.2 — RF копий в разных geo-zones |
| Friend-replication safety net | §9.3 — копии у friend's нод |
| DHT k=20 by xor-distance | §5.1.1 — auto-distributed geographically |
| Tier 0 anchors в разных регионах | §3.1 — recommended US, EU, AP |
| External direct URL fallback | §13.6 — bypass dead byo-нод |
| Friend-replica takeover | §20.6 (round 3) — primary serving при offline node |
Тестирование:
| Подход | Когда | Что даёт |
|---|---|---|
| Game days (manual exercise) | Раз в квартал | Команда тренируется coordinated failover; playbook updates |
| Synthetic chaos в staging | Continuous (low frequency) | Verify automated recovery без impact production |
| Hard takedown tests | Раз в полгода, scheduled | Координированный «turn off all EU», measure MTTR |
| Continuous chaos в prod (Netflix-style) | После v1.0 — daily injection | Catch real production issues; требует mature observability |
SLO метрики:
| Метрика | Target |
|---|---|
| MTTR для global DHT lookups при single-zone failure | < 60 sec |
| Data loss rate для billed content (durability) | 0% |
| User-visible downtime при failure одного провайдера | < 5 min |
| Full provider migration time (e.g., Hetzner→OVH) | < 24h |
Playbook для типичных incidents:
- Один Tier 0 anchor offline → остальные продолжают, при multi-sig 3-of-5 ничего не блокирует (см. A1).
- Один geo-zone полностью offline → friend-replication + cross-region replicas обслуживают reads; writes failover в healthy zones.
- Один провайдер полностью offline (e.g., Hetzner outage) → automated detection через
NodeStatus(§11h); user notification; manual emergency migration инструкции через admin. - Tier 0 root key compromised → emergency revocation через offline backup ключ; coordinated network-wide rotation Tier 0 pubkey list в новой client version.
Rollout plan для v1.0:
- Game days quarterly с момента launch.
- Synthetic chaos в staging — после первых 100 nodes.
- Continuous chaos в prod — после v1.0 stable run (≥6 месяцев).