Documentation
¶
Overview ¶
Package behavior provides human-like behavioral simulation for the Foxhound scraping engine. Every exported type is pure computation — no I/O, no goroutines — so callers can use the helpers from any context.
Index ¶
- func GammaClamped(alpha, rate, lo, hi float64) float64
- func GammaSample(alpha, rate float64) float64
- func GaussianClamped(sigma, bound float64) float64
- func LogNormalSample(mu, sigma float64) float64
- func WeibullClamped(k, lambda, lo, hi float64) float64
- func WeibullSample(k, lambda float64) float64
- type BehaviorProfile
- type DurationRange
- type FatigueConfig
- type KeyAction
- type Keyboard
- type KeyboardConfig
- type Mouse
- type MouseConfig
- type Navigation
- func (n *Navigation) Referer(targetDomain string) string
- func (n *Navigation) SessionDuration() time.Duration
- func (n *Navigation) SessionGap() time.Duration
- func (n *Navigation) SessionPages() int
- func (n *Navigation) ShouldGoBack() bool
- func (n *Navigation) ShouldSearch() bool
- func (n *Navigation) ShouldVisitUseless() bool
- type NavigationConfig
- type Point
- type ProfileName
- type Range
- type Rhythm
- type RhythmConfig
- type RhythmState
- type Scroll
- func (s *Scroll) ScrollGesture(mode ScrollMode) (distance int, pause time.Duration, up bool)
- func (s *Scroll) ScrollGestureAxis(mode ScrollMode, axis ScrollAxis) (distance int, pause time.Duration, reverse bool)
- func (s *Scroll) ScrollSequence(pageHeight int, mode ScrollMode) []ScrollAction
- func (s *Scroll) ScrollSequenceAxis(extent int, mode ScrollMode, axis ScrollAxis) []ScrollAction
- type ScrollAction
- type ScrollAxis
- type ScrollConfig
- type ScrollMode
- type SessionFatigue
- type Timing
- type TimingConfig
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func GammaClamped ¶
GammaClamped returns a Gamma sample clamped to [lo, hi] via rejection. Uses a safety cap of 1000 iterations. Falls back to the midpoint if no sample is accepted (indicates misconfigured parameters).
func GammaSample ¶
GammaSample returns a sample from a Gamma(alpha, rate) distribution using the Marsaglia-Tsang method. Pure Go, no external dependencies.
For alpha >= 1:
d = alpha - 1/3, c = 1/sqrt(9*d) loop: x = NormFloat64(), v = (1+c*x)^3 accept if v > 0 and log(U) < 0.5*x^2 + d - d*v + d*ln(v) return d*v / rate
For alpha < 1: use Gamma(alpha+1) * U^(1/alpha) transformation.
func GaussianClamped ¶
GaussianClamped returns a Gaussian sample with given sigma, within [-bound, +bound]. Uses rejection sampling to avoid probability spikes at the boundaries. sigma should be bound/2.5 so ~99% of raw samples are accepted.
func LogNormalSample ¶
LogNormalSample returns a sample from LogNormal(mu, sigma). Result = exp(mu + sigma * NormFloat64())
func WeibullClamped ¶
WeibullClamped returns a Weibull sample clamped to [lo, hi]. Uses rejection sampling with a safety cap of 1000 iterations. Falls back to the midpoint of [lo, hi] if no sample is accepted.
func WeibullSample ¶
WeibullSample returns a sample from a Weibull distribution with shape k and scale lambda. Formula: X = lambda * (-ln(U))^(1/k) where U ~ Uniform(0,1) Weibull is used for rhythm delays and scroll pauses because it produces right-skewed distributions matching observed human reaction times.
Types ¶
type BehaviorProfile ¶
type BehaviorProfile struct {
Name ProfileName
Timing TimingConfig
Mouse MouseConfig
Scroll ScrollConfig
Keyboard KeyboardConfig
// Rhythm controls the burst/pause cadence of a virtual user session.
// It is initialised from DefaultRhythmConfig and tuned per preset.
Rhythm *Rhythm
// Fatigue controls the session warmup and fatigue model.
Fatigue FatigueConfig
}
BehaviorProfile bundles all per-subsystem configurations into a named preset. Pass individual Config structs to the subsystem constructors as needed.
func AggressiveProfile ¶
func AggressiveProfile() *BehaviorProfile
AggressiveProfile returns a faster, higher-risk preset.
Suitable for sites with minimal or no behavioural analysis. Block rate will increase on heavily-protected targets.
func CarefulProfile ¶
func CarefulProfile() *BehaviorProfile
CarefulProfile returns a very human-like, slow preset.
Designed for Cloudflare Enterprise / Akamai Bot Manager targets where any timing anomaly triggers a challenge. Throughput is intentionally low.
func GetProfile ¶
func GetProfile(name ProfileName) *BehaviorProfile
GetProfile returns the named profile. Falls back to ModerateProfile for unknown names so callers never receive a nil pointer.
func ModerateProfile ¶
func ModerateProfile() *BehaviorProfile
ModerateProfile returns the default balanced preset.
func (*BehaviorProfile) Jitter ¶
func (p *BehaviorProfile) Jitter() *BehaviorProfile
Jitter returns a copy of this profile with per-session random perturbation applied to all numeric parameters. This prevents clustering attacks that would otherwise identify all sessions using the same named profile.
Each parameter is multiplied by (1 + U(-jitterFrac, +jitterFrac)) where jitterFrac defaults to 0.15 (±15%).
func (*BehaviorProfile) JitterBy ¶
func (p *BehaviorProfile) JitterBy(frac float64) *BehaviorProfile
JitterBy returns a copy with the specified jitter fraction applied.
type DurationRange ¶
DurationRange specifies an inclusive duration interval.
type FatigueConfig ¶
type FatigueConfig struct {
// WarmupAmplitude is the fractional slowdown at session start (default 0.4 = 40% slower).
WarmupAmplitude float64
// WarmupTau is the warmup time constant -- 63% of warmup effect gone after this duration.
WarmupTau time.Duration
// FatigueAmplitude is the maximum fractional slowdown from fatigue (default 0.25 = 25% slower).
FatigueAmplitude float64
// FatigueTau is the fatigue time constant -- 63% of max fatigue reached after this duration.
FatigueTau time.Duration
}
FatigueConfig controls the session warmup and fatigue model.
The speed factor follows an inverted-U curve:
speed_factor(t) = warmup(t) * fatigue(t) warmup(t) = 1.0 + WarmupAmplitude * exp(-t / WarmupTau) fatigue(t) = 1.0 + FatigueAmplitude * (1 - exp(-t / FatigueTau))
At session start (t=0): factor = 1 + WarmupAmplitude (slow start). At cruise speed (~5 min): factor ~ 1.07. At late session (~30 min): fatigue dominates, factor rises again.
func DefaultFatigueConfig ¶
func DefaultFatigueConfig() FatigueConfig
DefaultFatigueConfig returns the moderate fatigue preset.
type KeyAction ¶
type KeyAction struct {
// Char is the character to type. For backspace actions Char is 0.
Char rune
// Delay is the pause before this keystroke.
Delay time.Duration
// IsBackspace is true when this action represents a correction keystroke.
IsBackspace bool
}
KeyAction represents a single keystroke event in a typing sequence.
type Keyboard ¶
type Keyboard struct {
// contains filtered or unexported fields
}
Keyboard generates human-like typing sequences.
func NewKeyboard ¶
func NewKeyboard(cfg KeyboardConfig) *Keyboard
NewKeyboard creates a Keyboard with the supplied configuration.
func (*Keyboard) TypeString ¶
TypeString converts text into a sequence of KeyActions that, when played back in order, reproduce the intended string.
For each rune in text:
- With probability TypoProb a random adjacent-key typo is inserted, followed immediately by a backspace and the correct character.
- When BigramModel is enabled, inter-key delay is computed from character frequency, hand/finger transitions, and position fatigue. Otherwise a uniform delay in [MinDelay, MaxDelay] is used.
type KeyboardConfig ¶
type KeyboardConfig struct {
// MinDelay is the minimum inter-key delay (default 50 ms).
MinDelay time.Duration
// MaxDelay is the maximum inter-key delay (default 200 ms).
MaxDelay time.Duration
// TypoProb is the per-character probability of introducing a typo followed
// by a backspace correction (default 0.02).
TypoProb float64
// BigramModel enables the bigram-aware typing model that adjusts inter-key
// delay based on character frequency, hand/finger transitions, and position
// fatigue. When false, the original uniform delay is used.
BigramModel bool
}
KeyboardConfig configures keyboard typing simulation.
func DefaultKeyboardConfig ¶
func DefaultKeyboardConfig() KeyboardConfig
DefaultKeyboardConfig returns architecture-recommended defaults.
type Mouse ¶
type Mouse struct {
// contains filtered or unexported fields
}
Mouse generates human-like mouse-movement trajectories.
func NewMouse ¶
func NewMouse(cfg MouseConfig) *Mouse
NewMouse creates a Mouse with the supplied configuration.
func (*Mouse) ClickDuration ¶
ClickDuration returns the duration between mouse-down and mouse-up. Uses LogNormal distribution (median ~90ms) matching observed human click press-and-release timing. Clamped to [40ms, 250ms].
func (*Mouse) ClickOffset ¶
ClickOffset returns a small Gaussian-distributed offset from an element's centre. Range: [-5, +5] px in each axis, center-heavy for natural click targeting.
func (*Mouse) IdleDrift ¶
IdleDrift returns a small Gaussian-distributed drift suitable for idle mouse simulation. Range: [-2, +2] px in each axis, center-heavy for realistic micro-movements.
func (*Mouse) MoveTo ¶
MoveTo generates a bezier-curve path from start to end.
Implementation details:
- 3-5 random control points are placed between start and end, offset perpendicular to the straight line to create natural curvature.
- The bezier curve is sampled at 20-50 evenly-spaced t values.
- Each sampled point receives independent micro-jitter (±Jitter px).
- Speed profile: slow at endpoints, fast in middle (ease-in-out via smoothstep remapping of t).
- With probability OvershootProb the final point overshoots by up to OvershootPx px, then a correction segment brings it back to end.
type MouseConfig ¶
type MouseConfig struct {
// Jitter is the maximum per-point micro-jitter in pixels (default 2.0).
Jitter float64
// OvershootProb is the probability that the cursor overshoots the target
// before correcting back (default 0.2).
OvershootProb float64
// OvershootPx is the maximum overshoot distance in pixels (default 3.0).
OvershootPx float64
}
MouseConfig configures mouse movement generation.
func DefaultMouseConfig ¶
func DefaultMouseConfig() MouseConfig
DefaultMouseConfig returns the architecture-recommended defaults.
type Navigation ¶
type Navigation struct {
// contains filtered or unexported fields
}
Navigation generates human-like browsing patterns.
func NewNavigation ¶
func NewNavigation(cfg NavigationConfig) *Navigation
NewNavigation creates a Navigation with the supplied configuration.
func (*Navigation) Referer ¶
func (n *Navigation) Referer(targetDomain string) string
Referer returns a plausible HTTP Referer header value for a walker arriving at targetDomain from a search engine or direct navigation.
The returned string is always an absolute HTTP/HTTPS URL.
func (*Navigation) SessionDuration ¶
func (n *Navigation) SessionDuration() time.Duration
SessionDuration returns how long the current session should last, drawn uniformly from [SessionDuration.Min, SessionDuration.Max].
func (*Navigation) SessionGap ¶
func (n *Navigation) SessionGap() time.Duration
SessionGap returns the idle time before the next session begins, drawn uniformly from [SessionGap.Min, SessionGap.Max].
func (*Navigation) SessionPages ¶
func (n *Navigation) SessionPages() int
SessionPages returns how many pages the current session should visit, drawn uniformly from [PagesPerSession.Min, PagesPerSession.Max].
func (*Navigation) ShouldGoBack ¶
func (n *Navigation) ShouldGoBack() bool
ShouldGoBack returns true when the walker should navigate backward.
func (*Navigation) ShouldSearch ¶
func (n *Navigation) ShouldSearch() bool
ShouldSearch returns true when the walker should use the site search.
func (*Navigation) ShouldVisitUseless ¶
func (n *Navigation) ShouldVisitUseless() bool
ShouldVisitUseless returns true when the walker should visit a low-value page to simulate realistic browsing patterns.
type NavigationConfig ¶
type NavigationConfig struct {
PagesPerSession Range
SessionDuration DurationRange
SessionGap DurationRange
BackButtonProb float64
// (about, contact, FAQ) to add realism.
UselessPageProb float64
SearchProb float64
}
NavigationConfig configures browsing-pattern generation.
func DefaultNavigationConfig ¶
func DefaultNavigationConfig() NavigationConfig
DefaultNavigationConfig returns the architecture-documented defaults.
type ProfileName ¶
type ProfileName string
ProfileName identifies a preset behaviour configuration.
const ( // ProfileCareful is slow and maximally human-like — best for heavily // protected sites where throughput is less important than stealth. ProfileCareful ProfileName = "careful" // ProfileModerate is the default balanced profile. ProfileModerate ProfileName = "moderate" // ProfileAggressive trades stealth for speed — suitable for lightly // protected sites. ProfileAggressive ProfileName = "aggressive" )
type Rhythm ¶
type Rhythm struct {
// contains filtered or unexported fields
}
Rhythm manages the burst/pause state machine for a single virtual user. It is not safe for concurrent use — each Walker owns its own Rhythm.
func NewRhythm ¶
func NewRhythm(cfg RhythmConfig) *Rhythm
NewRhythm creates a Rhythm initialised at the beginning of the first burst.
func (*Rhythm) Next ¶
Next returns the delay that the caller should wait before performing the next action and advances the internal state machine.
State transitions:
RhythmBurst (actionsLeft > 0):
Decrement actionsLeft, return a short burst delay. State stays Burst.
RhythmBurst (actionsLeft == 0):
Burst exhausted. Transition to RhythmPause or RhythmLongPause and
return the pause duration so the caller sleeps before the next burst.
RhythmPause / RhythmLongPause:
Pause finished. Start a new burst and return 0 so the caller acts
immediately (the caller already slept the pause duration on the
previous call).
type RhythmConfig ¶
type RhythmConfig struct {
// BurstMin is the minimum number of actions in a single burst.
BurstMin int
// BurstMax is the maximum number of actions in a single burst.
BurstMax int
// PauseMin is the lower bound for a normal inter-burst pause.
PauseMin time.Duration
// PauseMax is the upper bound for a normal inter-burst pause.
PauseMax time.Duration
// LongPauseMin is the lower bound for a long pause.
LongPauseMin time.Duration
// LongPauseMax is the upper bound for a long pause.
LongPauseMax time.Duration
// LongPauseProb is the probability (in [0, 1]) that a long pause is
// chosen instead of a normal pause at the end of each burst.
LongPauseProb float64
}
RhythmConfig controls the burst/pause pattern for a scraping session.
Architecture spec:
Burst: 5-15 rapid actions Pause: 10-60 s Burst: 3-8 actions Long pause: 1-5 min (probability 0.15 after each burst)
func DefaultRhythmConfig ¶
func DefaultRhythmConfig() RhythmConfig
DefaultRhythmConfig returns the rhythm configuration that matches the architecture specification:
BurstMin/Max: 5 – 15 actions PauseMin/Max: 10 s – 60 s LongPauseMin/Max: 1 min – 5 min LongPauseProb: 0.15
type RhythmState ¶
type RhythmState int
RhythmState describes the current phase of a scraping session's rhythm.
const ( // RhythmBurst is the active phase: rapid sequential actions (page loads, // clicks, form submissions). Delays between actions are short. RhythmBurst RhythmState = iota // RhythmPause is the rest phase after a burst: simulates reading time or // light distraction. Duration is drawn from [PauseMin, PauseMax]. RhythmPause // RhythmLongPause is a deeper rest phase: simulates a break or context // switch away from the browser. Duration is drawn from [LongPauseMin, LongPauseMax]. RhythmLongPause )
type Scroll ¶
type Scroll struct {
// contains filtered or unexported fields
}
Scroll generates human-like scroll behaviour.
func NewScroll ¶
func NewScroll(cfg ScrollConfig) *Scroll
NewScroll creates a Scroll with the supplied configuration.
func (*Scroll) ScrollGesture ¶
ScrollGesture returns the attributes of a single scroll action:
- distance: pixels scrolled (always positive)
- pause: delay to wait after the gesture
- up: direction flag
func (*Scroll) ScrollGestureAxis ¶
func (s *Scroll) ScrollGestureAxis(mode ScrollMode, axis ScrollAxis) (distance int, pause time.Duration, reverse bool)
ScrollGestureAxis returns the attributes of a single scroll action for the given axis. For vertical, this delegates to ScrollGesture. For horizontal, distances are drawn from the horizontal config ranges.
func (*Scroll) ScrollSequence ¶
func (s *Scroll) ScrollSequence(pageHeight int, mode ScrollMode) []ScrollAction
ScrollSequence generates a complete scroll sequence for a page with the given pixel height. It keeps producing gestures until the cumulative net downward scroll roughly covers pageHeight, then stops. Upward gestures count against the total to ensure the sequence is realistic in length. Every page gets at least one action.
func (*Scroll) ScrollSequenceAxis ¶
func (s *Scroll) ScrollSequenceAxis(extent int, mode ScrollMode, axis ScrollAxis) []ScrollAction
ScrollSequenceAxis generates a complete scroll sequence for a page with the given pixel extent along the specified axis.
type ScrollAction ¶
type ScrollAction struct {
// Distance is the scroll magnitude in pixels (always positive; Up indicates
// direction).
Distance int
// Pause is the delay after this gesture before the next action.
Pause time.Duration
// Up is true if the scroll gesture moves the viewport upward.
Up bool
// Axis is the scroll direction (vertical or horizontal).
Axis ScrollAxis
}
ScrollAction describes a single scroll gesture and the pause that follows it.
type ScrollAxis ¶
type ScrollAxis int
ScrollAxis determines the scroll direction.
const ( // ScrollVertical scrolls the viewport up/down (default). ScrollVertical ScrollAxis = iota // ScrollHorizontal scrolls the viewport left/right. ScrollHorizontal )
type ScrollConfig ¶
type ScrollConfig struct {
// ReadMinPx is the minimum scroll distance per gesture in reading mode.
ReadMinPx int
// ReadMaxPx is the maximum scroll distance per gesture in reading mode.
ReadMaxPx int
// ScanMinPx is the minimum scroll distance per gesture in scan mode.
ScanMinPx int
// ScanMaxPx is the maximum scroll distance per gesture in scan mode.
ScanMaxPx int
// ReadPause is the midpoint pause for reading mode. Actual pauses are
// sampled from [ReadPause*0.5, ReadPause*2.5] to simulate natural variance.
ReadPause time.Duration
// ScanPause is the midpoint pause for scan mode. Actual pauses are
// sampled from [ScanPause*0.6, ScanPause*2.0].
ScanPause time.Duration
// ScrollUpProb is the probability that any given gesture scrolls upward
// (re-reading behaviour).
ScrollUpProb float64
// HorizMinPx is the minimum horizontal scroll distance per gesture in reading mode.
HorizMinPx int
// HorizMaxPx is the maximum horizontal scroll distance per gesture in reading mode.
HorizMaxPx int
// HorizScanMinPx is the minimum horizontal scroll distance per gesture in scan mode.
HorizScanMinPx int
// HorizScanMaxPx is the maximum horizontal scroll distance per gesture in scan mode.
HorizScanMaxPx int
}
ScrollConfig configures scroll behaviour generation.
func DefaultScrollConfig ¶
func DefaultScrollConfig() ScrollConfig
DefaultScrollConfig returns the architecture-recommended defaults.
type ScrollMode ¶
type ScrollMode int
ScrollMode determines which scroll-speed profile is used.
const ( // ScrollReading simulates a user reading content: short gestures with // long pauses. ScrollReading ScrollMode = iota // ScrollScan simulates a user skimming a page: long gestures, short // pauses. ScrollScan )
type SessionFatigue ¶
type SessionFatigue struct {
// contains filtered or unexported fields
}
SessionFatigue tracks elapsed session time and computes the current speed multiplier. Not safe for concurrent use -- each Walker owns its own instance.
func NewSessionFatigue ¶
func NewSessionFatigue(cfg FatigueConfig) *SessionFatigue
NewSessionFatigue creates a SessionFatigue with the given configuration.
func (*SessionFatigue) AdjustDelay ¶
func (f *SessionFatigue) AdjustDelay(base time.Duration) time.Duration
AdjustDelay multiplies the base delay by the current fatigue factor.
func (*SessionFatigue) Factor ¶
func (f *SessionFatigue) Factor() float64
Factor returns the current speed multiplier. Values > 1.0 mean slower.
Timeline with default config:
t=0s: 1.40 (40% slower -- session warmup) t=60s: 1.24 (warming up) t=120s: 1.15 (near cruise speed) t=300s: 1.07 (cruising, slight fatigue) t=1800s: 1.16 (fatigue building) t=3600s: 1.22 (noticeable slowdown)
func (*SessionFatigue) FactorAt ¶
func (f *SessionFatigue) FactorAt(elapsed time.Duration) float64
FactorAt returns the speed multiplier at a given elapsed time in seconds. Exposed for testing.
func (*SessionFatigue) Start ¶
func (f *SessionFatigue) Start()
Start records the session start time. Call once when the walker begins.
func (*SessionFatigue) StartAt ¶
func (f *SessionFatigue) StartAt(t time.Time)
StartAt records a specific start time (useful for testing).
type Timing ¶
type Timing struct {
// contains filtered or unexported fields
}
Timing generates human-like delays using log-normal distribution.
Why log-normal? Real human inter-action times are right-skewed: most actions happen quickly but rare long pauses (reading, distraction) pull the mean well above the median. Uniform random delay is trivially detectable by ML-based anti-bot systems because its distribution is not bursty.
func NewTiming ¶
func NewTiming(cfg TimingConfig) *Timing
NewTiming creates a Timing instance with the supplied configuration.
func (*Timing) Delay ¶
Delay returns a human-like general-purpose delay drawn from the configured log-normal distribution, clamped to [Min, Max].
With default parameters (Mu=1.0, Sigma=0.8):
- median ≈ 2.7 s
- mean ≈ 4.1 s
- 95th ≈ 13 s
func (*Timing) PageReadDelay ¶
PageReadDelay returns a delay appropriate for page-reading behaviour. Range: [3 s, 15 s].
func (*Timing) PaginationDelay ¶
PaginationDelay returns a delay for clicking pagination controls. Range: [0.5 s, 2 s].
func (*Timing) SearchDelay ¶
SearchDelay returns a delay for analysing search-result pages. Range: [5 s, 20 s].
func (*Timing) TypingDelay ¶
TypingDelay returns a per-character typing delay. Range: [50 ms, 200 ms].
type TimingConfig ¶
type TimingConfig struct {
// Mu is the log-normal location parameter (default 1.0).
// Controls the median: median = exp(Mu).
Mu float64
// Sigma is the log-normal scale parameter (default 0.8).
// Controls spread; higher = more variance / heavier tail.
Sigma float64
// Min is the hard lower bound for any generated delay.
Min time.Duration
// Max is the hard upper bound for any generated delay.
Max time.Duration
}
TimingConfig configures the log-normal delay generator.