Two of the four Pensar Apex install paths pipe a script into a shell
AI-powered offensive security testing using autonomous agents, directly in your terminal.
At a glance
- What is it?
- Pensar Apex is a TypeScript terminal agent for blackbox and whitebox pentesting, installable four ways and runnable headless for CI. Its most instructive surface is not the attack logic but the plumbing: stderr-only JSON logging, tracing that stays off until you register your own SDK, and a dependency list that reads like a reconnaissance kit.
- Who is it for?
- Pensar Apex suits a team that wants an autonomous pentest agent in CI with logs it can actually ship somewhere, since the stderr split and the four-level resolution order are built for that, and since traces follow published conventions rather than a private format. It does not suit an environment that blocks remote script execution, where the two piped install paths are simply unavailable.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two of the four install paths execute a remote script
The installation table offers four routes, and they are not equivalent in terms of what you can audit.
The quick install for macOS and Linux fetches a shell script over the network and pipes it into a shell interpreter. The Windows equivalent does the same thing in PowerShell. Neither gives you a chance to read what it is about to do.
The other two are ordinary package managers: a Homebrew tap for macOS, and a global install of the scoped npm package. Both resolve to a registry, both are inspectable before you run them, and both leave a record of what was installed.
There is also a naming inconsistency worth noticing. The install script is served from one domain, the documentation and the Discord invite from another, and the npm package is scoped to a third name. None of those are wrong on their own, but a reader verifying where a script comes from has three hostnames to reconcile.
A separate product exists for running from the cloud or wiring the agent into an existing pipeline, and that is a hosted console rather than part of this package.
Diagnostics go to stderr so stdout stays pipeable
The logging section is the most carefully specified part of the documentation, and the design decision is that diagnostics never touch the output a user or a script reads.
A centralized structured logger writes one-line JSON to stderr whenever the process is not attached to a terminal, and pretty colourised output when it is. User-facing command-line and terminal interface output stays on stdout. That split is what lets a pipeline collect the JSON without filtering a human-readable stream.
There are four levels, ordered from most to least verbose, with the default at the third. The resolution order is stated exactly: a command-line flag wins, then an environment variable, then a back-compatibility debug alias, then the default.
pensar pentest --target https://example.com --log-level debug # or --verbose / --quiet
PENSAR_LOG_LEVEL=debug pensar ... # via environment
PENSAR_DEBUG=1 pensar ... # back-compat alias for debugA separate variable forces the format to JSON or pretty regardless of whether a terminal is present, which is the switch you want when piping. And when the agent runs as a managed agent, the log level is supplied by the host environment, so the same code emits different verbosity depending on who is running it.
Tracing stays a no-op until you register an SDK yourself
Two tracing systems are documented, and both are off by default in a way that is easy to miss.
For OpenTelemetry, spans are emitted for agent runs, model calls, and tool executions through the API package. They are no-ops unless your process registers an SDK as the global tracer provider, and the project ships no SDK. The documented ways to supply one are an OpenAI-instrumented error tracker with its model-call integration added to its init call, or the Node SDK with an OTLP exporter pointed at any compatible backend.
The subtlety is that the exporter plumbing is present as a dependency while the SDK is not. So the pieces that ship a trace somewhere are installed, and the piece that decides whether a span is real is yours.
What a span contains is also configurable. By default only shape is recorded: the model, token counts, latency, and tool names, following the published GenAI semantic conventions. Setting an environment variable switches on recording of prompts, tool input and output, and model outputs, which is the setting to think about before turning it on in a shared environment.
The second path, an experiment-tracking service, needs an API key and an entity name, defaults its project name, and includes reasoning steps, tool calls, token usage, and state checkpoints. It is silently disabled when credentials are absent.
The flag table documents a mode the examples never show
The headless interface is a subcommand with a target, and the flag table below it is the real specification.
The target is required. A working directory turns on whitebox mode by giving the agent access to source. Extended thinking is described as applying to supported models, which is a per-model caveat rather than a guarantee. A task-driven flag enables a task-driven architecture and is marked experimental.
Two flags accept either literal text or a file path, for custom agent guidance and for supplying a threat model. That file form is the one to reach for in a real engagement, since a threat model is long enough that embedding it in a shell argument is unpleasant.
The targeted subcommand adds a repeatable objective, so several objectives accumulate on one run.
The oddity is the mode flag. The table documents it as taking a mode value, with one named value described as being for pivoting and flag extraction. None of the four example commands use it, so the exact spelling of that value and the rest of the mode vocabulary have to come from elsewhere.
That gap is worth resolving before a run rather than during one.
Provider selection exists in the interface and on the command line
Model providers are configured in two ways, and they have to agree with each other.
Inside the interface there are two commands: one connects a provider and the other lists the models it discovered. On the command line the same choice is expressed as a provider flag paired with a model flag.
A named hosted provider is enabled by setting its API key as an environment variable and then selecting one of the discovered model identifiers from its catalog, with a separate document covering discovery, token budgets, and a live check.
Bring-your-own endpoints go through configuration or a worker environment variable holding the definitions, and carry a bearer token against an OpenAI-compatible interface. The documentation for that path covers endpoint settings including one model family, headless job configuration, and a live connection test.
The provider flag and the model flag are always paired. Passing a model name without its provider has nothing to resolve against, which is why the model flag's documented default is described as auto-selected rather than as a specific model.
For a hosted deployment the resolution is different again: the log level comes from the host, so the agent is expected to run inside a supervisor that sets its environment rather than by a person typing flags.
The dependency list reads like a reconnaissance kit
The dependency manifest says more about what the agent does than the README does, and it is worth reading as a list rather than skipping.
Five of them are email clients and parsers: a Google mail client, a Microsoft Graph client, an IMAP client, a mail parser, and a mail sender. Four are model providers behind one interface, plus an aggregator and an OpenAI-compatible adapter. Then there is a context protocol SDK, a Playwright server, an anti-detect browser build, an image processing library, a domain popularity lookup, a Markdown renderer, a syntax highlighter, and a concurrency limiter.
Assembled, that is a machine that reads mailboxes, browses with evasion, renders documents, and reasons about them. It also explains the example directory: the repository ships a prompt-injection example, and mail parsing plus a browser plus a renderer is exactly the combination an injected instruction would target.
Two more details. The terminal interface is built on a component library with a React layer, which is why React appears in the dependencies of a command-line tool. And one first-party package is pinned to an exact version rather than a range, which usually means the surface is coupled closely enough that a minor bump would matter.
Runtime is Bun, evidenced by the lock file and its configuration, while the testing and formatting stack mixes a linter, a formatter, an unused-export checker, and a test runner.
The default branch is canary and the tags carry commit hashes
The release naming says this ships from a canary line.
The default branch is not the stable one, it is named canary, and the recent tags all share a version prefix with a commit hash appended. Three of them landed within about two days. So the published artifacts are builds of a moving branch rather than cuts of a release branch.
That is a reasonable choice for an agent tool where behaviour changes with model releases, and it means you should pin a specific tag rather than track the branch if you want repeatability.
The repository is unusually transparent about governance for a tool in this category. Alongside the contributing guide and the licence sit a responsible use document and a security policy, plus a decisions directory that reads like an architecture record, a benchmarks directory, and generated API documentation.
It is also configured for agent-assisted work, with instruction files for several tools at the root and in hidden directories. Three editors' worth of configuration files sit next to the source.
For an evaluation, the responsible use document is the one to read first, since this is a tool that takes a URL and reasons about how to attack it.
Editorial conclusion
Pensar Apex suits a team that wants an autonomous pentest agent in CI with logs it can actually ship somewhere, since the stderr split and the four-level resolution order are built for that, and since traces follow published conventions rather than a private format. It does not suit an environment that blocks remote script execution, where the two piped install paths are simply unavailable. Before you install, pick the auditable route over the piped one, decide whether you need prompt and output payloads recorded before you enable tracing, and read the responsible use document first.
Frequently asked questions
Is there AI in Apex?
Yes. Pensar Apex is described as AI-powered penetration testing using autonomous agents directly in the terminal, running blackbox and whitebox pentests that explore, reason, and surface vulnerabilities. The terminal interface opens with the `pensar` command, and headless runs use `pensar pentest` with a target.
How do I install Pensar Apex?
Four documented routes: a quick install script for macOS and Linux, a Homebrew tap, `npm install -g @pensar/apex`, and a PowerShell one-liner on Windows. The two script routes fetch and execute a remote file directly, while the package manager routes are inspectable. A separate hosted console exists for cloud and CI use.
What does the task-driven flag do in an Apex pentest?
It enables a task-driven architecture and is marked experimental. It appears alongside extended thinking for supported models, a working directory flag that gives the agent source access for whitebox mode, and two flags that accept either literal text or a file path for custom guidance and a threat model.
How do I turn on tracing for Apex?
OpenTelemetry spans for agent runs, model calls, and tool executions are emitted through the API package and stay no-ops until your process registers an SDK as the global tracer provider. Setting `AI_TRACE_RECORD_PAYLOADS=true` additionally records prompts, tool input and output, and outputs. An experiment-tracking path needs an API key and entity, and is silently disabled without them.
Why does Apex send diagnostics to stderr?
So one-line JSON diagnostics stay separate from user-facing stdout output when the process is not attached to a terminal, which makes them easy to ship to a log pipeline. In an interactive terminal it prints pretty colourised output instead. The level resolves from the command-line flag, then an environment variable, then a back-compatibility debug alias, then the default.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pensarai-apex)