블라인드 릴레이 악용 방지
AeroNyx 노드가 암호문을 읽지 않고 재전송, 루프, 과도한 요청, 장애 peer를 억제하며 개인정보 보호 운영 증거를 제공하는 방식입니다.
Blind Relay Abuse Guard는 AeroNyx 분산 암호화 전달의 안전 경계입니다. 암호문을 분석하거나 영구 경로 기록을 만들거나 노드 운영자를 트래픽 관찰자로 만들지 않으면서 악의적이거나 불안정한 릴레이 동작을 제한합니다.
상태와 범위
가드는 Rust에 구현되어 있고 집계 결과는 node health metadata와 Nodeboard에 표시됩니다. 목적은 억제와 정직한 증거입니다. 불투명 relay work가 accepted, protected, degraded, stale인지 보여 주되 내용은 보여 주지 않습니다.
| 제어 | 상태 |
|---|---|
| Blind payload forwarding | 구현됨 |
| Signed freshness and replay suppression | 구현됨 |
| Previous-hop rate limiting and quarantine | 구현됨 |
| Privacy-safe runtime evidence | 구현됨 |
| Nodeboard operator visibility | 구현됨 |
| Plaintext or payload inspection | 금지 |
| User, route, or social-graph analytics | 금지 |
블라인드 노드 불변 조건
Relay는 routing metadata 서명을 검증하고 제한된 정책을 적용하며 opaque envelope를 전달하고 terminal signed receipt를 반환할 수 있습니다. 그러나 payload를 검사·추론하거나 경로, 사용자, 사회 관계를 복원할 metadata를 노출해서는 안 됩니다.
- message plaintext, packet payload, media content, MemChain plaintext
- DNS content, destination, domain, URL, browsing history
- route ID, complete path, endpoint URL, client public IP
- full public key, receiver identity, message ID, social-graph edge
- private key, voucher secret, wallet-level traffic, decryption material
승인 처리 흐름
각 request는 forwarding capacity를 쓰기 전에 제한된 검사를 통과합니다. 아래 순서는 개념적이며 모든 판단은 signed routing metadata와 local aggregate state만 사용하고 복호화 내용은 사용하지 않습니다.
verify signed previous_hop and envelope
apply in-flight backpressure
check timestamp freshness
check route replay cache
apply previous-hop rate/quarantine decision
validate TTL, loop safety, next-hop descriptor, and endpoint
forward opaque ciphertext or terminate into pending store
재전송 및 시각 보호
Replay suppression은 local, 단기, 용량 제한 방식입니다. Signed envelope timestamp로 오래되었거나 지나치게 미래인 frame을 거부합니다. 표의 값은 현재 main runtime default이며 영구 protocol promise가 아닙니다. 변경 시 test와 docs를 함께 갱신합니다.
| 런타임 상수 | 현재 기본값 |
|---|---|
MAX_BLIND_RELAY_SEEN_ROUTES | 8192 route IDs |
BLIND_RELAY_ROUTE_REPLAY_WINDOW_SECS | 600 seconds |
BLIND_RELAY_PREVIOUS_HOP_RATE_LIMIT | 120 requests / 60 seconds |
BLIND_RELAY_PREVIOUS_HOP_FAILURE_THRESHOLD | 12 scored failures / 300 seconds |
BLIND_RELAY_PREVIOUS_HOP_QUARANTINE_SECS | 300 seconds |
MAX_BLIND_RELAY_PREVIOUS_HOP_BUCKETS | 4096 buckets |
BLIND_RELAY_MAX_ENVELOPE_AGE_SECS | 600 seconds |
BLIND_RELAY_MAX_FUTURE_SKEW_SECS | 120 seconds |
BLIND_RELAY_DELIVERY_RECEIPT_MAX_AGE_SECS | 120 seconds |
MAX_BLIND_RELAY_FORWARD_ATTEMPTS | 3 attempts |
이전 홉 속도 제한과 격리
Rate limit는 signed previous-hop identity별로 적용됩니다. 60초에 120 request를 넘으면 5분 local quarantine이 시작됩니다. 별도 failure score는 5분 안에 공격적 validation failure 12회가 쌓이면 같은 격리를 시작합니다.
invalid_previous_hop | invalid_signature | self_loop | route_loop | ttl_exhausted
적대적 validation reason만 failure score에 포함됩니다. Transport timeout, ACK loss, duplicate route retry는 정상 previous hop을 오염시키지 않습니다. Bucket store는 제한되고 idle state는 만료되어 영구 communication graph가 되지 않습니다.
멱등성과 재시도 의미
Replay window의 중복 route ID에는 idempotent success를 반환합니다. Aggregate replay drop은 기록하지만 envelope를 다시 전달하거나 forward하지 않습니다. 일시적 next-hop failure는 bounded jitter로 최대 3회 시도하고 영구 validation failure는 retry하지 않습니다.
duplicate route_id -> accepted=true, reason=duplicate_route, no second delivery
transient next-hop failure -> bounded retry with deterministic jitter
permanent validation failure -> no retry
집계 런타임 카운터
Rust는 아래 두 path에 coarse cumulative counter와 freshness timestamp를 제공합니다. 이는 node-scoped operational evidence이지 message log, user billing analytics, 특정 대화의 증거가 아닙니다.
system_stats.discovery_status.peer_store.runtime.blind_relay
system_stats.discovery_status.peer_store.peer_health_summary
| 카운터 그룹 | 필드 |
|---|---|
| 수신과 처리 | received, terminal, forwarded, rejected |
| 검증과 보호 | invalid_signature, envelope_too_large, ttl_exhausted, no_route, invalid_endpoint, loop_detected, replay_dropped, timestamp_rejected, rate_limited, quarantined, quarantine_started |
| 전송과 재시도 | backpressure_dropped, forward_failed, retry_attempted, retry_succeeded, retry_exhausted |
| 합성 증거 | probe_attempted, probe_succeeded, probe_failed, two_hop_probe_attempted, two_hop_probe_succeeded, two_hop_probe_failed |
| 실제 전달과 최신성 | verified_client_onion_deliveries, last_verified_client_onion_delivery_at, last_accepted_at, last_event_at |
증거 품질 의미
Quality summary는 accepted opaque work, synthetic reachability probe, synthetic two-hop control proof, terminal-signed client delivery receipt를 구분합니다. real_relay_ready는 expected terminal의 fresh authenticated client-originated receipt에만 사용하며 synthetic evidence를 App/user traffic으로 표시하면 안 됩니다.
status | 의미 |
|---|---|
idle | Relay 또는 probe evidence가 아직 없습니다. |
observing | Evidence는 있지만 readiness가 확립되지 않았습니다. |
stale | 이전 성공 evidence가 더 이상 fresh하지 않습니다. |
ready | Fresh accepted work 또는 qualifying proof가 있고 active transport attention은 없습니다. |
protecting | Abuse protection counter가 작동 중이지만 relay는 운영됩니다. |
degraded | Forwarding 또는 probe failure를 조사해야 합니다. |
attention | Backpressure 또는 retry exhaustion에 즉시 대응해야 합니다. |
proof_scope는 client_message_delivery, relay_acceptance, message_delivery, control_plane, single_hop_control_plane, attempted, none을 구분합니다. Historical total은 누적되지만 readiness는 fresh evidence와 active transport failure를 반영합니다.
개인정보 보호 peer 상태
peer_health_summary는 축약 node identifier와 coarse health bucket으로 traffic relationship을 노출하지 않고 failing/quarantined peer를 격리하게 합니다. 명시적 privacy boundary가 있는 control-plane 진단입니다.
허용:
- 축약
node_id_prefix - coarse health와 descriptor state
- gossip과 route success freshness bucket
- aggregate route success/failure count
- aggregate loop/replay/rate-limit/quarantine count
- quarantine remaining time과 bounded reason bucket
허용 안 됨:
- 전체 node public key
- route ID 또는 endpoint list
- encrypted blob 또는 payload hash
- message ID 또는 receiver identity
- client IP, destination, DNS data
- social-graph edge 또는 통신 상대 관계
운영자 절차
Nodeboard에서 node를 선택한 뒤 Discovery와 Security / Relay Protection을 엽니다. 추세는 node health, reachability, queue pressure, signed proof freshness 범위에서만 해석합니다.
- Discovery descriptor와 bootstrap recovery가 fresh한지 확인합니다.
- Runtime ready 선언 전
accepted_total,accepted_percent, last accepted age를 비교합니다. real_relay_ready와 synthetic probe readiness를 구분합니다. 전자만 authenticated client-originated terminal receipt를 증명합니다.- protecting, degraded, attention이면 aggregate reason bucket과 transport health만 조사하고 user-level log를 요구하지 않습니다.
소스 맵
인프라가 다른 host로 이동해도 유효하도록 repository-relative path를 사용합니다. Backend와 Nodeboard는 별도 repository지만 owner-scoped privacy-safe node metadata만 사용합니다.
| 계층 | 저장소 경로 | 역할 |
|---|---|---|
| Rust relay API | crates/aeronyx-server/src/api/chat_peer.rs | Envelope를 인증하고 loop, replay, freshness, rate, quarantine, retry, terminal receipt rule을 적용합니다. |
| Rust PeerStore | crates/aeronyx-server/src/services/peer_store.rs | Bounded aggregate counter, peer health, readiness, proof classification을 저장합니다. |
| Rust health API | crates/aeronyx-server/src/api/vpn_health.rs | Local privacy-safe health JSON을 게시합니다. |
| Rust reporter | crates/aeronyx-server/src/management/reporter.rs | Node heartbeat metadata로 aggregate status를 전달합니다. |
| Backend observability | privacy_network/api/vpn_observability.py | Owner-scoped system metadata를 operator console에 반환합니다. |
| Nodeboard types | types/index.ts | Blind relay와 peer health response type을 정의합니다. |
| Nodeboard detail 및 i18n | app/dashboard/nodes/[id]/page.tsx and lib/i18n/index.ts | Localized privacy-boundary copy로 Security / Relay Protection을 표시합니다. |
다중 홉 라우팅 기반
Multi-hop routing에는 replay resistance, loop containment, bounded retry, peer quarantine, user traffic과 혼동되지 않는 evidence가 필요합니다. 이 guard는 blind-node invariant를 약화하지 않고 layered encryption과 route diversity의 기반이 됩니다.
개발 규칙
새 field는 모두 privacy review 대상으로 봅니다. Operator metric은 node reliability 질문에는 답해야 하지만 payload, sender, receiver, path, endpoint, conversation을 식별해서는 안 됩니다.
- 모든 relay path에서
payload_b64를 opaque로 유지합니다. - Aggregate counter 또는 bounded reason bucket만 추가합니다.
- Counter를 route ID, endpoint, user, receiver, message metadata와 join하지 않습니다.
- Synthetic probe를 encrypted message, packet, byte total에 포함하지 않습니다.
- 의미가 바뀌면 Rust test, Nodeboard type, 모든 언어 문서를 함께 갱신합니다.