Computes coarse, privacy-safe signals that help estimate whether a CLI
process is being driven by a human or an AI agent: terminal attachment, a
coarse parent-process bucket (node/python/shell/other), a
cooperative agent marker (self-declared via FLY_INVOKED_BY, or passively
detected from known agent-harness environment variables), and CI detection.
These are meant to be combined into an estimate with confidence, never treated as per-request certainty, and never used for gating, blocking, rate-limiting, or auth decisions.
sig := clientsignals.DetectOnce()
httpClient := &http.Client{
Transport: sig.WrapTransport(http.DefaultTransport),
}ClientSignalsTransport (returned by Signals.WrapTransport) attaches
Fly-Client-* headers and a (interactive=...; parent=...; agent=...)
User-Agent suffix to every request it forwards. Detection happens once,
at the point you call Detect()/DetectOnce() — never per request.
This library isn't Fly.io-specific — the Fly header prefix is just the
default. Use Signals.WrapTransportWithPrefix to use your own:
httpClient := &http.Client{
Transport: sig.WrapTransportWithPrefix(http.DefaultTransport, "Acme"),
}
// -> Acme-Client-Interactive, Acme-Client-Parent, ...For traffic that doesn't go through an http.RoundTripper at all — e.g. a
WebSocket handshake built and sent by hand — use Signals.ApplyHeaders (or
ApplyHeadersWithPrefix) to set the same headers directly on an
http.Header:
header := http.Header{}
sig.ApplyHeaders(header) // or sig.ApplyHeadersWithPrefix(header, "Acme")
conn, _, err := dialer.DialContext(ctx, wsURL, header)go run ./cmdprints the currently-detected signals as JSON.
- Only approved, finite values are ever emitted (see
Signalsfield docs). - Agent detection is presence/exact-value based; secret-shaped environment variables are never read or forwarded.
FLY_INVOKED_BY(and the cross-toolAGENTconvention) are sanitized and length-capped before being emitted anywhere.
See docs/signals.md for the reasoning behind each field
in Signals and its known reliability caveats (parent-process detection
especially is noisier than it looks), and
docs/markers.md for the full rationale behind the known
agent markers table (markers.go) — where each entry came from, its
confidence, and how to add new ones.
If you have write access to this repo, you can ship a release with:
scripts/bump_version.sh
Or a prerelease with:
scripts/bump_version.sh prerel
The release and notes will be created automatically via Github Actions. Follow along in: https://github.com/superfly/client-signals/actions/workflows/release.yml