# Binary Hub Protocol (wire) — TODO

## Priority legend
- **P0** blocker · **P1** important · **P2** nice-to-have · **P3** idea

> **Archived entries**: see [`SIGNALR_BINARY_PROTOCOL_TODO_2026_05.md`](SIGNALR_BINARY_PROTOCOL_TODO_2026_05.md) (`Status: Closed` entries rotate into year-month buckets as they accumulate).
> Archive files are not auto-loaded — read on demand if relevant context is suspected (regression hint, supersession reference, ID lookup for an archived entry).

---

## ACCORE-SBP-T-T5R2: Per-üzenet diagnosztikai sorok átsorolása Trace szintre
**Priority:** P2 · **Type:** Behaviour / zajcsökkentés · **Related:** `../../../AyCode.Core/docs/LOGGING/LOGGING_TODO.md#accore-log-t-d7s3`

**Probléma:** a protokoll soronkénti diagnosztikája túl magas szinten logol — `Serialize/Deserialize start/end` = **Info**, `WriteArgument`/`ReadSingleArgument`/`ReadArguments`/`ParseInvocation`/`TryParseMessage` = **Debug/Trace**. Következmény: minden hub-üzenet ~4–8 diagnosztika-sort termel a protokoll-loggerrel rendelkező oldalon — nagy forgalomnál konzol-áradat, és egy mindig-bekötött diagnosztikai logger (ld. ACCORE-LOG-T-D7S3) default szinten spammelne.

**Megoldás:** a per-üzenet sorok `LogTrace`-re; hiba/anomália marad Warning/Error. A `Serialize end totalSentSize=…` jellegű összegző sorok szintje mérlegelendő (Debug-on hagyva olcsó forgalmi áttekintést adnak).

**Ellenőrzendő:** az MS→AyCode szint-megfeleltetés az `AddAcLogger` hídban — a konzol-kimenet alapján MS `Trace` jelenleg Ac `Detail(0)`-ra képződik (a TryParseMessage-sorok `[Detail]`-lel jelennek meg), az Ac-enumban pedig `Detail(0) < Trace(5)`, tehát egy Detail-szintű writer a Trace-t IS átengedi. A „csend default szinten" célhoz a fogyasztó loggerek Debug(10)+ szintje és/vagy a mapping tisztázása kell.

**Érintett:** `AcBinaryHubProtocol` `Log*` segédmetódusai + `DebugLogArgument`.

## ACCORE-SBP-T-Y2R8: Per-message protocol mode selection
**Priority:** P2 · **Type:** Feature

**Problem:** Global `BinaryProtocolMode` config (program.cs) forces all messages through the same path — either Bytes (RAM spike on large payloads, no streaming overlap) or AsyncSegment (dedicated deser thread overhead on small frequent messages). Mixed workloads (small logs/telemetry + large DataSource/exports) cannot optimize per use-case.

**Solution:** Per-message mode override via invocation headers (`X-Binary-Protocol-Mode: Bytes|AsyncSegment`). Server `AcBinaryHubProtocol.SelectProtocolMode` checks header → explicit mode; fallback → `DefaultProtocolMode`. Client fluent API: `InvokeAsync(..., protocolMode: BinaryProtocolMode.Bytes)`.

**Use-cases:**
- Small frequent (logs, telemetry, CRUD < 4KB): `Bytes` → minimal overhead, inline deser on pool thread
- Large payloads (DataSource 10MB, file exports): `AsyncSegment` → streaming overlap + bounded RAM + dedicated deser thread

**Config:** `AcBinaryHubProtocolOptions.AllowPerMessageProtocolMode` gate (default: true).

**Non-solution:** Auto-detect size heuristic rejected — wire-format size unknown until serialize (dynamic collections, string interning, ID tracking).

## ACCORE-SBP-T-K3J7: Chunked protocol integration test
**Priority:** P1 · **Type:** Test coverage

End-to-end round-trip:
- `WriteMessageChunked(obj, AsyncSegment)` → bytes
- `TryParseMessage(bytes)` → reconstructed object
- Assert structural equality with original

Cover asymmetric cases:
- AsyncSegment sender, Segment receiver
- Bytes sender, AsyncSegment receiver
- WASM-downgraded sender (Segment), server AsyncSegment receiver

## ACCORE-SBP-T-L1V4: `BinaryProtocolMode.Auto` wire-detection implementation
**Priority:** P3 · **Type:** Feature · **Related:** `../SIGNALR/SIGNALR_TODO.md#accore-sig-t-v9h1`

Client-side adaptive mode: on first received message, inspect first payload byte:
- `CHUNK_START (200)` → peer uses AsyncSegment → match it on subsequent sends (subject to local-platform constraint — WASM safety-net overrides to Segment)
- Non-chunked → stay with Segment/Bytes

Per-`HubConnection` state. Requires changes to `BinaryProtocolMode` enum + detection wiring in `TryParseMessage`.

## ACCORE-SBP-T-R6D2: SignalR handshake-extension for upfront mode negotiation
**Priority:** P3 · **Type:** Feature · **Related:** ACCORE-SBP-T-L1V4

Alternative to wire-detection: use SignalR handshake message's `extensions` JSON field to carry protocol-capability info — e.g.
```json
{ "protocol": "acbinary", "version": 1, "extensions": { "acbinary": { "preferredMode": "AsyncSegment" } } }
```
Zero first-message overhead, fully explicit. Both sides advertise their send-modes; pick intersection. Specification to be drafted; compatibility with non-AC clients (pure JSON etc.) must remain.

---

# 🟡 NuGet competitiveness ideas — NOT current priority

> **Status: speculative / future-only.** The following P3 entries are feature ideas for a public NuGet release to broaden adoption appeal vs. competing protocols (gRPC, MessagePack, MemoryPack, Protobuf-net). **None are committed work.** They exist here for future reference only — safe to skip during current sprint planning. Each requires:
> - Threat model / use-case justification document **before** any code
> - Benchmark of zero-copy / pipeline impact
> - Decorator pattern (compose, do not embed in `AcBinaryHubProtocol`)
> - Opt-in via options; default path **must** stay free of any added overhead

These ideas were captured because the **wider binary-protocol market is currently silent** on these features (no major .NET binary serializer ships built-in encryption / compression / tracing). Real differentiation potential, but only if executed correctly.

## ACCORE-SBP-T-H7M5: Optional payload encryption (`AcEncryptionOptions`)
**Priority:** P3 — IDEA · **Type:** Feature (NuGet competitiveness) · **Status:** Open · **Umbrella ADR:** [`adr/0001`](../adr/0001-acbinary-decorator-feature-stack-design.md)

### Niches where TLS alone is insufficient
- **SignalR backplane / Azure SignalR Service** — relay sees plaintext; payload encryption gives true end-to-end client↔client.
- **TLS-terminating proxies** (Cloudflare, nginx, ALB) — internal segment cleartext; payload encryption is TLS-topology-independent.
- **Mobile/MAUI reverse-engineering** — if the key derives from user credentials (not embedded), a leaked binary doesn't grant payload access.
- **Multi-tenant SaaS over shared channel** — per-tenant keys make broadcast leakage fail closed.
- **Zero-trust intra-cluster** (modern enterprise trend) — complementary to mTLS service mesh.
- **Regulatory compliance** (HIPAA, PCI-DSS 4.1, GDPR Art.32, FedRAMP) — auditors often reject "TLS only" for ePHI / cardholder / classified data.

### Design constraints
- **Decorator only** — `EncryptingHubProtocolWrapper`, never embedded into `AcBinaryHubProtocol` (SRP).
- **`System.Security.Cryptography.AesGcm`** — never custom crypto. Optional ChaCha20-Poly1305 for ARM/mobile (faster on those targets).
- **Authenticated encryption mandatory** — encryption-only modes (CBC etc.) **forbidden**. Tamper detection is baseline, not optional.
- **Pluggable `IAcEncryptionKeyProvider`** — caller chooses: env var, KeyVault, HKDF from user creds, hardware token. No baked-in key source.
- **Replay protection bundled** — monotonic per-connection nonce counter; rejects out-of-window packets.
- **Opt-in** — `AcEncryptionOptions = null` (default) means the encryption code path is **completely absent** from the hot path (no branch, no check).
- **WASM compatibility** mandatory test — `AesGcm` is available on net8+ WASM.
- **AsyncSegment compatibility decision** — per-chunk auth tag (~28 byte overhead per chunk) vs. single-stream cipher (loses pipeline parallelism). Benchmark required.

### Acceptance criteria (before any code)
- Threat model document: what does this defend against that TLS doesn't, in measurable terms.
- Benchmark plan: zero-copy loss percentage, AsyncSegment chunk overhead.
- Decorator API sketch reviewed.

## ACCORE-SBP-T-N9F3: Optional message compression with `MinSize` threshold
**Priority:** P3 — IDEA · **Type:** Feature (NuGet competitiveness) · **Status:** Open · **Umbrella ADR:** [`adr/0001`](../adr/0001-acbinary-decorator-feature-stack-design.md)

### Niches
- **Large structured payloads** (orders, product lists, shipping documents) — typical 50-90% compression on text-heavy DTOs.
- **Mobile / metered connections** — bandwidth cost reduction.
- **Legacy slow links** (satellite, GPRS-class IoT) — payload size matters more than CPU.
- **Cross-DC replication / SignalR backplane traffic** — bytes through Redis backplane × N consumers.
- **Mixed traffic real-time apps** (heartbeat / acks / lookups + occasional large DTO) — see `MinSize` rationale below.

### `MinSize` threshold — market-gap differentiator

**Industry observation:** every major compression-aware system today is "all or nothing": gRPC, HTTP/2 response compression, WebSocket per-message-deflate (RFC 7692), Kafka producer compression — none have per-message size threshold. They compress everything once enabled.

**Empirical reality:** every compression algorithm has a **break-even point** below which compression LOSES (output > input + CPU cost wasted):

| Algorithm | Approximate break-even (byte) | Recommended `MinSize` default |
|-----------|------------------------------|-------------------------------|
| LZ4 | ~64-128 | 128 |
| Snappy | ~100 | 128 |
| Zstd | ~256-512 | 512 |
| Brotli | ~512-1024 | 1024 |
| Gzip/Deflate | ~200-500 | 512 |

In a real-time SignalR app where small heartbeats / acks / status pings interleave with occasional large DTOs, "all or nothing" compression **wastes CPU on small frames AND increases their size**. A per-message `MinSize` skip-threshold turns this from a coin-flip into a measurable win.

**Proposed semantic:**
```csharp
public sealed class AcCompressionOptions
{
    public AcCompressionAlgorithm Algorithm { get; set; }   // None | Lz4 | Brotli | Zstd | Snappy | Gzip
    public int? MinSize { get; set; } = null;               // null = use per-algorithm default; 0 = always compress; explicit value = override
    public CompressionLevel Level { get; set; } = CompressionLevel.Optimal;
    public bool AlwaysCompressInAsyncSegment { get; set; } = true;  // see AsyncSegment trade-off below
}
```

### AsyncSegment interaction — the streaming dilemma

In `AsyncSegment` mode the total message size is unknown until `CHUNK_END`, so a post-serialization `MinSize` check is impossible without buffering the entire message — which would defeat the pipeline-parallelism advantage of `AsyncSegment` in the first place.

| Option | How it works | Trade-off |
|--------|-------------|-----------|
| **(A) Per-chunk threshold** | Each chunk independently compressed or not, marker byte per chunk | Streaming-friendly. Lose cross-chunk dictionary benefit (Brotli/Zstd). Small chunks may bypass compression even when whole-message would benefit. |
| **(B) Whole-message buffer** | Buffer until `MinSize`, then decide; if compress, buffer the rest too | **Kills AsyncSegment pipeline parallelism** — message becomes effectively non-streaming. |
| **(C) `AsyncSegment` = always compress** (PROPOSED DEFAULT) | `MinSize` ignored in AsyncSegment mode; `Bytes` and `Segment` modes honour it | Logical: anyone choosing `AsyncSegment` is already optimizing for large payloads (small ones would use `Bytes`/`Segment`), so the threshold would never trigger anyway. Simple mental model. |
| **(D) Sliding window first-chunk buffer** | Buffer first N bytes (= MinSize); if exceeded, that buffer flushes as compressed first chunk + downstream streaming with dictionary | Elegant, preserves both threshold and pipeline. Significantly more complex. Future optimization. |

**Default decision: (C)** — `AlwaysCompressInAsyncSegment = true`. Override possible per-options. Document the trade-off explicitly so users picking `AsyncSegment + compression + small messages` know what they're choosing.

### Other design constraints
- **Decorator pattern**, same as ACCORE-SBP-T-H7M5.
- **Algorithm-pluggable** — at minimum LZ4 (fastest), Brotli (best ratio for text), Zstd (modern balanced). Default **none**.
- **Order with encryption matters**: compress FIRST, encrypt AFTER (compressing ciphertext is futile).
- **Wire marker byte** — 1-byte algorithm ID prefix so decoder knows what to expand. `0x00` = uncompressed (the small-message path), other values = algorithm IDs.
- **Per-algorithm `MinSize` default** — applied when `MinSize == null`; user override always wins.
- **Empty / single-byte messages** — bypass compression unconditionally regardless of `MinSize` (zero benefit, nonzero overhead).

### Acceptance criteria
- Benchmark: compression ratio + CPU cost per algorithm on representative payloads (Order, ProductList, ShippingDocument) AND small payloads (Ping, Ack, StatusUpdate). Verify break-even points empirically — adjust `MinSize` per-algorithm defaults if measured values diverge from the table above.
- Decorator API sketch reviewed.
- AsyncSegment interaction documented — the (C) default with override path; sample log output showing `MinSize`-skipped vs. compressed messages for diagnostics.
- Fallback / negotiation strategy: what happens if peer can't decompress? (Suggested: wire-marker `0x00` always works since it means "uncompressed" — sender can downgrade per-message if peer signals incompatibility, similar to HTTP `Accept-Encoding` semantics.)

## ACCORE-SBP-T-J5W8: OpenTelemetry tracing integration
**Priority:** P3 — IDEA · **Type:** Feature (NuGet competitiveness) · **Status:** Open · **Umbrella ADR:** [`adr/0001`](../adr/0001-acbinary-decorator-feature-stack-design.md)

### Niches
- **Distributed tracing** is a modern .NET observability standard — gRPC has it, MessagePack/MemoryPack/Protobuf-net don't.
- **Production diagnostics** — correlate SignalR call → server method → DB query in one trace.
- **Backpressure / latency analysis** — flush time, chunk-by-chunk progress as span events.

### Design constraints
- **`System.Diagnostics.ActivitySource`** based — no hard dependency on OTel SDK; consumers wire their own exporter.
- **Opt-in via `AcBinaryHubProtocolOptions.ActivitySource`** — null = zero overhead, no branch.
- **Span structure**: per `WriteMessage` / `TryParseMessage` invocation; chunked path emits chunk events as span events (not nested spans, to avoid cardinality explosion).
- **W3C Trace Context propagation** — read/write `traceparent` header in `Headers` dictionary for cross-service correlation.

### Acceptance criteria
- API sketch with sample exporter wiring (Jaeger / OTel Collector).
- Hot-path branch verification — when `ActivitySource == null`, JIT must eliminate the tracing code completely.

## ACCORE-SBP-T-B3K6: Optional HMAC signing (without encryption)
**Priority:** P3 — IDEA · **Type:** Feature (NuGet competitiveness) · **Status:** Open · **Umbrella ADR:** [`adr/0001`](../adr/0001-acbinary-decorator-feature-stack-design.md)

### Niches
- **Tamper detection where confidentiality is acceptable** — audit log forwarding, telemetry where data isn't sensitive but integrity must be provable.
- **Compliance lite** — "we sign all messages" satisfies some integrity-focused audit requirements without encryption complexity.
- **Plaintext debugging + integrity** — payload remains readable in tcpdump / Wireshark (debugging-friendly), but tampering is detected.

### Design constraints
- **Decorator pattern** — `SigningHubProtocolWrapper`, separate from encryption. Composable: encryption decorator + signing decorator can stack (encrypt-then-sign or sign-then-encrypt — pick canonical order, document it).
- **`System.Security.Cryptography.HMACSHA256`** default; SHA-512 optional.
- **Append-only on wire** — N-byte MAC tag at end of message, before any framing trailer.
- **Pluggable `IAcSigningKeyProvider`** — same idea as encryption key provider.
- **Constant-time comparison** mandatory — `CryptographicOperations.FixedTimeEquals`, never `==`.

### Acceptance criteria
- API sketch.
- Stack-with-encryption order decision documented (industry standard: **encrypt-then-MAC**, but evaluate trade-offs).

## ACCORE-SBP-T-S2H6: Replace `Type.GetType(wire-supplied-name)` with whitelist-based type resolution
**Priority:** P0 — security blocker · **Type:** Security fix · **Related Issue:** [`SIGNALR_BINARY_PROTOCOL_ISSUES.md#accore-sbp-i-r8k3`](SIGNALR_BINARY_PROTOCOL_ISSUES.md#accore-sbp-i-r8k3-typegettypewire-supplied-name-enables-deserialization-gadget-attack) (full threat model, attack surface, and mitigation analysis there — **do not duplicate here**) · **Planned solution:** [`../../../../AyCode.Core/AyCode.Core/docs/BINARY/BINARY_AQN_IMPLEMENTATION.md`](../../../../AyCode.Core/AyCode.Core/docs/BINARY/BINARY_AQN_IMPLEMENTATION.md) (consolidated design plan — single `IAcTypeBinder` shared with the AcBinary polymorph path, implicit trust via metadata ctor-feed, framework helper for `[SignalRTagBinding]` bulk-seed)

Replace the unsafe `Type.GetType(typeName)` lookup in `AyCodeBinaryHubProtocol`'s argument-binder header parsing with a registry-validated lookup. The deserialization-gadget attack surface this fix closes, the rationale for why the type-resolution mechanism is needed at all, and the four mitigation directions are covered in the issue — **this TODO tracks the chosen implementation path only**. The consolidated design plan in `BINARY_AQN_IMPLEMENTATION.md` selects the whitelist-registry direction and ties it together with the AcBinary polymorph path (single binder, two call-sites) — the implementation steps below align with that plan.

### Implementation steps

1. **Pick a mitigation direction** (the issue lists four: whitelist, tag-based, type-hash, audit-only stopgap). Recommended starting point: **whitelist registry**, because it can ship without a wire-format change and immediately closes the vulnerability for typical deployments. Tag-based dispatch can land as a follow-up wire-format evolution (separate TODO once direction is committed).

2. **Registry surface design** — likely on `AyCodeBinaryHubProtocolOptions`:
   - `Action<TypeWhitelistBuilder>? ConfigureTypeWhitelist { get; init; }` — fluent builder for explicit type registration
   - Auto-population path: `RegisterAcBinarySerializableTypesFrom(Assembly...)` — scans for `[AcBinarySerializable]`-marked types from declared assemblies (acceptable trust boundary for many apps; ALSO opt-in)
   - Defaulting: if no whitelist configured, the protocol either (a) refuses to start with a clear error message, or (b) uses an empty whitelist (rejecting all `Type.GetType` lookups). Decide based on backward-compat appetite.

3. **Replace the call site** (`AyCodeBinaryHubProtocol.cs` ~line 114):
   - Before: `resolvedType = Type.GetType(typeName);`
   - After: `resolvedType = _typeWhitelist.TryResolve(typeName);` — returns `null` if not registered → propagate as protocol error (reject the message, log with the offending type-name for security audit).

4. **Test plan**:
   - Unit: whitelist-registered type resolves; non-registered type returns `null` and the protocol throws / logs.
   - Security regression: pass `System.Diagnostics.Process`, `System.IO.File`-related descriptor types — all rejected.
   - Backward compat: existing benchmarks / integration tests continue to pass with their DTOs registered.
   - Versioning: a registered DTO whose assembly version changed (NuGet upgrade) still resolves, because the whitelist is registry-keyed (not assembly-version-keyed) — verify or design accordingly.

### Acceptance criteria
- The vulnerability described in `R8K3` is no longer reachable: a wire-supplied non-whitelisted type-name results in a protocol error, NOT a `Type.GetType` BCL lookup.
- The `R8K3` issue is updated with `Status: Fixed` (or `Mitigated` if a chosen direction only partially closes it).
- A subsequent TODO is opened for tag-based dispatch (wire-format evolution) IF that direction is also pursued — this TODO covers the immediate whitelist-fix only.

### Out of scope (deliberately)
- Wire-format changes (tag-based / type-hash dispatch). Those are a separate TODO once direction is committed and a wire-format-evolution PR plan exists.
- Audit of every Hub-method `dataArg : object` use site in consumer projects. The fix is at the protocol level — consumers register their DTOs and the protocol enforces.
