Packages
Session-sticky User-Agent rotation with modern, refreshable browser profiles. Assigns one realistic browser identity (UA + Accept-Language) per session key and keeps it sticky; the desktop + mobile pool is rendered from a small version table you refresh with `mix guise.refresh`.
Current section
Files
Jump to
Current section
Files
README.md
# Guise
Session-sticky **User-Agent rotation** with modern, refreshable browser
profiles.
Rotating a *fresh random* UA on every request is a tell — real clients keep
one identity for a while. Guise assigns one realistic browser profile
(User-Agent **+** matching `Accept-Language`) per session key and keeps it
sticky for 30 minutes, so your traffic looks like a handful of real
browsers rather than a random-UA firehose.
```elixir
def deps do
[{:guise, "~> 0.1"}]
end
```
## Usage
```elixir
# Random, share-weighted UA string
Guise.random()
#=> "Mozilla/5.0 (Windows NT 10.0; Win64; x64) … Chrome/150.0.0.0 Safari/537.36"
# Headers for a one-off request
Guise.headers("https://example.com/")
#=> [{"user-agent", "…"}, {"referer", "https://example.com/"}]
# Sticky headers for a session — same key → same browser identity for 30 min
Guise.headers("https://example.com/", {user_id, "example.com"})
#=> [{"user-agent", "…"}, {"accept-language", "en-US,en;q=0.9"}, {"referer", "https://example.com/"}]
```
Guise starts its own supervised process (an ETS-backed session store), so
there's nothing to add to your supervision tree.
## The pool
A modern UA string is a stable *template* plus one volatile part — the
browser's **major version** (Chrome froze everything below the major to
`.0.0.0` years ago). Guise renders its pool from templates (engine × OS ×
weight) against a small version table, so the whole pool modernizes when a
handful of numbers do — and can't produce an inconsistent string.
The default pool spans **desktop** (Chrome/Firefox/Edge/Safari across
Windows, macOS and Linux) and **mobile** (Chrome on Android, Safari on
iPhone and iPad), share-weighted so Chrome-heavy rotation looks like real
traffic.
## Keeping it fresh
```sh
mix guise.refresh
```
Pulls the current stable Chrome and Firefox majors from
[endoflife.date](https://endoflife.date) and rewrites `priv/versions.json`.
Edge is derived from Chrome (Chromium tracks it); Safari has no clean feed
so it's maintained by hand. The **runtime never hits the network** — it only
reads the committed file at boot. (`guise.refresh` uses OTP's built-in
`:httpc`, so the package itself adds no HTTP dependency.)
## Configuration
Pin or override individual versions:
```elixir
config :guise, Guise,
versions: %{"chrome" => "150.0.0.0"}
```
Or replace the pool wholesale with your own profiles — each a map with a
required `:ua` and optional `:accept_language` (default `en-US`) and
`:weight` (default `1`); a bare string is taken as the UA. When `:profiles`
is set it wins over the built-ins and `:versions` is ignored:
```elixir
config :guise, Guise,
profiles: [
%{ua: "MyBot/1.0 (+https://example.com/bot)", weight: 3},
"Mozilla/5.0 (…) Chrome/150.0.0.0 Safari/537.36"
]
```
## License
MIT © Alexander Don