@trenchwork/erosolar
v1.1.38
Published
DeepSeek AI-powered CLI agent for code assistance and automation. Source: ECCN 4E001 / binary: 4D004 — see SECURITY.md for the export-classification matrix and BIS Note 1 carve-out for vulnerability disclosure output.
Maintainers
Readme
Trenchwork · @trenchwork/erosolar
A multi-provider terminal agent for coding work and authorized offensive-security research. Default backend is DeepSeek-V4-Pro via a shared key gated by Erosolar Auth (no key bundled in the npm tarball); Anthropic / OpenAI / Google / xAI / Qwen / Ollama are opt-in providers with bring-your-own keys.
This README documents the current shipped state of the CLI — the
auth-gated boot, profiles, capability surface, MCP integration,
artifact store, and operational knobs. Examples, "try asking…" hints,
and onboarding fluff are deliberately absent (CLAUDE.md
is strict about that). Deep technical reference lives in
docs/ENGINEERING.md; security model and
threat boundaries live in SECURITY.md.
Install
npm install -g @trenchwork/erosolarTwo binaries land on $PATH and point at the same entry:
erosolar— preferredtrenchwork— alternate name
Node ≥ 20 required.
First-run boot
erosolarOn first launch the CLI:
- Opens a browser tab to
https://ero.solar/auth?port=…. Sign in with Google SSO (Firebase Auth, projecterosolar-1b0db). SSO is enforced both at the auth page and at the Firestore rule layer (request.auth.token.firebase.sign_in_provider == 'google.com'). - Stores a Firebase ID token + refresh token at
~/.erosolar/auth.json(0o600). The refresh token is long-lived; the ID token rotates automatically before expiry. - Approval gate. The CLI checks
approved_users/{your-email}in Firestore. The package ships offsec capabilities (Kali tools, AFL++, gdb, pwntools, Ghidra), so only allowlisted emails can launch. If your email isn't approved, the CLI writes a pending request toapproval_requests/{uid}and exits with a clear message — re-run once the operator approves. - Fetches the shared DeepSeek API key from Firestore at
shared_secrets/deepseekand writes it into the process env for the duration of the run. Nothing lands on disk. Reads are gated to authenticated + approved users; writes are admin-only. - Starts the Ink-based interactive shell.
Requesting approval
Sign in once via Google SSO. The CLI files a pending request at
approval_requests/{uid}. The operator reviews and approves with:
node scripts/approve-user.mjs <your-email>Re-run erosolar after approval is confirmed.
Bring-your-own key
If DEEPSEEK_API_KEY is set in env, the Firestore fetch is skipped.
Resolution order:
process.env.DEEPSEEK_API_KEY(env override always wins)~/.erosolar/secrets.json(set in-CLI via/key sk-…or/secrets set)- Firestore
shared_secrets/deepseek(the post-login fallback)
The same chain applies to TAVILY_API_KEY and any provider key the
shared-secrets module recognises.
Modes
erosolar # interactive
erosolar -q "explain X" # one-shot, prints answer, exits
git diff | erosolar # pipe mode — stdin becomes the prompt
erosolar --profile variant-research "<goal>"
erosolar --self-test # provider/auth/capability smoke testProfiles
A profile binds a system prompt template, default model + provider,
and a rulebook (a phase-structured guidance document
inlined into the system prompt). Switch via --profile <name> or the
EROSOLAR_PROFILE environment variable. Three profiles ship:
erosolar-code (default)
General-purpose terminal agent for coding, builds, tests, sysadmin,
package management. Default model deepseek-v4-pro, default provider
deepseek. Coding tool inventory only — the offsec capabilities are
withheld so the model's tool list isn't padded with sqlmap / ROP-search
in normal sessions. Rulebook:
agents/erosolar-code.rules.json.
variant-research
Authorized offsec / vulnerability-research surface. Walks the standard
n-day-to-0-day pivot: recon → vulnerable+patched binary acquisition →
patch diff (Ghidra) → variant search → fuzz campaign (AFL++) → crash
triage (gdb/pwndbg) → PoC development (pwntools) → coordinated
disclosure. Rulebook:
agents/variant-research.rules.json.
The disclosure terminal is pinned to coordinated channels
(HackerOne / Bugcrowd / vendor PSIRT / CERT-CC / internal write-up /
90-day published advisory). The rulebook contains an explicit
vr.r.no_brokerage rule — it is not a vector for selling unreported
exploits.
The companion research workspace is Aroxora/patchpivot
(private): target portfolio, per-investigation findings dirs, and a
disclosure log driven by this profile.
erosolar --profile variant-research "investigate the patch at <commit-url>"engagement-delivery
Sibling of variant-research running the same eight-phase VR workflow,
but the terminal phase delivers to an authorized engagement
recipient — a U.S. government contract / task order, a U.S. defense
prime under contract, or a published bug-bounty program. The rulebook
adds an intake phase that requires an active engagement identifier in
the artifact store before any other phase advances; missing engagement
id → halt + prompt operator. Rulebook:
agents/engagement-delivery.rules.json.
erosolar --profile engagement-delivery "<task>"Both variant-research and engagement-delivery get the offsec
capability surface. erosolar-code does not. Gating lives in
src/runtime/profileGates.ts.
Capability surface
Capabilities are typed wrappers around external tools, registered in
src/capabilities/ and exposed flat in the LLM's tool list.
Coding default (always on)
| Family | Tools |
|---|---|
| Filesystem | Read, Write, Edit, MultiEdit, Glob, Grep, Search |
| Process | Bash (bracketed-paste-safe stdin, 1MB stdout cap) |
| Git | git_status, git_log, git_diff, git_show, git_blame, git_revert, full enhanced-git surface |
| Memory | memory_save, memory_load, memory_list, memory_delete |
| Planning | TodoWrite (state + plan-formatter integration), PlanMode |
| Skills | Skill, list_skills (loads ~/.erosolar/skills/<name>/SKILL.md) |
| Web | WebSearch, WebExtract (Tavily under the hood) |
| Sub-agent | spawn_agent, agent_status, agent_output, agent_stop |
| Notebook | NotebookEdit |
| HITL | hitl_decision, hitl_approve, hitl_choose |
| Worktree | worktree_create, worktree_remove, worktree_list |
| Monitor | monitor_start, monitor_stop (long-running command observers) |
| Schedule | schedule_*, trigger_* (in-session reminders / cron-style triggers) |
Offensive (variant-research / engagement-delivery only)
| Capability | Module | Tools |
|---|---|---|
| Kali (network/web) | kaliCapability.ts | kali_sqlmap, kali_gobuster, kali_ffuf, kali_feroxbuster, kali_nikto, kali_wpscan, kali_hydra, kali_john, kali_hashcat, kali_masscan, kali_amass, kali_subfinder |
| Static binary analysis | binaryAnalysisCapability.ts | bin_file, bin_strings, bin_objdump, bin_readelf, bin_nm, bin_checksec, bin_ropgadget, bin_radare2_cmd |
| Headless Ghidra | ghidraHeadlessCapability.ts | ghidra_analyze, ghidra_decompile, ghidra_diff |
| AFL++ fuzzing | aflppCapability.ts | afl_compile_harness, afl_fuzz_start (detached), afl_fuzz_status (auto-registers crashes in artifact store), afl_fuzz_stop, afl_showmap, afl_cmin, afl_tmin |
| Crash triage | gdbCapability.ts | gdb_run_with_input, gdb_inspect_at, gdb_disassemble (pwndbg/GEF inherited from ~/.gdbinit) |
| Exploit dev | pwntoolsCapability.ts | pwn_eval, pwn_rop_search, pwn_packed |
The offsec capabilities are guardrail-free per
ero.solar/about — the operator authorizes
their own engagements; the CLI does not validate target/argv. Install
the underlying binaries via kali-linux-everything + ghidra on
Kali, or the Kali rolling repo on Debian/Ubuntu.
MCP servers
The CLI auto-loads MCP servers declared in mcp.json (or
mcp.json.example as a template). MCP tools appear in the flat tool
list with the prefix mcp__<server>__<tool>.
{
"mcpServers": {
"ghidra": { "command": "python3", "args": ["-m", "ghidra_mcp"], "env": { "GHIDRA_INSTALL_DIR": "/usr/share/ghidra" } },
"mcp_kali_server": { "command": "mcp-kali-server", "args": [] },
"metasploitmcp": { "command": "metasploitmcp", "args": [] },
"tavily": { "command": "tavily-mcp", "args": [] }
}
}mcp-kali-server and metasploitmcp ship in kali-linux-everything.
ghidra-mcp is a separate pip install. tavily-mcp is on npm.
None are required; they're additive.
Slash commands
Handled before the agent loop sees the input. The set below reflects
the actual command table (src/shell/commandRegistry.ts plus the
inline matchers in src/headless/interactiveShell.ts):
| Command | Aliases | Effect |
|---|---|---|
| /help | /h, /? | Inline help panel |
| /model [name] | /m | No arg → picker menu; arg → silent switch (provider, provider model, provider/model, or model) |
| /providers | | List configured providers |
| /key <sk-…> | | Save DEEPSEEK_API_KEY to ~/.erosolar/secrets.json |
| /secrets [set [name]] | /s | Inspect / set / unset every known provider key |
| /auto | /continue, /loop, /dual | Cycle auto-continue mode (off → on → dual → off) |
| /bash <cmd> | /sh | One-shot shell command, bypasses the agent |
| /clear | /c | Clear screen, redraw welcome |
| /context | | Refresh workspace context |
| /sessions | | Session management menu |
| /revert [confirm] | | Preview / roll back files modified during the current run |
| /tools | | List active tools |
| /mcp | | MCP server + tool status |
| /learn | | Learning-system status |
| /debug [on\|off\|status] | | Toggle agent-internal debug logging |
| /stats | /status | Session token + cost stats |
| /keys | /shortcuts, /kb | Keyboard shortcuts panel |
| /exit | /quit, /q | Leave the shell |
Profile is selected at boot only — there is no in-session /profile
switch; restart with --profile <name> or set EROSOLAR_PROFILE.
HITL is configured per-tool through the capability's autoPause
option (Option+V toggles the equivalent).
Configuration
Filesystem layout (~/.erosolar/)
| Path | Purpose | Permissions |
|---|---|---|
| auth.json | Firebase ID + refresh tokens | 0o600 |
| secrets.json | User-set provider keys (overrides shared) | 0o600 |
| sessions/<uuid>.json | Persisted conversation history | 0o600 |
| artifacts/<sha256[:2]>/<sha256> | Content-addressed blob store | dir 0o700, files 0o600 |
| artifacts/index.json | Artifact metadata | 0o600 |
| jobs/aflpp/<jobId>.json | Detached AFL++ job state | 0o600 |
| cache/ | Model discovery cache, etc. | 0o700 |
| skills/<name>/SKILL.md | User-defined skill playbooks | — |
Environment variables
| Var | Effect |
|---|---|
| DEEPSEEK_API_KEY | Bypass shared-key fetch and use this key |
| EROSOLAR_HOME | Override ~/.erosolar storage root |
| EROSOLAR_PROFILE | Default profile (alternative to --profile) |
| EROSOLAR_INK_DEBUG=1 | Verbose Ink prompt + state logging on stderr |
| <PROFILE>_MODEL | Per-profile default model override (e.g. EROSOLAR_CODE_MODEL=claude-opus-4-7) |
| <PROFILE>_PROVIDER | Per-profile default provider override |
| <PROFILE>_SYSTEM_PROMPT | Per-profile system prompt override |
| NO_COLOR, FORCE_COLOR | Standard color toggles |
| ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, XAI_API_KEY, DASHSCOPE_API_KEY, OLLAMA_BASE_URL | Other provider credentials when those models are selected |
~/.erosolar/settings.json (hooks + permissions)
The harness reads ~/.erosolar/settings.json (user) and
<workspace>/.erosolar/settings.json (project) for hooks, allow/deny
permission lists, and env injection. See src/core/hooks.ts for the
contract.
Variant research workflow
The variant-research and engagement-delivery rulebooks drive the
same eight-phase pivot the LLM reads from its system prompt:
| Phase | Intent | Tools |
|---|---|---|
| recon | Identify target patch / CVE / advisory; record bug-class hypothesis | Tavily / WebSearch |
| acquire | Build vulnerable + patched binaries; persist to artifact store | Bash, Git, bin_* |
| bindiff | Diff binaries; pull decompiled C of changed functions | Ghidra MCP / ghidra_* |
| variant | Hunt the same buggy pattern in older versions / forks / siblings | Ghidra, Grep |
| fuzz | Build harness + run AFL++ campaign as detached job | afl_* |
| triage | Classify each crash; correlate to decomp; identify primitive | gdb_*, Ghidra |
| poc | Develop minimal reliable PoC reproducing the primitive | pwn_* |
| disclose / deliver | Author write-up; ship through coordinated / contracted channel | — (rulebook lists allowed channels) |
engagement-delivery adds a leading intake phase that gates the
workflow on a registered engagement identifier (USG contract / task
order, defense-prime engagement reference, or published bug-bounty
program scope).
The artifact store is the load-bearing piece: every multi-megabyte output (binaries, decompilations, crash corpora) is registered with a sha256 id and tagged. The LLM passes ids around in conversation instead of pasting blobs into context — without this, a long workflow gets summarized away mid-investigation.
Architecture
Agent loop
src/core/agent.ts runs a single-turn provider-native tool-use loop:
while (true) {
const r = await provider.generate(messages, tools);
if (r.type === 'tool_calls') { await resolveToolCalls(r); continue; }
break; // text response — turn complete
}No DAG planner, no ReAct scratchpad. Multi-step planning is implicit
in the LLM's tool calls, guided by the rulebook injected into the
system prompt. Sub-agents (spawn_agent / agent_status /
agent_output / agent_stop) are the long-task supervisor — anything
running for hours (fuzz campaigns, large recompiles) goes detached and
the parent polls.
Capability registry
Each capability module implements CapabilityModule and contributes a
flat ToolSuite to the runtime. Registration happens in
src/capabilities/index.ts. Tools declare JSON Schema parameters;
inputs are validated and coerced via src/core/schemaValidator.ts
before the handler runs. MCP tools are first-class — same
ToolDefinition shape, mcp__<server>__ prefix.
Profile-based gating (src/runtime/profileGates.ts) decides which
capabilities are mounted. Cowork / productivity capabilities were
extracted to ~/GitHub/erosolar-cowork in 2026-05 and are not
present in this repo (enforced by test/capability-separation.test.ts).
Rulebooks
A rulebook (src/contracts/schemas/agent-rules.schema.json) is a
phase-structured JSON document with globalPrinciples, phases, and
per-step entryCriteria / exitCriteria / rules. The CLI renders it
to markdown and inlines it into the profile's system prompt at boot.
It is declarative guidance, not an enforced state machine — the
LLM is expected to follow it; nothing in the runtime blocks a phase
from being skipped. Rule severities (critical / required /
recommended) influence how strongly the LLM treats them.
Artifact store
src/core/artifactStore.ts — content-addressed blob store at
~/.erosolar/artifacts/. JSON metadata index, sha256-keyed blob dir,
no extra deps. Tools that produce large outputs return
{ artifact_id, summary } instead of pasting raw bytes into chat;
downstream tools fetch by id when they need the content. This is what
makes a 12-hour fuzz + triage + ROP-build workflow survive context
compaction.
Shared secrets
src/core/sharedSecrets.ts — REST fetch of shared_secrets/<name>
from Firestore using the user's Firebase ID token. Auto-refreshes the
token if expired. Writes to process.env, no on-disk cache (a rotated
key shouldn't get stuck stale on a user's machine). Triggered once at
boot from src/bin/deepseek.ts after requireAuth().
Context manager
src/core/contextManager.ts — token-budget aware sliding window with
LLM-driven summarization. Defaults: 130k max, 100k target compaction
trigger, last 10 exchanges always preserved, file-read truncation
keeps head + tail of large files. Uses the profile's default model for
the summarization pass.
HITL
src/capabilities/hitlCapability.ts — confirmation/approval/choice
tools that pause the run timer until the user responds. Configurable
via autoPause and timeoutMs on the capability instance. Driven by
the LLM (it decides when to ask), not the runtime — there's no
"pause every tool call" mode.
Companion surfaces
This monorepo contains the CLI plus three companion surfaces that share Erosolar Auth, balance, and the model brain:
- Helia (
Erosolar_Browser/) — Electron + Chromium browser modeled after ChatGPT Atlas, with a side-panel Erosolar agent that has full page context and CDP-driven action automation. Cross-platform: macOS (dmg/zip, x64+arm64), Windows (NSIS + portable), Linux (AppImage + .deb). Distributed at ero.solar/helia with an S3-manifest auto-updater. Current Helia version:0.1.21. - Site (
site/) — Firebase-hosted marketing/portal (/about,/portal,/auth,/docs,/defense,/process,/audit,/admin,/npm,/helia). - AWS Lambda backend (
aws/lambda/) — single Lambda fronted by API Gateway, Stripe checkout, Firestore for state, service-account JSON pulled from AWS Secrets Manager. Replaces the legacy Cloud Functions deployment; portal + CLI both call into it.
Cross-surface PRs are atomic by design. See CLAUDE.md "Monorepo for one product, separate repos for different products" for the rule.
Building from source
git clone https://github.com/Aroxora/deepseek-coder-cli.git
cd deepseek-coder-cli
npm install
npm run build # tsc → dist/
npm test # full jest suite
node dist/bin/erosolar.js --self-testpretest runs the build, and prepublishOnly rebuilds before any npm
publish so the published tarball is always rebuilt from source.
Tests
test/**/*.test.ts — driven by jest with the config in
jest.config.cjs. Test discipline (per CLAUDE.md): real behavior
end-to-end, no mocks for things the test claims to verify. Tests for
provider calls require credentials; those are gated by env-var
presence and skipped (with a clear reason) when absent.
Skipped ≠ passing.
A pre-push git hook (scripts/git-hooks/pre-push) runs npm test
before every push. Install once per checkout:
git config core.hooksPath scripts/git-hooks. Bypass in an emergency
with git push --no-verify.
Releasing
npm run release → scripts/create-release.sh patch (interactive —
checks clean git, runs tests, bumps version, builds, publishes, tags).
Non-interactive publishes: npm version patch && npm publish --access public.
Versioning + deprecation policy
Semver. Patches add features and fix bugs without API breakage; minor bumps signal new public surfaces; major bumps signal breaking changes.
Versions 1.1.16 → 1.1.19 are deprecated — they bundled an
embedded DeepSeek API key that has been revoked. Installations on that
range print an npm deprecate warning. Upgrade to ≥ 1.1.20. The
shared-key fetch via Firebase Auth replaces the embedded key.
Security posture
Full threat model + controls live in SECURITY.md. The summary below is an index; SECURITY.md is the authoritative reference (SSO, allowlist, revocation, profile gating, audit log, threat model, residual risk).
- The CLI is dual-use offensive-security tooling. U.S. classification
is EAR-controlled (CCL ECCN 4D004
- 4E001 for development technology). Domestic development and use is unrestricted; export controls apply to international transfer. Good-faith security research is affirmatively protected by Van Buren (2021), the DOJ May-2022 CFAA Charging Policy, and the LOC § 1201 Triennial Exemption (2024).
kaliCapability.tsand the offsec capability surface are guardrail-free. Operator authorization is assumed — both legal authorization (you have permission to test the target) and ethical authorization (the engagement scope covers what you're about to run).- Disclosure is pinned. The
variant-researchrulebook'sdisclosephase has explicitvr.r.no_brokerageandvr.r.respect_embargorules. PoCs go to vendor / HackerOne / Bugcrowd / CERT-CC, not to brokers.engagement-deliveryships only to authorized contracted recipients. - Secrets handling: the npm package ships zero embedded API keys.
Keys come from env, user-set local file, or Firestore via Firebase
Auth (admin-managed). Error messages are sanitized through
secretStore.sanitizeErrorMessageso leaked tokens in stack traces get redacted before they reach the terminal. - Auth tokens are stored at
~/.erosolar/auth.jsonwith0o600permissions, in an0o700directory. Atomic writes prevent half-written JSON from breaking subsequent loads.
See /about for the full disclosure
including BIS links and the relevant rulemaking.
Links
- npm: https://www.npmjs.com/package/@trenchwork/erosolar
- Source: https://github.com/Aroxora/deepseek-coder-cli
- Companion research workspace:
Aroxora/patchpivot(private) - Helia (browser companion, mac/win/linux): https://ero.solar/helia
- Erosolar Auth: https://ero.solar/auth
- Project context: https://ero.solar/about
License: MIT (see LICENSE).
