HarperFast / HarperFast/symphony

Allow forwardFingerprint to carry both JA3 and JA4

Open
#35 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
1
Forks
0
PR merge metrics
No merged PRs in 30d

Description

## Summary

`forwardFingerprint` is a single-valued enum — `'ja3' | 'ja4' | 'none'` — so a route can forward one fingerprint or the other, never both. Please allow both.

## Why

Symphony already computes both in `PeekInfo` (`src/sni.rs`), for every connection, whether or not either is forwarded. The two carriers are already distinct and non-conflicting:

- PROXY v2 TLV: `0xE0` = JA3, `0xE1` = JA4 — separate types, both fit the existing TLV section.
- HTTP header fallback: `X-JA3` and `X-JA4` — separate names, and `header_rewrites` already returns a `Vec`.

So the values exist and the wire formats exist; the enum is the only thing preventing both from being emitted.

## Why a downstream would want both

WAF and abuse-detection operators generally need both during any realistic transition period:

- **JA3** matches existing blocklists, historical logs, and third-party threat-intel feeds, which are overwhelmingly JA3-keyed.
- **JA4** is the value you actually want to make decisions on going forward, for exactly the reason your README documents — Chrome and other modern browsers randomize ClientHello extension order, so a single browser produces many JA3 hashes and per-browser JA3 blocking is unreliable.

Forwarding only JA4 means an operator's accumulated JA3 corpus goes dark on cutover. Forwarding only JA3 means adopting a fingerprint your own docs describe as unreliable. Both, for a period, is the honest configuration — and the cost here is one extra TLV (or one extra header) per connection on routes that opt in.

Your README already notes that JA3 values collected from earlier Symphony builds are not comparable after the GREASE-handling fix, which is another reason operators will want to run both concurrently while they rebuild a corpus against the corrected implementation.

## Suggested shape

Relax the field to accept a set while keeping the current scalar form working:

```typescript
forwardFingerprint: 'ja4' // unchanged
forwardFingerprint: ['ja3', 'ja4'] // new
```

`header_rewrites` already builds a vector, so the header path looks like appending a second `HeaderRewrite`. The TLV path appends a second TLV. The existing "no viable carrier" startup warning (`src/proxy.rs:744`) should presumably fire if *any* requested fingerprint has no carrier.

## Context

We're deploying Harper Fabric behind Symphony in front of a WooCommerce origin ([HarperFast/harper-woo-cache](https://github.com/HarperFast/harper-woo-cache)), where the origin's WAF consumes the fingerprint for rate limiting and bot classification. Our intake work is tracked in [harper-woo-cache#26](https://github.com/HarperFast/harper-woo-cache/issues/26).

Worth noting for anyone finding this issue later: our actual blocker is **not** Symphony. It is that Harper core parses PROXY protocol **v1 only** — verified in both `harper@5.1.23` and `harper@5.2.0-beta.2` (`dist/server/http.js`, `enableProxyProtocol`) — so the v2 TLV carrier, which is the correct and h2-safe way to move a fingerprint, cannot be consumed yet. That's a Harper-side ask and is filed there. This issue is only the "both fingerprints" enhancement.

One small doc nit while I'm here: the comment at `src/proxy_conn.rs:191` refers to "Harper's `-h2.sock` mirror". No such mirror exists in 5.1.23 or 5.2.0-beta.2 — Harper's UDS mirror is a plain `node:http` server (HTTP/1.1 only) and its HTTP/2 support lives only on the secure TCP port. The `destination_h2` split-upstream feature is fine; the example target just isn't real yet.

Contributor guide

Open the contributing guide

Research direction

Start by tracing forwardFingerprint through the route configuration and forwarding paths, beginning with src/sni.rs and the warning at src/proxy.rs:744. Review the existing header_rewrites and PROXY v2 TLV handling, then verify that scalar configuration remains valid and that selecting both fingerprints emits both carriers when available, with the warning covering any unavailable request.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
networking
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
66/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.