clientsignals

package module
v0.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 13, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

README

client-signals for Go

Go implementation of the shared client-signals contract.

Installation

go get github.com/superfly/client-signals/go

The package name is clientsignals:

import clientsignals "github.com/superfly/client-signals/go"

Usage

sig := clientsignals.DetectOnce()

header := http.Header{}
sig.ApplyHeaders(header)

To wrap an http.Client transport:

httpClient := &http.Client{
    Transport: sig.WrapTransport(http.DefaultTransport),
}

Use a custom header prefix with ApplyHeadersWithPrefix or WrapTransportWithPrefix:

sig.ApplyHeadersWithPrefix(header, "Acme")

API

  • Detect() Signals computes fresh signals.
  • DetectOnce() Signals computes and caches process-wide signals.
  • Signals.ApplyHeaders(http.Header) applies Fly-Client-* headers.
  • Signals.ApplyHeadersWithPrefix(http.Header, string) applies custom prefix headers.
  • Signals.WrapTransport(http.RoundTripper) wraps a transport and appends the User-Agent suffix.
  • Signals.WrapTransportWithPrefix(http.RoundTripper, string) wraps with a custom header prefix.

The Go module lives under go/; consumers of the old root module path must update imports to github.com/superfly/client-signals/go.

Development

go test ./...
go build ./...
GOOS=linux GOARCH=amd64 go build ./...
GOOS=darwin GOARCH=arm64 go build ./...
GOOS=windows GOARCH=amd64 go build ./...

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

View Source
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.

func (*ClientSignalsTransport) RoundTrip

func (t *ClientSignalsTransport) RoundTrip(req *http.Request) (*http.Response, error)

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

func (s Signals) ApplyHeaders(header http.Header)

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

func (s Signals) ApplyHeadersWithPrefix(header http.Header, prefix string)

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

func (s Signals) Operator() string

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.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL