Corvus Labs

Falcon telemetry

Instrument Falcon Solana submissions so acceptance and landing stay separate metrics, per transport, without ever logging your API key.

Falcon's response tells you almost nothing about whether a transaction landed, and each transport tells you a different amount. So instrument for that: one record for what Falcon said, one record for what the chain did, joined on the signature.

What to measure

Since no HTTP route reports a forwarding failure, a 200 rate near 100% is the expected steady state rather than a health signal. The number that means something is your landing rate, measured on your Solana RPC.

MetricSourceWhat a drop means
Acceptance rateFalcon response per transportYour requests are malformed, unauthorized or over the allowance
Landing rategetSignatureStatuses on your RPCCompetition, tip level, blockhash expiry, or delivery loss
Time to first observationSubmit timestamp → first statusRegional or pacing problems
Expiry rateSignatures that never landed before blockhash expiryYou are retrying past the useful window

Track them separately per transport and per region, or you'll average away the one transport that's failing.

What each transport can tell you

TransportRecordStructurally unavailable
JSON-RPCHTTP status, JSON-RPC code, message prefix, data.minimumLamports / data.expected / data.actualForwarding outcome
/binary, /plaintextHTTP status and the plain-text bodyForwarding outcome; the body is the only way to separate two different 413s or seven different 400s
QUICWhether the error was a SubmitError or a transport error, the variant, and the active TransportModeForwarding outcome; in datagram mode, acceptance too
Native UDPLocal send() errors onlyEverything else: acceptance, rejection reason, rate limiting

Record the active TransportMode alongside every QUIC result. In stream mode Ok(()) covers both "the server accepted it" and "the datagram was queued and the ack got lost", and without the mode those merge into one number.

Fields worth recording per submission

Key everything on the transaction signature: you know it before you submit, on every transport.

FieldWhy
SignatureThe only identifier that exists on all five transports
Transport and regionFailures are usually specific to one
Serialized size in bytesProximity to the 1,232-byte cap explains TooLarge clusters
Submit timestamp (UTC)Joins to slot timing and to support requests
skipPreflight valuePreflight failures (-32002, -32603) only exist when it is false
Falcon result: status, code and message prefix, or SubmitError variantThe whole error surface
Attempt number for this signatureFalcon suppresses a duplicate once that signature has been delivered, so attempts are not independently observable server-side; count them locally
Landing state, slot, and time from submitThe metric that matters
Terminal reason: landed, expired, abandonedCloses the record

Match Falcon errors on status + code + message prefix, never on full-string equality; messages carry variable detail like parse error: <detail> and the simulation error text.

A pcap of Falcon UDP traffic is a credential dump

The first 16 bytes of every native UDP datagram are your raw API key, or 36 ASCII characters in the alternative frame. The same goes for any HTTP access log that keeps the query string, since the key travels as ?api-key=.

So don't record:

  • the API key, in any transport;
  • full request URLs: strip or hash the api-key parameter before the URL reaches a log, a trace or an error reporter;
  • packet captures of UDP or QUIC traffic that leave your environment;
  • the client certificate material falcon-client generates at connect time.

Reporting a problem

On Discord, include:

  • the transaction signature;
  • UTC timestamp and the affected time window;
  • region and transport;
  • the exact error: HTTP status + JSON-RPC code + message, the plain-text body, or the SubmitError variant;
  • attempt count for that signature;
  • what your Solana RPC reports for the signature, if anything.

Never post the API key, or a URL containing it. For UDP problems, reproduce the same transaction over JSON-RPC first; it turns the silence into a readable error.

On this page