Documentation
¶
Overview ¶
Package clientsignals computes coarse, privacy-safe signals that help estimate whether a CLI process is being driven by a human or an AI agent.
This package is intentionally self-contained: no dependencies beyond the standard library and golang.org/x/sys (used for syscall-based parent-process lookup on Darwin/Windows), so it can be used standalone.
Index ¶
- Constants
- type ClientSignalsTransport
- type Signals
- func (s Signals) ApplyHeaders(header http.Header)
- func (s Signals) ApplyHeadersWithPrefix(header http.Header, prefix string)
- func (s Signals) Operator() string
- func (s Signals) WrapTransport(inner http.RoundTripper) *ClientSignalsTransport
- func (s Signals) WrapTransportWithPrefix(inner http.RoundTripper, prefix string) *ClientSignalsTransport
Constants ¶
const DefaultHeaderPrefix = "Fly"
DefaultHeaderPrefix is the header-name prefix WrapTransport uses when no other prefix is configured: "Fly", giving Fly-Client-Interactive, Fly-Client-Parent, etc. Callers outside Fly.io can use WrapTransportWithPrefix to substitute their own.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ClientSignalsTransport ¶
type ClientSignalsTransport struct {
InnerTransport http.RoundTripper
// contains filtered or unexported fields
}
ClientSignalsTransport wraps an http.RoundTripper, attaching the {prefix}-Client-* headers and appending the client-signals token to the existing User-Agent header on every outgoing request.
Construct one via Signals.WrapTransport or Signals.WrapTransportWithPrefix. RoundTrip does no detection work itself — it only applies the values already computed when the transport was built.
type Signals ¶
type Signals struct {
// Interactive is true if the process's stdout appears to be attached to
// a terminal.
Interactive bool
// Parent is a coarse bucket describing the immediate parent process.
// Always one of "node", "python", "shell", or "other" — never a raw
// process name.
Parent string
// Agent is the cooperative agent marker, e.g. "claude-code". Empty if no
// agent was declared or detected.
Agent string
// AgentSource identifies how Agent was determined, e.g.
// "env:FLY_INVOKED_BY" or "env:CLAUDECODE" — the matched variable name,
// never its value. Empty if and only if Agent is empty.
AgentSource string
// CI is true when a CI environment is detected.
CI bool
}
Signals is the set of coarse, privacy-safe traffic-classification signals computed once per process.
See docs/signals.md for the reasoning behind these fields, each one's known reliability caveats, and how they're meant to be combined — no single field here is sufficient on its own, and none should ever drive gating/enforcement decisions.
func Detect ¶
func Detect() Signals
Detect computes the current process's client signals fresh from the environment and file descriptors. It is pure and side-effect free (aside from reading process state); it does not cache its result — callers that want a single value for the lifetime of a process should cache it themselves.
func DetectOnce ¶
func DetectOnce() Signals
DetectOnce returns the process-wide signals, computed once via Detect and cached for the lifetime of the process. Detection involves a parent-process lookup and environment scanning, so callers should fetch this once (e.g. at client-construction time) and reuse the result rather than calling it per request.
func (Signals) ApplyHeaders ¶
ApplyHeaders sets the Fly-Client-* headers on header directly, for callers that need to attach client signals to something other than an http.Request going through an http.RoundTripper — e.g. a WebSocket handshake's header, built and sent outside of net/http's Client/Transport machinery entirely. Uses DefaultHeaderPrefix ("Fly").
func (Signals) ApplyHeadersWithPrefix ¶
ApplyHeadersWithPrefix is like ApplyHeaders but lets callers outside Fly.io substitute their own header prefix, matching WrapTransportWithPrefix's prefix for the same Signals value.
func (Signals) Operator ¶ added in v0.4.2
Operator returns a single classification for the process's operator. Precedence: ci > agent > interactive > unknown.
This is a convenience for consumers that want one label describing who is driving the process. The raw fields (CI, Agent, Interactive) remain available for callers that need finer-grained logic.
func (Signals) WrapTransport ¶
func (s Signals) WrapTransport(inner http.RoundTripper) *ClientSignalsTransport
WrapTransport wraps inner in a *ClientSignalsTransport that attaches s to every request the returned transport forwards, using DefaultHeaderPrefix ("Fly-Client-*").
func (Signals) WrapTransportWithPrefix ¶
func (s Signals) WrapTransportWithPrefix(inner http.RoundTripper, prefix string) *ClientSignalsTransport
WrapTransportWithPrefix is like WrapTransport but lets callers outside Fly.io substitute their own header prefix (e.g. "Acme" for Acme-Client-Interactive, Acme-Client-Parent, ...) instead of the "Fly" default. prefix must not be empty.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Command clientsignals prints the client signals that would be attached to outbound Fly API requests from the current process/environment.
|
Command clientsignals prints the client signals that would be attached to outbound Fly API requests from the current process/environment. |